Whitebox Attacks  

JavaScript Objects & Prototypes


Before jumping into prototype pollution, we must establish a baseline about JavaScript objects and JavaScript prototypes.


Objects in JavaScript

JavaScript supports different data types, including simple ones such as numbers, strings, or booleans, and more complex ones called objects, which can consist of multiple data types. They are called the properties of the object. As an example, let us consider a JavaScript object representation of a Hack The Box Academy module. We can create a module object like this:

module = {name: "Web Attacks", author: "21y4d", tier: 2}

We can access properties of our module object with a dot followed by the property name:

module.name

The same syntax allows us to set additional properties of our object:

module.difficulty = "medium"

image

We can also create more complex objects by assigning functions or other objects as properties.


Prototypes in JavaScript

JavaScript uses a pre-defined notion of inheritance to provide basic functionality to all existing objects. This is implemented via Object prototypes. The prototype of an object is a reference to another object that is inherited from it. Each object inherits from a prototype. As such, the prototype of an object itself also has a prototype. This chain of prototypes is called the prototype chain. For example, let us consider our module object from before.

Our object has a property that defines the toString function, which we can call like so:

>> module.toString()

"[object Object]"

However, where does this property come from? We only explicitly defined our object's name, author, and tier properties, not the toString property. Our object inherits this property from the module object's prototype. We can check out the prototype of our object by accessing the __proto__ property:

image

We can see that the prototype of the module object is an object called Object.prototype. This is the base prototype that all created objects inherit. We can also see that this is where the property toString is defined. Whenever we access a property of our object that does not exist, the prototype is searched for this property. If it does not exist there, the prototype's prototype is searched, and so on, until the end of the prototype chain is reached. When the property is still not found, undefined is returned.

We can, of course, override inherited properties to implement specific requirements of our object. For instance, we can implement a custom toString function for our object:

module.toString = function() {return "This is the HTB Academy module: " + this.name;}

Since our object's properties have precedence over the prototype's properties, when we call the toString function, our custom toString function is executed:

image

This process of overriding a prototype's property is called shadowing.

Previous

+10 Streak pts

Next