| Field | Details |
|---|---|
| Platform | PortSwigger Web Security Academy |
| Type | HTTP Request Smuggling — H2 Request Splitting via CRLF, Response Queue Poisoning |
| Difficulty | Practitioner |
| Objective | Poison the response queue to intercept the admin's session and delete carlos |
| Note | Back-end connection resets every 10 requests. Admin logs in approximately every 10 seconds. |
HTTP/2 Request Splitting via CRLF Injection¶
The application has a search bar. Intercepting a search:
POST / HTTP/2
Host: 0afd009f042f35028008174e00710090.web-security-academy.net
Cookie: session=WvVTktpWEnVliloObWpvxV5uzGxS3ZeL
Content-Type: application/x-www-form-urlencoded
Content-Length: 11
search=miku
Switching to POST and trying the standard Transfer-Encoding: chunked approach:
POST / HTTP/2
Host: 0afd009f042f35028008174e00710090.web-security-academy.net
Content-Length: 127
Transfer-Encoding: chunked
0
GET /miku HTTP/1.1
Host: 0afd009f042f35028008174e00710090.web-security-academy.net
Content-Length: 5
Teto: teto
teto=teto
No desync — always 200 OK. Trying CRLF injection via Inspector with the same Transfer-Encoding: chunked approach — still 200 OK.
Trying a GET request with a body to see what happens:
GET / HTTP/2
Host: 0afd009f042f35028008174e00710090.web-security-academy.net
teto=teto
403 Forbidden — GET requests cannot contain a body. The application explicitly rejects GET requests with a body, which rules out the standard smuggling approach where excess body bytes get buffered as the next request. We need a different technique: request splitting.
Instead of smuggling content in the body, we inject a complete second HTTP request directly into a header value. Add a header whose value contains \r\n\r\n followed by a full HTTP request — when the front-end downgrades to HTTP/1 and serializes the headers, the CRLF sequence terminates the header block and the injected content appears as a standalone second request on the connection. The outer request's body stays empty, so the GET body check never fires.
Using Burp's Inspector to add:
Name: Teto
Value: teto\r\n
\r\n
GET /miku HTTP/1.1
Host: 0afd009f042f35028008174e00710090.web-security-academy.net
The \r\n\r\n after teto ends the HTTP/1 header block — everything after is parsed by the back-end as a separate, complete HTTP request. Sending several times and a 404 appears, confirming the back-end is processing GET /miku as a new request.
With splitting confirmed, the rest is response queue poisoning — same mechanics as the H2.TE lab. We keep sending the CRLF-split request targeting a nonexistent path. Each send generates two back-end responses: one for our outer GET and one for the injected GET /miku. Our response comes back normally. The 404 for /miku sits in the queue. When the admin's login arrives, they get the queued 404 and we get the 302 meant for them — including the Set-Cookie header with their session token.
Sending the attack repeatedly until a 302 appears:
Drop the administrator's session cookie into browser storage and navigate to /admin:
Deleting carlos will get the lab solved u-u