You may be surprised to learn that there are a number of widespread prototype pollution gadgets in the JavaScript APIs commonly provided in browsers.
Prototype Pollution via fetch()
fetch('https://normal-website.com/my-account/change-email', {
method: 'POST',
body: 'user=carlos&email=carlos%40ginandjuice.shop'
})
As you can see, we’ve explicitly defined method
and body
properties, but there are a number of other possible properties that we’ve left undefined. In this case, if an attacker can find a suitable source, they could potentially pollute Object.prototype
with their own headers
property. This may then be inherited by the options object passed into fetch()
and subsequently used to generate the request.
You can use this technique to control any undefined properties of the options object passed to fetch()
. This may enable you to add a malicious body to the request, for example.
Prototype Pollution via Object.defineProperty()
Developers with some knowledge of prototype pollution may attempt to block potential gadgets by using the Object.defineProperty()
method:
Object.defineProperty(vulnerableObject, 'gadgetProperty', {
configurable: false,
writable: false
})
As we can see, Object.defineProperty()
accepts an options object, known as a
“descriptor”.
Among other things, developers can use this descriptor object to set an initial value for the property that’s being defined. However, if the only reason that they’re defining this property is to protect against prototype pollution, they might not bother setting a value at all.
An attacker may be able to bypass this defense by polluting Object.prototype
with a malicious value
property.
Lab: Client-side Prototype Pollution via Browser APIs
Found a source in search
param:
/?search=hello&__proto__[testproperty]=DOM_INVADER_PP_POC
Developers patched the gadget property in searchLoggerConfigurable.js
file:
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() {
let config = {params: deparam(new URL(location).searchParams.toString()), transport_url: false};
Object.defineProperty(config, 'transport_url', {configurable: false, writable: false});
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);
}
}
window.addEventListener("load", searchLogger);
Payload:
/?search=hello&__proto__[value]=data:,alert('xss')
Related
list
from outgoing([[Port Swigger - Prototype Pollution via Browser APIs]])
sort file.ctime asc