Prototype Pollution
Exploit recursive property setting functions with special .__proto__ and .prototype options to add fallbacks to other property accesses
Last updated
Exploit recursive property setting functions with special .__proto__ and .prototype options to add fallbacks to other property accesses
Last updated
JavaScript has a feature called "Object prototypes" that allows you to add default fallback properties to objects as a fallback if they don't exist yet. Every type has a separate prototype, but instances of the same type will share that prototype.
Check out the following article for a more detailed explanation of prototypes and pollution:
Where polluting comes in is when an application allows you to set arbitrary properties on an object. This allows you as the attacker to set the __proto__
property and alter other objects because you control fallback values. Common sources of pollution come from recursive property setting functions like merge()
, or parsing some attacker-controlled string into an object by using recursion.
To better understand why this is vulnerable you should follow it with a debugger by setting a breakpoint at the merge function. The function will first take the first attribute from the source
which is __proto__
, and then because its value is an object, enter the recursion by calling itself with both values. This again looks at the attributes and finds b which isn't an object on target and source, so it sets the attribute directly on the target. At this step, we took target["__proto__"]
, and then set ["b"] = 2
on it. This effectively does the same as in the previous example and will pollute the whole Object
prototype as shown by the newObj
.
In some other cases, you will find that this function isn't recursively setting properties, but instead sets the final property to a whole value at once. An example of this was a vulnerability in xml2js < 0.5.0, where XML was parsed into an Object. This situation is not vulnerable to prototype pollution as it mimics the following example:
For more complex types that aren't directly Object
s, their prototype may be different from the target variable that you want to pollute. Take an HTML element, for example. This is a complex type with a lot of nested inheritance, but by chaining enough properties we can reaccess the Object prototype as everything in JavaScript inherits from it:
constructor.prototype
Because this vulnerability is relatively well-known, some developers correctly block the __proto__
key from being set. This prevents the attack shown above, but there is another important keyword .prototype
that all constructors have. We can easily access an instance's constructor by accessing its .constructor
property.
This is useful as it semi-replaces the need for __proto__
in most cases, but one caveat is that we cannot simply chain them on top of each other because we will reach a loop of getting the same constructor every time, thus never reaching Object. Luckily, some other properties have different types, some of which may be Object
s themselves. Accessing such an instance's .constructor.prototype
brings us back to the Object prototype with which we can pollute anything.
The above creates an HTMLDivElement
as an example starting point and finds paths all the way to a raw Object
:
That means you are able to pollute the Object
prototype by setting the following properties:
When you find a way to pollute the prototype and have confirmed that any new instance of that type has the fallback property you set, it is time to find a way to exploit it. There are some common patterns that will unknowingly use prototype properties if their regular properties aren't set. These can then be overwritten and cause all kinds of extra behaviour inside the code. You may find a way to add a sensitive property that should normally not contain user input.
Here are a few examples of patterns to look for. What these all have in common is that properties are accessed, and prototypes will also be looked at:
The above is useful when a custom gadget needs to be found, but common libraries have already been researched to find common gadgets collected in the following repository:
Ordinarily, JavaScript runs in the Browser and the impact is often XSS. But engines like NodeJS which also support prototypes in the same way are also vulnerable to the same types of attacks. Gadgets will now be targetting the server side of the application, often resulting in Remote Code Execution by adding the right properties.
Read more about detecting such vulnerabilities in the following article:
Next, you can find many libraries that also have known server-side gadgets allowing for high-impact bugs: