Lab: Client side prototype pollution via flawed sanitization
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=barAfter 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.

There are other prototype pollution vectors that can be tried. For example:
/?__proto__[foo]=bar
/?constructor.prototype.foo=barHowever, Object.prototype is not changed in any case. In the DevTools of the browser, we now switch to the Sources tab and take a look at the loaded JavaScript files. We see that deparamSanitized.js uses the function sanitizeKey() defined in searchLoggerFiltered.js to remove potentially dangerous property keys based on a blacklist. However, this filter is not applied recursively.


Let's try to insert one of the blocked keys in the URL so that the dangerous key is still there after the cleanup. For example:
/?__pro__proto__to__[foo]=bar
/?__pro__proto__to__.foo=bar
/?constconstructorructor[protoprototypetype][foo]=bar
/?constconstructorructor.protoprototypetype.foo=barWe enter Object.prototype again in the console and see that it now has its own property foo with the value bar. We have successfully found a source of prototype pollution and bypassed website key sanitization.
Identify a gadget
We look at the JavaScript files again and see that searchLoggerFiltered.js dynamically attaches a script to the DOM by using the transport_url property of the config object, if present.
async function searchLogger() {
let config = {params: deparam(new URL(location).searchParams.toString())};
if(config.transport_url) {
let script = document.createElement('script');
script.src = config.transport_url;
document.body.appendChild(script);
}
if(config.params && config.params.search) {
await logQuery('/logger', config.params);
}
}No transport_url property is set for the config object. This is a potential gadget.
Craft an exploit
Now we use our previously identified key (/?__pro__proto__to__[foo]=bar) to insert an arbitrary transport_url property:
/?__pro__proto__to__[transport_url]=fooWe go to the "Elements" tab in the browser's DevTools and examine the HTML content of the page. Notice that a <script> element has been rendered on the page, with the src attribute foo.

Let's now modify the payload so that we inject an XSS proof-of-concept. To do this, we use the data: URL. The payload could look like this:
/?__pro__proto__to__[transport_url]=data:,alert(1);We now insert this payload into our URL and confirm with the ENTER key.
https://<YOUR-LAB-ID>.web-security-academy.net/?__pro__proto__to__[transport_url]=data:,alert(1);We receive the following window in the browser and see that the alert() function has been successfully called. We have now successfully completed the exercise.


Video Solution
Last updated