Skip to content
Field Details
Platform PortSwigger Web Security Academy
Type HTTP Request Smuggling — CL.TE, Web Cache Deception
Difficulty Expert
Objective Trick the cache into storing the victim's /my-account response under the tracking.js cache key, then retrieve their API key

Exploiting HTTP Request Smuggling to Perform Web Cache Deception

Logging in as wiener:peter. The account page shows an API key:

Screenshot

The lab says the front-end caches static resources. Intercepting GET /resources/js/tracking.js:

Screenshot
Cache-Control: max-age=30

The file has a 30-second cache window, age incrementing by one each second. This is the cache deception target — if we can make the cache store the victim's /my-account response under the tracking.js cache key, any request for tracking.js within that window returns the victim's account page with their API key.

Intercepting a root request, switching to POST and HTTP/1.1. Testing the CL.TE desync with GET /miku:

POST / HTTP/1.1
Host: 0ab700c203afa17680281c7400cc0055.web-security-academy.net
Content-Length: 35
Transfer-Encoding: chunked

0

GET /miku HTTP/1.1
Teto: teto
Screenshot

404 Not Found — desync confirmed.

This is the inverse of cache poisoning. In cache poisoning, we poison the cache so a static resource URL serves malicious content. In cache deception, we trick the cache into storing a sensitive response under a static resource URL — so anyone requesting that static URL gets the victim's authenticated response back.

The mechanism: we smuggle a GET /my-account prefix. When the victim's next request arrives on the connection, the back-end processes GET /my-account first — using the victim's session cookie from their appended request — and generates the account page response. The front-end, which thinks this is a response to the victim's tracking.js request, caches it under the tracking.js key.

POST / HTTP/1.1
Host: 0ab700c203afa17680281c7400cc0055.web-security-academy.net
Cookie: session=oh83JnlttNswCWCXCZfQfbsf7kNDgJPz
Content-Type: application/x-www-form-urlencoded
Content-Length: 41
Transfer-Encoding: chunked

0

GET /my-account HTTP/1.1
Teto: teto

Our own session cookie is included so the smuggled request processes correctly. The back-end waits for the victim's request to complete the smuggled prefix. When the victim's browser requests tracking.js, their real request gets appended to our smuggled GET /my-account — the back-end processes the account page under their session and returns it, and the front-end caches it under the tracking.js key.

Sending the attack repeatedly — after a few cycles, we intercept a 200 OK containing the administrator's account page:

Screenshot

The administrator's API key is in the cached response. Submitting it solves the lab :P

Resources