Yet Another Perspective on Prototype Pollution

Yet Another Perspective on Prototype Pollution

Prototypes

JavaScript is a programming language based on prototypes instead of classes. When a new object is created, the features of the prototype object are inherited – this includes arrays, functions, and even class definitions. The new object can also act as a template for other inheriting objects, transferring its properties, and creating the prototype chain. This object-based inheritance provides the flexibility and efficiency that web developers favor, yet this behavior opens applications to vulnerabilities via object manipulation.

JavaScript objects are easily manipulated via static methods as demonstrated in this blog post. So, when is a case of object manipulation considered to be prototype pollution?

Consider the following example of a constructor named Shape:

 

Graphical user interface, text, application, email</p>
<p>Description automatically generated

A new object instance of the Shape object is created using the keyword new

The inheritance schema can be illustrated as such:

 

The following shows the prototype chain where the __proto__ property of the objects are accessed. It can be seen that accessing beyond Object.prototype returns null. (Note that prototype is a property of a class constructor whereas __proto__ is the property of that class instance; they are not the same)

 

Since the square instance contains properties from Object.prototype, it is possible to call functions such as square.toString() or square.getPropertyOf().

 

Text</p>
<p>Description automatically generated

Prototype Pollution

Object manipulation is possible by assigning values to __proto__ since this property of Object.prototype is a getter and setter function (More information here). Prototype pollution occurs when an attacker is able to assign values to and modify __proto__ and change the behavior of the application. Since objects inherit the prototypes from the prototype chain, polluting Object.prototype causes every subsequent new instance of a JavaSscript object to be polluted. 

The following shows the modification of the toString() function in Object.prototype through prototype pollution, resulting in the inheritance of the polluted toString() function when a new instance of an object is created: 

 

Graphical user interface, text, application</p>
<p>Description automatically generated

The inheritance schema illustrates the testObject inheriting the properties from Object.prototype after the pollution: 

 

Diagram</p>
<p>Description automatically generated

An example of a vulnerable program is:

 

Text</p>
<p>Description automatically generated

If an attacker controls userInput1 and userInput2, they can set __proto__ to an arbitrary value via userInput2, resulting in the modification of Object.prototype.

Although this is a contrived example, there have been unsuspecting real-world applications that provide such conditions under certain circumstances. 

Is it Prototype Pollution?

Let us consider the following program which attempts to pollute Object.prototype through the usage of JSON.parse().

 

Graphical user interface, text, application</p>
<p>Description automatically generated with medium confidence

In this specific example, the prototype pollution vulnerability does not exist since Object.prototype has been shown to be unmodified despite the attempt to overwrite __proto__. It appears that JSON.parse() does not follow the default behavior of JavaScript and produces a new property on the object called __proto__ rather than setting the prototype of object.

The same behavior can be seen in Ramda. This proof-of-concept was thought to demonstrate the prototype pollution through Ramda’s mapObjIndexed() initially.

 

Text</p>
<p>Description automatically generated

Graphical user interface, background pattern</p>
<p>Description automatically generated

However, upon creating a new instance of an object, it was found that the new object did not inherit the supposedly polluted __proto__ property. This meant that Object.prototype was not modified or polluted.

 

Text</p>
<p>Description automatically generated

Background pattern</p>
<p>Description automatically generated

Takeaways

If prototype pollution can be defined as unauthorized and unintended modifications to Object.prototype that cause every new object to inherit the modified properties, then it can be assumed that JSON.parse(), Ramda’smapObjIndexed() or Object.assign() are not vulnerable to prototype pollution.

It appears that such behavior of prototype manipulation, by default and by design, is what makes JavaScript a popular language beyond the web. From an application security perspective, it is reassuring to learn that the security aspects of such design have been considered, and restrictions put in place to prevent exploitation. Still, it is important to not be complacent in light of ever-shifting parameters and constraints, especially a vulnerability as impactful as prototype pollution.