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场景下的网站访问。
Cache Key(缓存键)的概念,CDN依此来判断是否返回缓存。
- Cache Key
Vary是HTTP响应头部中的一个字段,它决定了对于未来的一个请求头,应该用一个缓存的回复(response)还是向源服务器请求一个新的回复。 例如:
Vary: User-Agent
针对于不同的类型客户端的访问,pc和移动端的ua不同,就会返回不同的缓存。
如下图(当Accept-Encoding不同时,返回不同的缓存)
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就是非缓存键,顾名思义,非缓存键就是缓存键之外的参数,这样的话就会引入安全问题,如果非缓存键能够被攻击利用的话,那么被投毒的响应就会返回给正常用户(关键)。
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的值可以出现再缓存中,那么这里的利用方式就可以和上面相同
多头部
看到下面这个请求
Param-mine进行探测,发现当同时出现X-Forwarded-Host和X-Forwarded-Scheme的时候,页面会进行重定向
未知头部
看到下面这个请求
Vary: User-Agent头部,内容中存在携带host引用的js资源
先使用param miner进行头部测试,发现x-host字段是可以替换host的非缓存键
x-host进行测试
User-Agent才行。这个时候需要让受害者去访问下攻击者的资源就行了,可以找类似于评论这种功能,看看是不是可以提交富文本内容即可。
HTTP请求行
请求参数未被缓存
看到下面的请求
隐藏参数缓存的情况。使用param miner进行测试。
utm_content是被缓存的,验证方式很简单,只要修改utm_content的值,如果缓存命中的话,那就是缓存参数了。
随后可以发现参数会在响应包中出现,那么就可以进行投毒了。
origin属于cache破坏,避免测试是直接投毒了首页缓存,避免业务故障。
看到下面这个请求
utm_conten参数未被缓存的情况。
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中的特殊符号进行规范化后缓存,导致正常的请求会返回异常请求的响应。
例如火狐的更新请求:
缓存键注入
缓存键构成一般都会由多个元素构成,如果存在可控元素的话,可以结合跨域origin进行CRLF注入。
看到下面这个案例
/js/localize.js?lang=en&cors=0
cors=1,并且添加origin头
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值,缓存键会不会被修改。
utm_content当然需要试试,实际上在真正测试时,可能存在其它的参数。
构造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漏洞&缓存投毒
看到下面的请求
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 星期日