Lab: DOM XSS via an alternative prototype pollution vector

Find a prototype pollution source

After starting the exercise, the blog should look similar to the following illustration.

We now try to contaminate Object.prototype in our browser by inserting any property (/?__proto__[foo]=bar) via the query string. To do this, we click in the address line of the browser and add our property to the URL. The address line now looks like this:

https://<YOUR-LAB-ID>.web-security-academy.net/?__proto__[foo]=bar

After we have pressed the ENTER key, we open the DevTools of our browser. To do this, press the key combination CTRL + SHIFT + I. In the DevTools we switch to the Console tab.

In the console, we now enter Object.prototype and confirm our input with ENTER. If we now examine the properties of the returned object, we find that our injected foo property has not been added.

Now we switch back to the browser and try another Prototype Pollution Vector. This time we use /?__proto__.foo=bar. In the DevTools we now see our added property foo with the value bar.

We have now found a successful Prototype Pollution Source.

Identify a gadget

In DevTools, we switch to the Sources tab. Here we see all loaded JavaScript files.

Let's take a closer look at the searchLoggerAlternatives.js file. For a better overview, the source code can be seen in the following code snippet.

async function logQuery(url, params) {
    try {
        await fetch(url, {method: "post", keepalive: true, body: JSON.stringify(params)});
    } catch(e) {
        console.error("Failed storing query");
    }
}

async function searchLogger() {
    window.macros = {};
    window.manager = {params: $.parseParams(new URL(location)), macro(property) {
            if (window.macros.hasOwnProperty(property))
                return macros[property]
        }};
    let a = manager.sequence || 1;
    manager.sequence = a + 1;

    eval('if(manager && manager.sequence){ manager.macro('+manager.sequence+') }');

    if(manager.params && manager.params.search) {
        await logQuery('/logger', manager.params);
    }
}

window.addEventListener("load", searchLogger);

In line 18, we see the JavaScript function eval(). We also see that the manager.sequence property is passed to eval(), but this is not defined by default.

Craft an exploit

We now use the prototype pollution source we just identified to inject a random sequence property with an XSS PoC into the object. Such a payload could look like this: /?__proto__.sequence=alert(1)

We add this payload to our address line in the browser and confirm with ENTER.

https://<YOUR-LAB-ID>.web-security-academy.net/?__proto__.sequence=alert(1)

After pressing ENTER we do not see any changes in the browser. If we now switch to our DevTools and select the Console tab, we see an error message. This is shown in the following code snippet.

VM33:1 Uncaught (in promise) SyntaxError: missing ) after argument list 
at searchLogger (searchLoggerAlternative.js:18:76)
||searchLogger|@|searchLoggerAlternative.js:18|

There are two links in the message, if we select the upper link (searchLoggerAlternative.js:18:76), we get to the Sources tab, where the eval() function is called.

 eval('if(manager && manager.sequence){ manager.macro('+manager.sequence+') }');

We now set a breakpoint in line 18 (it could be a different line for you), where the eval() function is called. To do this, we simply click on the line number.

Now we refresh the web page and see that the execution stops at our breakpoint.

We now hover over manager.sequence and see that the value is alert(1)1. This indicates that we have successfully passed our payload to the sink, but a numeric character 1 is appended, resulting in invalid JavaScript syntax. We now remove our breakpoint by clicking on the line number and then press F8 to continue the code execution. In the address line we add - to the end of the line (this corrects the JavaScript code, in other environments this can be characters other than -) so that our URL looks like this:

https://<YOUR-LAB-ID>.web-security-academy.net/?__proto__.sequence=alert(1)-

If we now press ENTER, a window opens in the browser with the 1. We have now successfully completed the exercise.

Video Solution

Last updated