Request tunnelling via desync
TL;DR: Tunnelled requests reach the back-end on a shared keep-alive connection but never traverse the front-end fully, leaking responses meant for other users across the same socket.
What it is
A variant family of http-request-smuggling where the smuggled bytes do not become a fully-formed second request from the front-end’s perspective but instead “tunnel” along the same back-end connection. The front-end thinks it forwarded one request; the back-end services that request, then services the smuggled remainder, then returns the response on the same TCP connection — which the front-end mis-attributes to the next inbound client.
Preconditions / where it applies
- Shared connection pooling between front-end (CDN, ALB, nginx, HAProxy) and back-end origin. HTTP/1.1 keep-alive or HTTP/2 → HTTP/1.1 downgrade.
- Header normalisation or framing mismatch — TE/CL disagreement,
Content-Length: 0plus a body, oversized chunk-size hex, or H2 pseudo-header smuggling into H1. - Multi-tenant front-end (CDN) pools connections from many clients onto few back-end sockets — that’s where the cross-user leak shows up.
Technique
- Find the desync. Same probes as classic smuggling: send CL+TE, ambiguous TE, or H2-to-H1 framing tricks. Watch for delayed-response and probe-collision behaviour with Burp’s Smuggler extension.
-
Tunnel a request that prefixes the next. The smuggled request body contains a partial request line that, when combined with the next victim request, alters routing or headers:
1 2 3 4 5 6 7 8 9
POST / HTTP/1.1 Host: target Content-Length: 200 Transfer-Encoding: chunked 0 GET /private HTTP/1.1 X-Ignore:
Front-end thinks one request of length 200. Back-end stops at the
0\r\n\r\n, then parsesGET /private...as a new request, but reads its headers from whatever the next victim request put on the wire — including theirCookie. - Cross-user response steal. Set up a slow-read on a smuggled request whose response will be served on the next take of the connection — collect the response intended for another user.
- Header injection. Tunnel a header (
X-Forwarded-For: 127.0.0.1) that bypasses front-end IP allowlists for the next victim’s request. - HTTP/2 specifics. The “downgrade” surface in http2-h2-downgrade-desync-v3 is the most fertile in current research — H2 lets the attacker inject CR/LF and header smuggling that the H1 back-end then re-parses.
Detection and defence
- Disable front-end → back-end keep-alive, or set
Connection: closeon every forwarded request. Performance hit, but it kills the cross-user channel. - Speak HTTP/2 (or HTTP/3) end-to-end and refuse downgrade. Validate H2 pseudo-headers; reject CR/LF and ASCII control in header values.
- Reject ambiguous H1 requests at the edge: both CL and TE, malformed TE, oversize chunks, header lines with leading whitespace.
- Patch and re-test: every PortSwigger desync paper since 2019 has surfaced new variants on stacks that thought they were fixed.
- Detection: 5xx clusters on a single back-end socket, response/request id mismatches in correlated logs, Burp Collaborator hits on tunnelled probes.
References
- PortSwigger — HTTP request smuggling, redux — H2 desync variants.
- PortSwigger — Browser-Powered Desync Attacks — client-side amplification.
- PortSwigger — Listen to the whispers (2024) — timing-side desync detection.