Skip to content
Field Details
Platform PortSwigger Web Security Academy
Type HTTP Request Smuggling — H2.TE, Response Queue Poisoning
Difficulty Practitioner
Objective Poison the response queue to intercept the admin's session cookie and delete carlos
Note The back-end connection resets every 10 requests — send a few normal requests to get a clean connection if things break.

Response Queue Poisoning via H2.TE Request Smuggling

This lab uses HTTP/2 on the front-end but downgrades to HTTP/1 for the back-end. In HTTP/2, request length is determined by the framing protocol — no Content-Length or Transfer-Encoding ambiguity at the front-end level. But when the front-end downgrades to HTTP/1 for the back-end, it has to re-express the body length — and if it passes through a Transfer-Encoding: chunked header from the HTTP/2 request, the back-end processes it as chunked while the front-end already handled framing separately. That's the H2.TE desync.

Intercepting a request and stripping it to the essentials:

POST / HTTP/2
Host: 0a7700d603dd07ed8235561800b70067.web-security-academy.net
Transfer-Encoding: chunked

0
Screenshot

200 OK — the front-end accepts the Transfer-Encoding header and passes it through. The back-end processes chunked encoding. Vulnerability is present.

To confirm, we smuggle a GET to a nonexistent path:

POST / HTTP/2
Host: 0a7700d603dd07ed8235561800b70067.web-security-academy.net
Transfer-Encoding: chunked

0

GET /miku HTTP/1.1
X-Ignore: x

The X-Ignore: x header is there so the next real request gets appended cleanly after it — without a trailing header, the victim's request method and path would be concatenated directly onto the last header value, breaking the structure.

Screenshot

Sending the attack request, then a normal request — the normal request comes back 404 Not Found instead of the expected response:

Screenshot

Confirmed. The smuggled GET /miku is being processed by the back-end, and its response is being delivered to the next incoming request.

Now for the actual attack. The technique here is different from standard prefix-injection smuggling — instead of poisoning the next request, we're poisoning the response queue.

When we smuggle a complete second request to the back-end, the back-end generates two responses: one for the outer POST and one for the smuggled inner request. The front-end, which thinks it only sent one request, delivers the first response to us and queues the second. When the next user's request arrives, they receive the queued response instead of the one generated for their actual request.

We craft the smuggled inner request to a nonexistent path so it produces a predictable 404 that goes into the queue. When the admin logs in, their request arrives and the admin gets our queued 404 — we receive the 302 redirect generated for the admin's login, including the Set-Cookie header with their session token.

Attack request:

POST /miku HTTP/2
Host: 0a7700d603dd07ed8235561800b70067.web-security-academy.net
Transfer-Encoding: chunked

0

GET /teto HTTP/1.1
Host: 0a7700d603dd07ed8235561800b70067.web-security-academy.net
Screenshot

Since catching the admin's session requires timing, we set up Intruder with null payloads, continuous, one thread, 800ms delay between requests:

Screenshot

We start the attack and wait for a 302 Found in the results — that's a response meant for the admin, and it contains their session cookie.

Screenshot

We paste the administrator's session cookie into the browser storage and navigate to /admin:

Screenshot
Screenshot

Delete carlos. Lab solved :P

Dead Ends & Rabbit Holes

  • Catching the admin's 302 requires timing and it was a pain. The admin logs in approximately every 15 seconds. Too high a request rate dirties the connection before the admin's request arrives and I had to wait until the lab restarted. 800ms delay with a single thread was the working configuration.

Resources