Overview
Stored Cross-Site Scripting (XSS), also known as Persistent XSS or Type-II XSS, is a vulnerability where malicious scripts are permanently stored on the target server (typically in a database) and later retrieved and executed when users view the infected content. Key Characteristic: The malicious payload is permanently stored in the application’s database and executes automatically for every user who views the infected content.How Stored XSS Differs from Other XSS Types
| Feature | Stored XSS | Reflected XSS | DOM-based XSS |
|---|---|---|---|
| Persistence | Persistent (in database) | Non-persistent | Non-persistent |
| Attack Delivery | Automatic on page load | Requires victim to click link | Requires victim to visit crafted URL |
| Social Engineering | Not required after injection | Required per victim | Required per victim |
| Scope | All users viewing content | Single victim per link | Single victim per link |
| Severity | Highest (affects all users) | Medium (targeted) | Medium (targeted) |
| Detection | Easier (persisted in data) | Harder (no trace after execution) | Harder (client-side only) |
- No social engineering needed after initial injection
- Affects ALL users who view the content
- Persists until manually removed from database
- Can be used to create self-propagating XSS worms
Vulnerability Objective
Goal: Redirect everyone to a web page of your choosing by injecting malicious JavaScript into the guestbook that persists in the database.Application Context
DVWA’s Stored XSS module implements a guestbook feature with two input fields:- Name field (max 10 characters)
- Message field (max 50 characters)
guestbook database table and displayed to all users.
Security Level Analysis
Low Security
The low security level provides minimal sanitization - only SQL escaping, no XSS protection. Vulnerable Code (xss_s/source/low.php:3-17):
- Message Field:
Medium Security
The medium level adds protection to the message field but has inconsistent filtering on the name field. Protection Code (xss_s/source/medium.php:3-19):
- Alternative Tags:
/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i matches variations of “script” with any characters in between (case-insensitive).
Bypass Techniques:
Use HTML event handlers instead of <script> tags:
- Image Tag:
- Input Focus:
Impossible Security (Secure Implementation)
The impossible level demonstrates proper XSS prevention for stored data. Secure Code (xss_s/source/impossible.php:3-26):
- Prepared Statements: Prevents SQL injection using PDO parameter binding
- Steals cookies from every viewer
- Automatically posts itself to the guestbook
- Spreads to more users exponentially
Scenario 2: Admin Account Takeover
- Inject payload targeting admin actions:
Proper Defenses
1. Output Encoding (Essential)
Encode on output, not just input:3. Content Security Policy
5. Database Design
Store metadata about content:- Image Tag:
- Event Handler:
Code Reference
Source files:vulnerabilities/xss_s/source/
- Low:
low.php:3-17(no XSS protection) - Medium:
medium.php:8-15(inconsistent filtering) - High:
high.php:14(regex on name field) - Impossible:
impossible.php:12-19(proper encoding)
xss_s/index.php:44-58
Key Takeaways
- Stored XSS is the most dangerous XSS type - affects all users automatically
- Blacklist filtering fails - use whitelist validation and output encoding
- Encode on output, not input - preserves data integrity and prevents double-encoding
- Context matters - HTML, JavaScript, CSS, and URL contexts need different encoding
- Defense in depth - combine output encoding, CSP, input validation, and CSRF protection
- SQL escaping ≠ XSS protection -
mysqli_real_escape_string()only prevents SQLi - Inconsistent protection is dangerous - protect ALL input fields equally
Related Vulnerabilities
- Reflected XSS - Non-persistent XSS attacks
- DOM-based XSS - Client-side XSS exploitation
