Finding Client-Side Prototype Pollution with DOM Invader | Blog


Last year, we made finding DOM XSS much simpler by introducing a brand new tool called DOM Invader. This year, we’ve enhanced DOM Invader to make it easier to find CSPPs (Client-Side Pollution Prototype) with just a few clicks. If you want to investigate, find, and fix client-side pollution prototype vulnerabilities, you really should keep reading – to find out how DOM Invader makes your life easier. We’ve also created another YouTube video to help you use the new features:

What is prototype pollution?

We hope to publish client-side pollution lab prototypes in our Web Security Academy demonstrating the problem in a few months, but for now, here’s what you need to know.

Prototype pollution is a vulnerability that occurs when you merge an object with a user-controlled JSON object. This can also happen as a result of an object generated from query/hash parameters, when the merge operation does not clean up the keys. This allows an attacker to use property keys like __proto__ , which then allows them to create arbitrary assignments to the Object.prototype (or other global prototypes). When this occurs, it is referred to as a prototype pollution source. The following code example demonstrates this:

params.replace(/+/g, ' ').split('&').forEach(function(v){
var param = v.split( '=' ),
            key = decodeURIComponent( param[0] ),
            cur = obj,
            I = 0,
let url = new URL(location);
let params = url.searchParams;

To exploit the prototype pollution, you need a source and a gadget. A prototype pollution gimmick occurs when a site uses a property in an unsafe way without filtering. For example, a site can do the following:

let myObject = {};
if(myObject.html) {
   document.getElementById('myElement').innerHTML = myObject.html;

At first glance, it might seem that there is no problem here. The object does not contain any properties, but the JavaScript engine will examine the Object.prototype for the “html” property if it does not exist on the current object. This then leads to a prototype anti-pollution gadget called “html”. Let’s see what happens when we modify the Object.prototype:

This results in the use of the Object.prototype.html property, instead of the “html” property of the “myObject” object. A developer will assume that these properties are not controlled by the user and therefore lead to XSS.

How to discover the sources of pollution of the prototypes on the client side?

If you want DOM Invader to find prototype pollution sources, you need to enable the prototype pollution option.

Screenshot showing how to enable prototype pollution

When you’ve enabled it, go to a site you want to test. You can use one of our test cases if you want to see how it works. DOM Invader will attempt to test the query string, hash, and JSON objects sent using a web message and report if it was successful.

Screenshot showing prototype pollution sources

In this case, DOM Invader found two prototype pollution sources that both occur in the query string – denoted by “looking for”. You can use the “Test” button to manually check the source, or you can use the “Search gadgets” button to automatically discover gadgets. If you choose the latter, DOM Invader will open a new window and display a progress bar. Once the scan is complete, it will show you the results in the augmented DOM:

Screenshot showing gadget search result

In the example above, DOM Invader has discovered a gadget called “html”, which ends up in an innerHTML sink. You will notice that a green “Exploit” button has appeared – this will combine the discovered source with the gadget and automatically create a pollution exploit prototype.

If you want to try DOM Invader with a real CSPP vulnerability, we have hidden one in our Gin and juice shop; see if you can exploit it!

Find pollution prototypes on real sites

As always at PortSwigger, we use our tools to find real-world vulnerabilities – in doing so, we encountered many issues that we could fix by improving DOM Invader. The first thing that became apparent when scanning the gadgets was that we were getting a lot of noise from uninteresting sinks. To solve this problem, we decided to show only interesting wells by default. If you are not satisfied with the default, you can change the sources/sinks displayed if you wish.

We wanted to automate the discovery of pollution source prototypes and found the best way to do this was to use Puppeteer. However, we had a problem, how to eliminate the DOM Invader vulnerabilities? We could use Puppeteer to traverse the DOM like we did for our automated tests, but that would be slow and cumbersome.

So we decided to add callbacks in DOM Invader. Callbacks allow you to run JavaScript when a source, sink, or message has been found, making it easier to log vulnerabilities. If you open the configuration cog again as before, you will notice that each sub-tab has a reminder configuration button. This callback will allow you to call custom JavaScript whenever an element has been found, and the data will be passed to the callback which you can use:

Screenshot showing receiver callback setup button

Screenshot showing receiver callback code

Using these callbacks is really powerful, you can use navigator.sendBeacon or fetch to send this data to an endpoint that logs the data. You can return true or false if you want DOM Invader to display the data – this can be very useful if there is a noisy site and you want to know what data is reaching a specific sink. Reminders are disabled by default, which is why they are grayed out – once you edit one and click save, it becomes active. You can use the reset button to disable the snooze function and return it to its default state.

I created a source callback:

function(sourceDetails, sources) {
   let data = JSON.stringify(sourceDetails);
   let url="http://localhost:8000/log.php";
   fetch(url, {__proto__:null, method: "post", keepalive: true, body: data});
   return true;//return true to log source

This sent the data to a PHP script which saved the data. I then started testing them for gadgets. You can research gadgets independently, even if the site has no known prototype pollution source. To do this, you need to switch modes in the prototype’s pollution settings cog:

Screenshot showing the prototype pollution settings cog

Screenshot showing how to search for gadgets

You’ll notice that DOM Invader tries to choose the optimal settings for parsing gadgets – for example, it will strip CSP response headers – you can override these defaults if you wish. Using these techniques, I discovered several sites vulnerable to client-side prototype pollution, including a well-known car manufacturer, a well-known gaming site, a major WordPress domain, and others.

Credits and acknowledgments

Like always James kettle was super helpful with the DOM Invader design and made the great suggestion to have a “Scan for gadgets” button thanks James. Thanks to Nolan Ward for the excellent graphics and video editing. There have been a few excellent research in client-side prototype pollution which I found really useful. Thanks to Sergei Bobrov, Mohan Sri Rama Krishna P., Terjanq, Beomjin Lee, Masato Kinugawa, Nikita Stupine, Rahul Maini, jaiswal hard, Mikhail Egorov, Dev Melar, Michal Bentkowski, File descriptor, Olivier, William Bowling, John Bouchard to share their great tools and research.

Get the new version of DOM Invader

To get the new version of DOM Invader, simply update your version of Burp Suite Professional or Burp Suite Community Edition to 2022.6 on the Early Adopter Channel to start using it.


Comments are closed.