Skip to content
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:

Screenshot
POST / HTTP/2
Host: 0afd009f042f35028008174e00710090.web-security-academy.net
Cookie: session=WvVTktpWEnVliloObWpvxV5uzGxS3ZeL
Content-Type: application/x-www-form-urlencoded
Content-Length: 11

search=miku
Screenshot

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.

Screenshot

Trying a GET request with a body to see what happens:

GET / HTTP/2
Host: 0afd009f042f35028008174e00710090.web-security-academy.net

teto=teto
Screenshot

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
Screenshot

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:

Screenshot

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

Screenshot
Screenshot

Deleting carlos will get the lab solved u-u

Resources