Introduction to prototype pollution

Share on :

introduction to protoypical inheritance

JavaScript objects are dynamic "bags" of properties (referred to as own properties). JavaScript objects have a link to a prototype object. When trying to access a property of an object, the property will not only be sought on the object but on the prototype of the object, the prototype of the prototype, and so on until either a property with a matching name is found or the end of the prototype chain is reached. as descriped in MDN

so for example when you do :

var test_obj = {};
var test_str = "hi";
var test_int = 1;

you have created an object test_obj which inherites its properties from the main javascript Object's protoype, a string test_str which inherites its properties from the main javascript String's and an integir test_int which inherites its properties from the main javascript Number's protoype.

var test_obj = {};
var test_str = "hi";
var test_int = 1;
cosnole.log(test_obj.toString()); // "[object Object]"
cosnole.log(test_str.toString()); // "hi"
cosnole.log(test_int.toString()); // "1"

In the previous code each of these objects inherited the toString property from their constructor's prototype.

A final thing to note is that adding or changing a property of any of these objects prototype doesn't change the protoype of their constructor, meanwhile if we do the inverse it should work...so if we add or change a property of the constructors prototype, this property gets inherited by all child objectsfor this constructor.

Getting practical

You can call the constructor of any variable using variable_name.constructor and you can get the prototype of a variable using variable_name.prototype, these are called magic properties.

var test_obj = {};
cosnole.log(test_obj.constructor); // function Object()

so now that we called the main Object what if we get its prototype

var test_obj = {};
cosnole.log(test_obj.constructor.prototype);

This will return an object containing all the properties of all objects inherited from the main Object in this code

First prototype pollution

What's good about calling prototype that it's a setter/getter magic property so we can set the returned value of it or of properties inside it.

var test1 = 1; // int (Number)
var test2 = 2; // int (Number)
console.log(test1.constructor); // function Number()
console.log(test2.constructor); // function Number()

// ---------------------------------------------
// test1.constructor.protoype is equivalent to Number.prototype
// Number.prototype returns an object with all properties of any int in the code which inherited from the main Number
// ---------------------------------------------

console.log(test2.toString()); // "2"
test1.constructor.prototype.toString = function(){return "hacked"}
console.log(test2.toString()); // "hacked"

Here we can see that we changed the returned value of the function toString to a controled value by poisoning the prototype of Number so now any toString function that's called on an intger will return the string "Hacked"

Exploiting in web applications

Nowadays tons of websites run Javascript in their backend, so a vulnerability like this can ruin the whole website, specially in a case like if the website is built on nodejs as the variables are the same across all users visiting the website. This can be used to perform DoS, Authentication bypass, RCE and much more.

As we know HTTP parameters value are treated as strings (unless someone evals them :D), so how can we inject the properties constructor and prototype as strings to perform the attack...

A good thing in javascript that we can call properties using another syntax

var test = {"a":1}
console.log( test["a"] === test.a ) // true

As we can see it can be used as a key for the object or as a property

Authentication bypass

consider this javascript code :

info[year][term][subject] = grade;
if(user.isAdmin() === 0){
    console.log("Hi user");
} else {
    showAdmin();
}

if year, term, subject and grade are user controllable an authentication bypass can be performed by setting year to constructor, term to prototype, subject to isAdmin and grade to any value you'd like to except 0, so when the user,isAdmin() check is performed it'll return the value of grade parameter which is not 0 so the check will pass and showAdmin() will be executed.

Another thing to note is the __proto__ magic property, this property is equivalent to constructor.prototype so that's a single property which calls the prototype of the constructor, What’s good to note about this property is that it’s implemented as a getter/setter property which invokes getPrototypeOf/setPrototypeOf on read/write. So assigning a new value to the property “__proto__” doesn’t shadow the inherited value defined on the prototype. The only way to shadow it involves using “Object.defineProperty”.

so the previous attack can happen if there's only two keys instead of 3 :

info[name][first] = firstName;
if(user.isAdmin() === 0){
    console.log("Hi user");
} else {
    showAdmin();
}

In this case we set the value of name to __proto__, the value of first to isAdmin and the value of firstName to whatever we want except 0 .

Denial of service

This can happen by simply poisoning already existing properties like toString, valueOf, ...etc to give unexpected output that produces some errors.