The lab
here is how portswigger describes it
This lab involves a front-end and back-end server, and the front-end server doesn't support chunked encoding.
The application is also vulnerable to reflected XSS via the User-Agent header.
To solve the lab, smuggle a request to the back-end server that causes the next user's request to receive a response containing an XSS exploit that executes alert(1).
this follows the capture requests lab. that one stole what a victim sent to the server. this one flips the direction, we control what the victim receives back, by smuggling a request so that its response, carrying our xss, lands in the next visitor's browser.
The idea#
reflected xss normally has a delivery problem. the payload lives in a request, so to hit a victim you have to trick the victim into sending that request themselves, usually with a crafted link. here the xss lives in the User-Agent header, and no one is going to send our User-Agent for us. request smuggling solves the delivery. we smuggle a request that already carries the malicious User-Agent, and because of the desync the response to that smuggled request gets served to whoever browses next. the victim never sent our payload, they just loaded the site, and our xss executes in their browser. this is the whole point of pairing smuggling with reflected xss, smuggling becomes the delivery mechanism the reflected bug was missing.
Step 1 - Find the reflected xss#
we visit a blog post, intercept the request, and send it to repeater. looking at the page, the comment form drops our User-Agent header into a hidden input field.

because our header is placed inside an html attribute, we can break out of it. we set the User-Agent to a payload that closes the attribute and the tag and then opens a script.
"/><script>alert(1)</script>
sending that, the response reflects it unescaped and the script sits live in the page, so the User-Agent header is a working reflected xss sink.

Step 2 - Smuggle the XSS to the next visitor#
now we deliver it to someone else. we switch to http/1, disable the automatic content length update, and send this.
POST / HTTP/1.1
Host: your-lab-id.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 150
Transfer-Encoding: chunked
0
GET /post?postId=5 HTTP/1.1
User-Agent: a"/><script>alert(1)</script>
Content-Type: application/x-www-form-urlencoded
Content-Length: 5
x=1

this is the cl.te frame again. the front end trusts Content-Length: 150 and forwards everything, the back end trusts Transfer-Encoding, meets the 0 chunk, ends the outer request, and treats the GET /post?postId=5 block as a smuggled request. that smuggled request asks for a blog post and carries our xss in its User-Agent, so the back end builds a response for it with our script reflected into the page. the next visitor's request arrives and gets paired with that poisoned response, so their browser loads a blog post page with alert(1) baked in and the script runs. that fires the alert on a victim, which solves the lab.
how this attack works and what poisoning means?#
the underlying technology is the same http desync that runs through this whole series. a front end and a back end share a connection and pass many requests over it back to back, and they only stay in sync if they agree on where each request ends. cl.te breaks that agreement, the front end measures our request by Content-Length and the back end by Transfer-Encoding, so the back end thinks our one request was actually two, and the second one, our smuggled request, is left sitting in the connection.
poisoning is what that leftover does to the queue. servers answer requests on a connection in order, first request gets the first response, and so on. our smuggled request slips into that line without a matching request from anyone, so the ordering is knocked off by one. when the next genuine visitor sends their request, the back end hands back the response that was queued for our smuggled request instead of a fresh one for theirs. that mismatched, attacker chosen response is the poison, the victim asked for one thing and received our thing. in this lab the poison is a page carrying alert(1), so the victim's browser simply runs our script.
it is worth being clear about scope, this lab is solved the moment alert(1) runs in a victim's browser, so we do not go further here. but the reason this class of bug is rated so seriously is that the script we control could do far more than pop an alert. the same poisoned response could carry javascript that reads the victim's cookies and sends them to a server we own, and if that victim were an administrator, their captured session would let us log in as the admin. the alert is just the proof, arbitrary script running in another user's authenticated session is the real danger.
with this, we solved the lab!
