Bug Bounty & Agile Pentesting Platform

DOM-Based Cross-Site Scripting

Let’s start to understand the details of a DOM-based cross-site scripting vulnerability. We are going to hear about the basic description of the vulnerability and how to search for it.

What is DOM-based cross-site scripting?

DOM-based XSS is occurring if Javascript is executed coming from a source that is controlled by an attacker, landing in a sink that allows code execution.

Let’s try to understand the concept of sources and sinks first:

A source is a client-side container of data under control of an attacker. Think of the URL, request headers, cookies, etc. Have a look at the “domxsswiki” for a good summary of sources.

A sink on the other hand is the place where attacker-controlled data lands and is getting executed. Typical sinks include the eval() function or the innerHTML element property.

You might be wondering right now why this whole vulnerability is called DOM-based XSS. The reason for this is that attacker controlled data is manipulating the DOM during runtime. The DOM is a model describing the look of the web application at the time you are browsing the app. This model can change, e.g. when you click a button, when you are inserting data, etc.

It is important to understand that not every point where data lands is called a “sink”. We generally only speak of “sinks”, if Javascript execution is possible. Have a look at the “domxsswiki” for a good list of sinks.

How do you search for DOM-based cross-site scripting vulnerabilities?

Let’s look at ways how you can manually search for DOM-based XSS vulnerabilities. There are also automated tools that can help you on the way. We are covering those in our “Hacking Tools” section (Quick sneak peak: Burp Suite’s DOM Invader is a really good options).

Manually testing for DOM-based XSS is a very time-consuming task. You have to track the flow of user input from the source to the sink.

Firstly, we need to differentiate between HTML sinks and Javascript sinks that are executed during runtime. For HTML sinks, you will have to use the browser’s developer tools looking at the “Elements” tab (or “Inspector” in Firefox). For Javascript sinks, you have to use the “Sources” tab (or “Debugger” in Firefox).

As a next step, we need to insert data into all available sources. To ease the process of identifying your data, you can send an alphanumeric string (e.g. t9e8s7t6) to the application. Make sure that your string is randomly chosen and not a word that is in use by the application.

Next up, go to the “Elements” tab and use the search functionality to find your random string in the DOM.

Alternatively, go to the “Sources” tab and search where your user input is being read. From there, follow the data flow by using the debugger’s controls until you find a sink.

As a final step, you have to figure out in which context your input is getting inserted (e.g. within HTML tags, inside an event handler, etc.). Depending on the context, you can start to try sending various XSS payloads to the application (more on that in the next paragraph).

How do you test for DOM-based cross-site scripting?

XSS vulnerabilities come in various different shapes. There is no one golden payload that works in every case. On the one hand, you can go ahead and try payloads listed on pages like OWASP’s XSS filter evasion cheatsheet. On the other hand, it is not recommended to spread payloads over an application that you don’t understand.

It’s important to understand that specific cross-site scripting payloads do not just work for either reflected, stored, or DOM-based vulnerabilities. There is a lot of common ground, which is why we are summarising attack vectors in the article below.