Authorization disclaimer. This walkthrough is limited to the intentionally vulnerable PortSwigger Web Security Academy lab linked below. Test only systems you own or are explicitly authorized to assess.

Overview

This lab isolates Stored XSS defeats CSRF tokens. Stored comment script runs same-origin, requests /my-account, extracts the csrf input, then posts email and that token to /my-account/change-email. The victim only needs to view the poisoned comment; same-origin XSS can read the token that blocks cross-origin CSRF.

Lab objective/setup

Open the Academy exercise at the reserved lab URL and work only in that disposable environment. The required proof is specific: The victim administrator email changes to the attacker-selected value and the lab solves.

What I observed

Stored comment script runs same-origin, requests /my-account, extracts the csrf input, then posts email and that token to /my-account/change-email. The victim only needs to view the poisoned comment; same-origin XSS can read the token that blocks cross-origin CSRF.

Academy objective requires stored XSS to read a same-origin CSRF token and change the email address of a comment viewer.
Academy objective requires stored XSS to read a same-origin CSRF token and change the email address of a comment viewer.
My Account page for user `wiener` shows the normal email-change form whose hidden token must be obtained by the script.
My Account page for user `wiener` shows the normal email-change form whose hidden token must be obtained by the script.

Why it is vulnerable

The application applies protection for the wrong parser or omits a control at the decisive boundary. In this case, stored comment script runs same-origin, requests /my-account, extracts the csrf input, then posts email and that token to /my-account/change-email. Browser parsing therefore preserves the attacker-controlled behavior instead of reducing the value to inert text or a safe URL.

Walkthrough in the authorized PortSwigger lab

First reproduce the recorded page or request and inspect the exact reflected, stored, or DOM-generated context described above. Then supply the following lab payload as data, not as active markup in this article:

1
`var token=this.responseText.match(/name="csrf" value="([^"]+)"/)[1];`

The victim only needs to view the poisoned comment; same-origin XSS can read the token that blocks cross-origin CSRF.

Account page shows the email changed to `test@example.com`, validating the request structure during the attacker-controlled test session.
Account page shows the email changed to `[email protected]`, validating the request structure during the attacker-controlled test session.
Burp message editor shows the same-origin account request and the follow-up email-change POST carrying an extracted CSRF token.
Burp message editor shows the same-origin account request and the follow-up email-change POST carrying an extracted CSRF token.

Verification

The victim administrator email changes to the attacker-selected value and the lab solves. This distinguishes execution from a payload merely appearing in source or being stored without reaching the required event or sink.

Impact and defensive lesson

In a real application, equivalent XSS could act with the victim’s same-origin privileges, expose readable data, or perform authenticated actions. Fix the data flow at its context: use text-only DOM APIs, JavaScript- or attribute-aware encoding, strict URL-scheme allowlists, and maintained sanitization where HTML is unavoidable. HttpOnly cookies and a restrictive CSP reduce consequences but do not repair the injection flaw.

References