Some well-known NPM packages, including ember.js, xmldom, loader-utlis and deep-object-diff, have recently been found to have a few prototype pollution vulnerabilities. I made the decision to investigate these vulnerabilities in order to see if there were any trends that we could identify and steer clear.
All of the NPM packages are open source, which allows us to evaluate where vulnerabilities were introduced and how they are remediated by reviewing the fixes.
A brief Overview of Prototype Pollution Vulnerability
We may need to examine some fundamental concepts about JavaScript Prototype and how Prototype pollution vulnerabilities are introduced and how they could be exploited before we dig in to see if there are any common patterns in these vulnerable libraries.
What is Prototype in JavaScript
Every object in JavaScript has a built-in property, which is called its prototype. The prototype is itself an object, so the prototype will have its own prototype, making what’s called a prototype chain. The chain ends when we reach a prototype that has null for its own prototype.
The following code snippet defined an Object called Student, the prototype of the Student is Object and it has many predefined properties
One power of the Prototype is that it allows an object to inherit properties/attributes from its prototype. Under this case, Student object could inherit and use all the predefined properties of its prototype Object.
Key takeaway 1: an object could access the properties/attribute of its prototype due to the inheritance of Prototype. Under this example, the toString() function is defined by Student prototype Object and it could be accessed by any Student object.
Key takeaway 2: An object could have many instance, they could shared the same Prototype properties
Key takeaway 3: Meanwhile, JavaScript allows some Object attributes to be changed during runtime, that includes the prototype property; though overwriting the prototype of a default object is considered as a bad practice.
How does prototype pollution occur and how to exploit them
As the term “prototype pollution” suggests, it happens when a hostile attacker has the ability to manipulate and alter an object’s prototype. Once an attacker could modify the object’s prototype, all the instances that share the object prototype properties would be affected
Let us tweak the aforementioned code by modifying the toString() properties of the Object prototype to a customized function in order to clarify the explanation.
When running the above code under the browser console, the studObj.toString() statement will be executed using the new toString() function once we update the toString() function of its Object prototype, and the same will be true for the newly generated object {} since they both share the same Object Prototype.
If you look at the payload we used in the example above, you’ll see that it has three parts
Part 1: studObj.__proto__.__proto__.
This is intended to obtain the target Object.Prototype, you may see many more .__proto__. in the actual payloads if the prototype chain is very long.
Part 2: .toString
Using this section, we may tell which function we wish to change or add to the Object.Prototype. In this example, we want to override the toString() function defined in the Object.Prototype
Part 3: = function() {return “I am changed”}
This part is used to set up the new value for the prototype property.
In a nutshell, access to the Object.Prototype and the ability to modify or add its properties are required for a successful exploit. You may start to think how any app would allow these conditions to be met. In the next section, We will examine the four most current Prototype vulnerabilities to 1) see how the property pollution vulnerabilities are introduced into the codes and 2) see what sort of mitigation methods are employed to fix the prototype pollution.
Case studies for Prototype Pollution Vulnerability & Remediation
Case 1: Prototype Pollution Vulnerability in Ember.js < 4.4.3 (PR)
Root Cause
Ember.js provides two functions EmberObject.setProperties or EmberObject.set to set properties of an object.
As there is no validation on the untrustPath variable, if an attacker defines the path to __proto__.__proto__.srcdoc, it would modify the property of the fundamental Object, which will affect all the objects inherited from it.
Mitigation
By referring to the remediation PR, the mitigation method is to forbid specific keyword __proto__ and constructor to block a prototype chain access.
Case 2: Potential Prototype Pollution vulnerability in xmldom < 0.7.7
Root Cause
The potential prototype pollution vulnerability (CVE-2022-37616) is caused when this library provides the following function to copy one DOM element to another. (I marked it as potential because a valid POC has not been provided and this copy is NOT performing a deep clone).
Mitigation
The hasOwnProperty() method is used in this mitigation procedure (PR) to determine whether the src object has the requested property as its own property (as opposed to inheriting the Prototype property). The copy function will fail if the src attribute is inherited from its prototype to prevent a malicious user to access the Prototype Property
Case 3: Prototype Pollution in webpack loader-utils < 2.0.3
Root Cause
This potential vulnerability (CVE-2022-37601) was caused by the queryParse() function when parsing the query parameter and composing a new Object called result with the query parameter name and value.
Mitigation
The mitigation method is very straightforward by using Object.create(null) on objects created as Object.create(null) does NOT inherit from Object.prototype. It means the created Object is not able to access the Prototype Property
Case 4: Prototype Pollution in deep-object-diff
Root Cause
This weakness was introduced when two items were compared deeply and the difference between the two objects was returned as a new object. Due to the possibility that the difference contains prototype property and is under the attacker’s control. It enables the prototyping of pollution.
Mitigation
Similar to Case 3, the mitigation method is using Object.create(null) to create an object where Prototype property could not be inherited.
When going through all the reported vulnerabilities, cit seems these vulnerabilities are likely to be introduced when following operation occurs
- Path assignment to set the property of an Object. (Case 1, Case 3)
- Clone/Copy an object (Case 2, Case 4)
The common mitigation method includes
- Create objects without prototypes inheritance Object.create(null) (case 3,case 4)
- Use hasOwnProperty() function to check whether a property is on your actual object or inherited via the prototype (Case 2)
- Validate the user input with specific keyword filtering (Case 1)
Prototype Vulnerabilities, a much wider security issue
After reviewing several real-world examples of how prototype vulnerabilities could be developed and how to exploit them, I continued by examining some NPM open source libraries to determine whether prototype pollution is a widely ignored issue in the NPM open source libraries.
Without spending too much effort, I discovered and verified that two open source libraries used for merging objects are vulnerable to Prototype vulnerability by scraping the source code under Github using specific patterns listed below.
These two libraries appear to be rather dated though there are still hundreds of downloads every week, however I have contacted the maintainer but have not yet received a response.
All of these instances, in my opinion, are really the tip of the iceberg in terms of how JavaScript libraries are vulnerable to prototype pollution.
I see two possible explanations for why this vulnerability is so prevalent: 1) Given that this vulnerability is very recent, few developers appear to be completely aware of it. 2) At the moment, automation methods to find this kind of vulnerability are not very mature.