The DOM#

The Document Object Model (DOM) is HTML. The browser takes HTML, executes Javascript, render the page.

Javascript can be used to manipulate the DOM (HTML), including nodes, objects and properties.

DOM-based vulnerabilities arise when a website use JavaScript that:

  • Takes an attacker-controllable value, known as a source.
  • Passes it into a dangerous function, known as a sink.

To deliver a DOM-based XSS attack, you need to place data into a source so that it is propagated to a sink and causes execution of arbitrary JavaScript.

Sources and Sinks#

Sources#

A source is a JS property that accepts data that is potentially attacker-controlled

For example, location.search property. It reads input from the GET query string in the URL, which is relatively simple for an attacker to control

// URL is https://portswigger.net/web-security/dom-based?key=value
>> location.search  

"?key=value"

Common sources#

The following are typical sources that can be used to exploit a variety of taint-flow vulnerabilities:

document.URL
document.documentURI
document.URLUnencoded
document.baseURI
location
document.cookie
document.referrer
window.name
history.pushState
history.replaceState
localStorage
sessionStorage
IndexedDB (mozIndexedDB, webkitIndexedDB, msIndexedDB)
Database

Sinks#

A sink is a potentially dangerous JavaScript function or DOM object

For example, the eval() function. It takes a string and run it as JavaScript

An example of an HTML sink is document.body.innerHTML because it potentially allows an attacker to inject malicious HTML and execute arbitrary JavaScript.

Common Sinks#

The following are some of the main sinks that can lead to DOM-XSS vulnerabilities:

document.write()
document.writeln()
document.domain
element.innerHTML
element.outerHTML
element.insertAdjacentHTML
element.onevent

The following jQuery functions are also sinks that can lead to DOM-XSS vulnerabilities:

add()
after()
append()
animate()
insertAfter()
insertBefore()
before()
html()
prepend()
replaceAll()
replaceWith()
wrap()
wrapInner()
wrapAll()
has()
constructor()
init()
index()
jQuery.parseHTML()
$.parseHTML()

.innerHTML quirks#

The innerHTML sink does NOT accept script elements and svg onload event.

This means you will need to use alternative elements like img or iframe with events like onload and onerror

element.innerHTML='... <img src=1 onerror=alert(document.domain)> ...'