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 星期日