0x01 简介
Web Cache
web缓存主要是为了解决网页延迟,提高带宽利用而出现的技术。
web缓存可以分为浏览器缓存、代理服务器缓存、CDN缓存、数据库缓存、DNS缓存等。
浏览器缓存:也即客户端缓存,把请求过的文件保存在本地,再次访问时直接从本地取出。保存的技术有HTTP缓存
、Web Storage
、App Cache
、IndexedDB
、File SystemAPi
。判断头部有Expires
、Cache-control: max-age
、Last-Modified
、Etag
。
代理服务器/CDN缓存:共享缓存,多用户可同时访问。
数据库缓存:为数据库的增删改查做了缓存,主要代表为memecache
、redis
。
DNS缓存:缓存域名记录信息。
Web Cahce poisoning
web缓存投毒,主要的场景是在代理服务器/CDN。
下面用一张图来展示CDN场景下的网站访问。
从图中我们可以看出在CDN模式下,多用户访问站点的情况。
那么CDN是依据什么来给用户提供缓存的呢?
最简单的一个例子就是针对于不同地域的用户,需要提供的缓存是不同的。
由此引出一个叫做Cache Key(缓存键)
的概念,CDN依此来判断是否返回缓存。
- Cache Key
Vary
是HTTP响应头部中的一个字段,它决定了对于未来的一个请求头,应该用一个缓存的回复(response)还是向源服务器请求一个新的回复。 例如:
Vary: User-Agent
针对于不同的类型客户端的访问,pc和移动端的ua不同,就会返回不同的缓存。
如下图(当Accept-Encoding不同时,返回不同的缓存)
当然CDN的缓存方式不会这么简单,Cache key
不会由单个头部特征构成,通常由多个特征共同构成,其中可能由CDN自己的特征,并且一般不会用vary返回。(缓存投毒原因之一)
用一个cloudflare的官方示例说明下
GET /logo.jpg HTTP/1.1
Host: www.cloudflare.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36
Accept: image/jpg
默认的cache key
为cloudfare Zone ID、scheme、hostname、path
进行组合而得到。
那么该请求的cache key
为 1234:https://www.cloudflare.com/logo.jpg ,组合方式也由CDN决定,也可以自己进行配置。
再看一个请求
图中的橙色部分被作为缓存键进行匹配,那么对于两个地域的用户就会出现页面显示问题
使用en
语言的用户,就会返回pl
语言响应情况。
这里的language
就是非缓存键,顾名思义,非缓存键
就是缓存键之外的参数,这样的话就会引入安全问题,如果非缓存键
能够被攻击利用的话,那么被投毒的响应就会返回给正常用户(关键)。
那么你就会想,这不科学啊?为啥CDN没有按照Vary
的进行缓存,这其实也是各个厂商对于标准实现差异导致的,总是想夹带私货(功能扩展之类)。就像Smuggle一样。😂
那么如何去发现一个页面是否存在缓存投毒的可能呢? 方法论:
1. 寻找非缓存键
2. 分析非缓存键带来的风险,着重注意是否页面的缓存中存在一些风险参数,例如:文件后缀,Content-type、route(路由)、响应状态码、响应头。
3. 随后注入缓存,观察效果,找到目标页面
手动进行缓存投毒测试时,由于缓存响应可能会忽略非缓存键,这样会导致测试无法进行。因此cache-buster
(缓存破坏)就很重要了,通过找到一个缓存键,每次请求都改变缓存键,从而使得每次请求都不会被缓存。
手动测试还存在一个风险,就是在测试过程中会影响正常用户访问,所以每次请求最好使用cache-buster
来解决这个问题。
发现缓存投毒可以使用param-miner,PortSwigger官方出品。
危害
- XSS存储,投毒每个会浏览页面的人
- 投毒资源文件,比如js、css
Case
接下来使用一系列的案例来对缓存投毒场景进行说明
HTTP头部非缓存键
简单HTTP头部
先看到一个请求
响应包出现了x-cache: hit
代表命中了缓存,真实场景下只能依据不同请求包的响应内容来推测。随后就可以使用param-mine
进行测试,发现X-Fowarded-Host
就是他的非缓存键。
携带X-Fowarded-Host
发送请求包
从返回包中可以看出,X-Fowarded-Host
会修改tracking.js
的引用地址,那么就引用任意地址的脚本了。
这里要问题要说明下,从结果上来看X-Fowarded-Host
像是缓存键,因为每次改变都会产生新的缓存。其实由于X-Fowarded-Host
会直接改变响应内容导致,一定要区分清楚。
Cookie
看一个请求
从响应包中可以发现Cookie
的值可以出现再缓存中,那么这里的利用方式就可以和上面相同
多头部
看到下面这个请求
不像前2个响应一样,响应包中没有出现明显的可以篡改的特征,都使用了相对路径。
是可篡改主要看在请求或响应中存在相同值。
这里使用Param-mine
进行探测,发现当同时出现X-Forwarded-Host
和X-Forwarded-Scheme
的时候,页面会进行重定向
不过这里跳转的协议是https,可能是和服务器是https连接有关。
所以可以构造第三方恶意地址,使其跳转。这里选择对js文件进行跳转。
未知头部
看到下面这个请求
可以发现存在Vary: User-Agent
头部,内容中存在携带host引用的js资源
先使用param miner
进行头部测试,发现x-host
字段是可以替换host的非缓存键
头部添加x-host
进行测试
这里还有个问题要解决,就是如果要针对特定的用户(例如管理员)进行投毒攻击的话,需要知道他的User-Agent
才行。这个时候需要让受害者去访问下攻击者的资源就行了,可以找类似于评论这种功能,看看是不是可以提交富文本内容即可。
这样就获取到了他的ua,之后按照之前的构造链构造即可
HTTP请求行
请求参数未被缓存
看到下面的请求
测试参数看看,是不是会被缓存
从不同的参数值都返回新缓存来看,参数是会被缓存的,然后实际上会存在隐藏参数缓存
的情况。使用param miner
进行测试。
发现utm_content
是被缓存的,验证方式很简单,只要修改utm_content
的值,如果缓存命中的话,那就是缓存参数了。
随后可以发现参数会在响应包中出现,那么就可以进行投毒了。
这里使用的origin
属于cache破坏
,避免测试是直接投毒了首页缓存,避免业务故障。
看到下面这个请求
同样也存在这utm_conten
参数未被缓存的情况。
但是请求参数会被编码,直接通过响应的缓存思路就行不通,这里看到一个js调用。
发现callback
参数会被当作方法名传入响应中。那么怎么投毒/js/geolocate.js?callback=setCountryCookie
呢。在Ruby on Rails中有一个参数解析漏洞,以下两个请求效果一样。
/?param1=test¶m2=foo
/?param1=test;param2=foo
这样解析会导致很有很多问题,其中有一个参数覆盖的比较特别,结合utm_content
这种隐藏的非缓存参数。
对于Rails来说他会把第1个请求中的第2个callback作为值,但是缓存服务器会使用第1个callback作为缓存键。
所以构造如下请求即可完成对缓存投毒
Fat Get
在varnish cache
中有一种情况,允许GET请求携带请求体,对于参数相同的请求,后端服务器可能会采用请求体中而非请求行中的。但是缓存使用的是请求行中的进行缓存。
可以看到此时的缓存键是X-Cache-Key: /js/geolocate.js?callback=setCountryCookie$$
。
URL normalization
有一种URL规范化
的情况,由于缓存服务器把URL中的特殊符号进行规范化后缓存,导致正常的请求会返回异常请求的响应。
例如火狐的更新请求:
在第一个请求之后,缓存服务器把URL规范化之后记录了正常请求的URL作为缓存键,这样就会导致原有正常的更新请求变成了301。
直接用浏览器访问是无法进行xss反射的,但是可以利用缓存。
缓存键注入
缓存键构成一般都会由多个元素构成,如果存在可控元素的话,可以结合跨域origin
进行CRLF注入。
看到下面这个案例
访问首页,经过2次302跳转来到真正的页面。
还有一个请求/js/localize.js?lang=en&cors=0
修改cors=1
,并且添加origin头
可以进行CRLF注入。
需要解决的问题有几个
1、正常访问的cors=0,如何让受害者访问时,cors=1
2、缓存键是什么
关键是要知道缓存键的组成,这里使用Pragma: x-get-cache-key
(案例设置的,实际情况可以看缓存服务器的文档或者手动测试)。
X-Cache-Key: /js/localize.js?lang=en&cors=1$$origin=http://123.com
结构是url_path$$origin=origon_value
。
接下来就是解决问题1了,在/login/?lang=en
内容中,对/js/localize.js?lang=en&cors=0
进行了引用,并且这里的lang=en
是来自于请求中的。那么可以想到如果在/login?lang=en
的跳转可以修改cors值并且缓存键不修改的情况,这个投毒链就可行了。
首先来看看直接修改cors
值,缓存键会不会被修改。
根据缓存键的构成就能够猜测到会被修改。那么如何在url中修改参数,但是不影响缓存键呢?
这个时候就要寻找非缓存参数了,utm_content
当然需要试试,实际上在真正测试时,可能存在其它的参数。
Bingo.这里的跳转地址变了,但是缓存键没有变。
构造payload的时候要注意,$的数量还有编码以及缓存时间
GET /js/localize.js?lang=en?utm_content=x&cors=1
Origin: x%0d%0aContent-Length:%208%0d%0a%0d%0aalert(1)$$$$
GET /login?lang=en?utm_content=x%26cors=1$$Origin=x%250d%250aContent-Length:%208%250d%250a%250d%250aalert(1)$$%23
DOM漏洞&缓存投毒
看到下面的请求
响应脚本中存在一个对data.host数据引用,如果data可控就可以造成一个dom型的漏洞。
使用param miner
进行测试,看看是否头部存在可改变的非缓存键。
发现了x-forwarded-host
和origin
和缓存存在关系。
通过手动测试可以发现,x-forwarded-host
为非缓存键,origin
为缓存键。
接着就是分析initGeoLocate
函数和/resources/json/geolocate.json
文件的内容了
initGeoLocate
函数在/resources/js/geolocate.js
可以找到定义。
/resources/json/geolocate.json
文件内容
这个功能就是一个送货功能,国家是可控的。
接下来只要篡改data.host
,并且修改geolocate.json
即可。(缓存投毒时,要注意需要携带Cookie的session,如果响应包中存在set-cookie
字段,则该响应不可缓存)
混合投毒(DOM漏洞+重定向)
看到下面这个请求
和之前的DOM投毒
相同,可以看到data.host
依然被引用。
直接分析initTranslations
和/resources/json/translations.json
文件,同时使用param miner
进行非缓存键的测试。
这里是类似一个网页翻译的功能。默认是使用英语。目标是要对会选择英语页面的用户进行投毒,执行alert(document.cookie)
。根据代码来看,可以选择一个语言进行投毒,所以就要想办法把用户访问的页面切换到其它被投毒语言页面中。语言文件的code
和name
都没法执行xss,一个是setAttribute
,一个是innerText
。
经过param miner
测试,x-original-url、x-forwarded-host、origin
都会触发缓存变化。
x-forwarded-host、origin
的作用类似。x-original-url
可以覆盖目标的URL值,X-Rewrite-URL
也有类似的效果。
要达到投毒用户的目的,可以分成首页被设置为其它语言/用户切换语言、其它语言被投毒
2个思路。
在进行语言切换的时候,会产生302重定向的请求,这里如果可以投毒,可以让用户在进行语言切换时,切换到其它被投毒的语言。
这个时候使用x-original-url
即可
但是有个Set-Cookie
,说明响应无法缓存。
观察主页的时候发现,资源的引用有时候使用了\
。服务器会对重定向的地址中的\
变为/
。
这里可以把x-original-url: /setlang/es?
修改为x-original-url: /setlang\es?
可以看到没有Set-Cookie
。接下来就是把首页设置为其它语言,思路也类似,使用x-original-url
,把首页投毒成其它语言。
最后投毒其它语言页面。
这样一个攻击链就完成了。
One’s Storm
-
Web Cache Deception Web缓存欺骗,类似于RPO攻击,利用缓存服务器和后端服务器处理差异导致。 主要在于欺骗受害者访问一个不存在的静态资源,例如
/home.do/logo.png
,对于后端服务器来说,这个文件不存在,请求会返回/home.do
的响应内容,但是对于缓存服务器来说这个扩展是png
,是静态资源,可以缓存,那么就会创建home.do
目录,然后缓存一个logo.png
,内容是/home.do
的内容。 这种情况可以用来窃取CSRF Token、用户个人信息之类,Paypal就存在过相关问题。(有时间再详细写下吧看😂) -
Tips
- 缓存判断的三种方法:头部Hit、动态内容、响应计时
参考:
- practical-web-cache-poisoning
- Web Cache Entanglement: Novel Pathways to Poisoning
- 浅谈Web缓存
- Vary
- HTTP 缓存
(ง •_•)ง 2021-01-24 23:34:45 星期日