Share on :
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.
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
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"
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
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
.
This can happen by simply poisoning already existing properties like toString
, valueOf
, ...etc to give unexpected output that produces some errors.