JavaScript, often abbreviated JS, is a programming language that is one of the core technologies of the World Wide Web, alongside HTML and CSS. As of 2022, 98% of websites use JavaScript on the client side for webpage behavior, often incorporating third-party libraries. It is a very common and widely used programming language.
Table of Contents
ToggleIt is an Object Oriented Programming language, where we use the concept of prototyping of objects. This blog will focused on the vulnerability occurs in object prototyping, it’s causes and how the preventive measures related to protype pollution.
This blog will cover the following Segments:
- Basic introduction about the prototype in JavaScript
- What is prototype pollution?
- Impact of prototype pollution
- Identification of the vulnerability
- Affected libraries with this vulnerability
- Preventive measures.
A basic introduction to Prototype in JavaScript
What is a prototype?
A prototype is an attribute related to an object, it is used as a mechanism that enables JavaScript objects to inherit features from one another. Since almost everything in JavaScript is an object, a prototype is an object too.
Understandable definition:
JavaScript is prototype-based: when new objects are created, they carry over the properties and methods of the prototype “object”, which contains basic functionalities such as toString, constructor, and hasOwnProperty.
It defines from where you stated your object creation it gives you default things to you to object. Prototype defines the object structure and its properties.
An objects prototype may also have a prototype, and from it, it can inherit its prototype or other attributes, and so on. This is referred to as a prototype chain.
Let’s understand this with the help of an example.
Here I have created an object name customer in JavaScript.
Now run this JS and check it in a browser console. Here we can see our object along with the prototype.
This prototype contains all the properties in pre inherited by this object on creation
What is Prototype Pollution?
Prototype pollution is an injection attack that targets JavaScript runtimes. With prototype pollution, an attacker might control the default values of an object’s properties. This allows the attacker to tamper with the logic of the application and can also lead to denial of service or, in extreme cases, remote code execution.
Prototype pollution occurs when an attacker manipulates proto property of an object, usually by adding a new prototype onto proto. Since proto exists for every object, and every object inherits the prototypes from its prototype, this addition is inherited by all the JavaScript objects through the prototype chain.
Impact of Prototype Pollution
NOTE: The impact of prototype pollution depends on the application
The most common way to cause prototype pollution is to use an unsafe merge or extend function to recursively copy properties from an untrusted source object.
- Depends on the application logic.
- If it can bypass authorization, then the impact is high.
- We can crash the application.
- It can cause other dangerous vulnerabilities such as RCE (Remote Code Execution), IDOR (Insecure Direct Object References), Bypass Auth, and other vulnerabilities, namely XSS.
Root cause of the vulnerability
In JavaScript, during the declaration of class, the prototype is modified at runtime. This means that by default, the program can at any point in time add, change or delete entry in the prototype of a class.
In JavaScript there is no distinction between a property and an instance function. So instance function and other properties are accessed in the exact same way. There are two notations to access property in JavaScript.
- The dot notation:Â Ex.-
1obj.a
- The square bracket notation:Â Ex.-
1obj["a"]
Figure 5: Dot and Square Bracket Notation
There are many properties that exist by default on the object prototype, out of which “proto” is a property which returns prototype of the class of the object.
Let’s take an example:
Here I have created an object name “testobj” . Now using “proto” property I am changing the prototype at run time using square bracket notion method.
The general idea behind prototype pollution starts with the fact the attacker has control over least the parameter “a” and “value” of any expression of the following form:
1Object[a][b] = value
If the attacker has control over the values, the attacker can set “a” to “proto” and the property with the name defined by “b” and it will be defined on all existing object (of the class of “obj”) of the application with the value “value”.
Note:Â If the object that you are polluting is not an instance of “object” remember that you can always move up the prototype chain by accessing the “proto” attribute of the prototype (ex.: “inst.proto.proto” points to the prototype of “Object”).
Identification of the vulnerability in the libraries.
To identify this vulnerability, try to find these operations in the source code.
- Merge Operation
It is common in JavaScript that you want to merge two objects. This is how such a merge operation looks like the following code snippet.
code snippet
1 varmerge = function (target, source) {
2 for (varattrinsource) {
3 if(typeof(target[attr]) === "object" && typeof(source[attr]) === "object") {
4
5 merge(target[attr], source[attr]);
6 } else {
7 target[attr] = source[attr];
8 }
9 }
10 returntarget;
11 };
Working:
The merge operation iterates through the source object and will add whatever property is present in it to the target object.
Let us understand with an example.
1 vartar = {a:1}
2
3 varsou = {a:3, d:2}
4
5 //Merge code snippet
6
7 varmerge = function (target, source) {
8
9 for (varattrinsource) {
10
11 if(typeof(target[attr]) === "object" && typeof(source[attr]) === "object") {
12
13 merge(target[attr], source[attr]);
14
15 } else {
16
17 target[attr] = source[attr];
18
19 }
20
21 }
22
23 returntarget;
24
25 };
26
27 varc = merge (tar, sou);
28
29 console.log(c)
In our example, tar is our target object and sou are our source object, now it will iterate all the properties of the sou object and then merge them with the tar object.
The output will look like
Attack:
Let us take a scenario where a user enters data in the application and the application is storing is an object. Now the application is merging this with other objects.
What can an attacker do?
An attacker can pass this payload to our merge operation, it will completely pollute our object prototypes.
To pollute the prototype, the attacker needs to provide the JSON data that has the  __proto__ property.
1Payload: {b: 2, "\_\_proto\_\_": {"test": "pass"}}
After entering payload code will look like:
1 vartar = {a:1};
2
3 varsou = JSON.parse('{"a": 15, "\_\_proto\_\_": {"test": "pass"}}');
4
5 //Merge code snippet
6
7 varmerge = function (target, source) {
8
9 for (varattrinsource) {
10
11 if(typeof(target[attr]) === "object" && typeof(source[attr]) === "object") {
12
13 merge(target[attr], source[attr]);
14
15 } else {
16
17 target[attr] = source[attr];
18
19 }
20
21 }
22
23 returntarget;
24
25 };
26
27 varc = merge (tar, sou);
28
29 console.log(c)
After entering the payload output will look like this:
Note -> This makes things complicated if the source is supplied by a 3rd party.
- Clone Operation:
Used for cloning an object and based on merge operation.
This is how such a clone operation looks like in the following code snippet.
1 functionclone(a)
2
3 {
4
5 returnmerge ({}, a);
6
7 }
As shown in the code, it is using the merge operation, as seen in the previous section.
The main difference here is that the target object is an empty object.
Attack:
1 varmerge = function (target, source) {
2
3 for (varattrinsource) {
4
5 if(typeof(target[attr]) === "object" && typeof(source[attr]) === "object") {
6
7 merge(target[attr], source[attr]);
8
9 } else {
10
11 target[attr] = source[attr];
12
13 }
14
15 }
16
17 returntarget;
18
19 };
20
21 functionclone(a)
22
23 {
24
25 returnmerge ({}, a);
26
27 }
28
29 vara = JSON.parse('{"a":1,"\_\_proto\_\_": {"test": "pass"}}');
30
31 varb= clone(a);
32
33 console.log(b);
Same attack scenario as shown in merge operation. Here our source object is a.
1 Payload: {"a" : 1 , "\_\_proto\_\_": {"test" : "pass"}}
After entering payload output will looks like:
- Path Assignment Operation
As you have seen in the above image, this is the operation in which we can set the value of any object’s property.
Also, the function setvalue() is user-defined, it can be any function that is performing the same operation.
For demo purposes, I have created a function, which looks like the following code snippet.
1 functionsetvalue(objname, objprop, propvalue)
2
3 {
4
5 objname[objprop] = propvalue;
6
7 }
Here, it is taking three inputs including “object name”, “property name of object”, and “value” and assigning the value to the provided property value.
Let’s understand by an example.
1 functionsetvalue(objname, objprop, propvalue)
2
3 {
4
5 objname[objprop] = propvalue;
6
7 }
8
9 vartest = JSON.parse('{"a": 1, "b" : 2}')
10
11 setvalue(test, "a", 3);
12
13 console.log(test)
Here, we have an object name test, which has two properties a and b with values 1 and 2 respectively.
Here we are calling the function set value, which will change the value of the property from 1 to 3.
Attack:
Let’s consider an application using this kind of functionality, in which it is taking input from the user and directly passing it to the function.
Here an attacker can input “proto “, in the field of object property name and can pollute the prototype.
How does the attack code look like
1 functionsetvalue(objname, objprop, propvalue)
2
3 {
4
5 objname[objprop] = propvalue;
6
7 }
8
9 vartest = JSON.parse('{"a": 1, "b" : 2}')
10
11 setvalue(test, "\_\_proto\_\_", {"test" :"pass"});
12
13 console.log(test)
Affected libraries with this vulnerability
- Lodash
– Just-Extend
Mitigations
- Input validation
- Use a JavaScript library that is using safe merge, clone, extend, or path assignment operations to recursively copy properties from an untrusted source object.
- Consider Using Object. null() which has no prototype.
- You can use a map instead of an object.
- Freez the prototype. 16RackMultipart20220830-1-jokkjp_html_2b3e210472199fe7.png)
Conclusion
You’ve learned what prototype pollution is and how to protect your applications from it. We hope you will apply your new knowledge wisely and make your code safer.
Practice labs
https://github.com/abhiabhi2306/prototype-pollution/
https://github.com/rafax00/PrototypePollution-Lab
https://github.com/Dheerajmadhukar/Prototype-Pollution-Lab_me_dheeraj
https://msrkp.github.io (Only XSS)
References:
- https://www.youtube.com/watch?v=__65_GFERKs
- https://www.acunetix.com/vulnerabilities/web/prototype-pollution/
- https://portswigger.net/daily-swig/prototype-pollution-the-dangerous-and-underrated-vulnerability-impacting-javascript-applications
- https://learn.snyk.io/lessons/prototype-pollution/javascript/
- https://www.whitesourcesoftware.com/resources/blog/prototype-pollution-vulnerabilities/
- https://hackerone.com/reports/788883
- https://hackerone.com/reports/712065
- https://hackerone.com/reports/998398
About Payatu
Payatu is a research-powered, CERT-in empanelled cybersecurity consulting company specializing in security assessments of IoT (Internet of Things) product ecosystem, Web application & Network with a proven track record of securing applications and infrastructure for customers across 20+ countries.
Want to check the security posture of your organization? Browse through Payatu’s Service and get started with the most effective cybersecurity assessments.