Prototype pollution
Last updated
Last updated
Prototype pollution is a JavaScript vulnerability that enables an attacker to add arbitrary properties to global object prototypes, which may then be inherited by user-defined objects.
Although prototype pollution is often unexploitable as a standalone vulnerability, it lets an attacker control properties of objects that would otherwise be inaccessible. If the application subsequently handles an attacker-controlled property in an unsafe way, this can potentially be chained with other vulnerabilities. In client-side JavaScript, this commonly leads to DOM XSS, while server-side prototype pollution can even result in remote code execution.
DOM Invader provides a number of features to help you test for client-side prototype pollution vulnerabilities. These enable you to perform the following key tasks:
Automatically detect sources for prototype pollution in the URL and any JSON objects sent via web messages. This includes detecting alternative techniques using the same source.
Generate a proof of concept by polluting the Object.prototype
using any discovered sources. You can then manually verify the vulnerability via the browser console.
Scan for potential gadgets that you can use to craft an exploit.
Setup: https://portswigger.net/burp/documentation/desktop/tools/dom-invader/enabling
DOM Invader finds two sources:
For these sources, we can scan for gadgets (in order to exploit these sources):
It finds a Sink (script.src)
DOM Invader makes it very easy for us, we can now just click "Exploit" and it will chain a XSS with the prototype pollution:
An obvious way in which websites attempt to prevent prototype pollution is by sanitizing property keys before merging them into an existing object. However, a common mistake is failing to recursively sanitize the input string. For example, consider the following URL:
If the sanitization process just strips the string __proto__
without repeating this process more than once, this would result in the following URL, which is a potentially valid prototype pollution source:
Example poor JS filter function:
Developers with some knowledge of prototype pollution may attempt to block potential gadgets by using the Object.defineProperty()
method. This enables you to set a non-configurable, non-writable property directly on the affected object as follows:
In this case, an attacker may be able to bypass this defense by polluting Object.prototype
with a malicious value
property. If this is inherited by the descriptor object passed to Object.defineProperty()
, the attacker-controlled value may be assigned to the gadget property after all.
JavaScript was originally a client-side language designed to run in browsers. However, due to the emergence of server-side runtimes, such as the hugely popular Node.js, JavaScript is now widely used to build servers, APIs, and other back-end applications. Logically, this means that it's also possible for prototype pollution vulnerabilities to arise in server-side contexts.
POST
or PUT
requests that submit JSON data to an application or API are prime candidates for this kind of behavior as it's common for servers to respond with a JSON representation of the new or updated object. In this case, you could attempt to pollute the global Object.prototype
with an arbitrary property as follows:
If the website is vulnerable, your injected property would then appear in the updated object in the response. We can perhaps use this vulnerability to escalate our privileges to admin:
Notice that the Object.entries looks at "isAdmin". We can set this to true using prototype pollution:
Result:
If you can find an object whose properties are visible in a response, you can use this to probe for sources. In the following example, we'll use UTF-7 encoding and a JSON source.
Add an arbitrary UTF-7 encoded string to a property that's reflected in a response. For example, foo
in UTF-7 is +AGYAbwBv-
.
Send the request. Servers won't use UTF-7 encoding by default, so this string should appear in the response in its encoded form.
Try to pollute the prototype with a content-type
property that explicitly specifies the UTF-7 character set:
Repeat the first request. If you successfully polluted the prototype, the UTF-7 string should now be decoded in the response:
Use the Server-Side prototype pollution scanner extension in Burp to scan a request
If you find a source, instead of specifying __prototype__ directly, use the constructor:
This suggests that the object doesn't have its own isAdmin
property, but has instead inherited it from the polluted prototype.
While client-side prototype pollution typically exposes the vulnerable website to DOM XSS, server-side prototype pollution can potentially result in remote code execution (RCE). Try polluting the prototype with a malicious execArgv
property that adds the --eval
argument to the spawned child process. Use this to call the execSync()
sink, passing in a command that triggers an interaction with the public Burp Collaborator server. For example:
If their are successful HTTP requests coming in we can assume RCE. more info: https://book.hacktricks.xyz/pentesting-web/deserialization/nodejs-proto-prototype-pollution/prototype-pollution-to-rce#pp2rce-via-env-vars--cmdline