The Complete Guide to Prototype Pollution Vulnerabilities
Table of Contents
Prototype Pollution is one of the less known vulnerabilities in the security community. Researchers started to discuss it as a potential attack vector around 2017, and the first vulnerabilities were found in the wild at the start of 2018.
In this article, we’re going to take a deep dive into what Prototype Pollution vulnerabilities are, and how they can be mitigated.
Prototype pollution: How it started
It all started in 2015, when the ECMA (European Computer Manufacturers Association) released a new specification — ECMAScript 2015, 6th Edition, which provided the JavaScript standard, to ensure compatibility between web pages across different web browsers. The spec included the standardization of the proto feature.
The _proto_ feature
Before explaining what proto is, let’s quickly review some basic definitions in JavaScript:
Object can be viewed as a key-value pair, with the key being a string, and the value can be anything (similar to “map” or “dictionary” in other languages). Everything that you type in JavaScript (except from primitives) is an Object.
Prototype is an attribute related to Object, it is used as a mechanism that enables JavaScript Objects to inherit features from one to another. Since almost everything in JavaScript is an Object, Prototype is an Object too.
An Objects Prototype may also have a Prototype, and from it, it can inherit his Prototype or other attributes, and so on. This is referred to as a prototype chain.
There are three main points we need to know about proto:
- it is a special attribute that refers to all the Prototype of an object
- all Objects have _proto_ as their attribute (Prototype)
- _proto_ is also an Objects
Clearly, proto was meant to be a feature, to support processes like inheritance of all attributes from a JavaScript class. But eventually the logic of “everything is anything and anything is everything” had a weakness in it, or to be more precise – a vulnerability.
When do Prototype pollution vulnerabilities happen?
Prototype Pollution occurs when an attacker manipulates proto , usually by adding a new Prototype onto proto . Since proto exists for every Object, and every Objects inherits the Prototypes from their Prototype, this addition is inherited by all the JavaScript Objects through the prototype chain. Malicious players could take advantage of the ability to insert properties into existing JavaScript code, and execute either Denial of Service attacks, by triggering JavaScript exceptions, or Remote Code Execution, by injecting malicious code.
The animation shows the Objects with their Prototypes, then a new Prototype (Prototype_#4) is added on the proto of another Object (Object_#3). This causes all of the Objects, including their prototypes (as they are objects too), to inherit the new Prototype – Prototype_#4. The result: the application crashes.
Example of a Prototype pollution vulnerability
Let’s take a look at one of the Prototype Pollution vulnerabilities detected by the Mend.io security research team.
CVE-2020-28282
npm package “getobject” is a library which assists in the process of getting and setting Object’s easily. The package has more than 600,000 weekly downloads and multiple packages dependent on it. Since proto is a feature in JavaScript developers of libraries like “getobject” don’t have to use it to be vulnerable. Attackers only need to have a flow that can parse proto without any sanitation or mitigation, then exploit it and execute a Prototype Pollution attack.
Reviewing the POC code:
set()function in the “getobject” package intends to assign properties to an Object, it accepts three arguments Obj, parts and value. Because there is no validation, the values passed into parts and value arguments can be manipulated by an attacker that can supply a malicious value by adjusting parts to includeproto. Next, check will be assigned toproto with the value “polluted”, polluting all the Objects in our application.
In the output, we can see that before the pollution occurs, obj2 .check was undefined, then, when running the set() function with our arguments(obj, “proto.check”, “polluted”), the pollution occurs. Now obj2 .check exists, and its value is “polluted”. By assigning a new “unknown” Prototype to all the Object in the application we are causing a Denial of Service for the whole application.
The rise of Prototype pollution vulnerabilities
Now that we know where Prototype Pollution came from and how Prototype Pollution vulnerabilities work, let’s take a look at the data.
Our research team dove into the Mend open source vulnerabilities database to learn how the community has been addressing Prototype Pollution vulnerabilities over the past few years.
In the graph above we see how many Prototype Pollution vulnerabilities have been published each year since the security community became aware of them. Looking back, there is a huge upward trend in 2019 and especially in 2020. It’s still early to say how many Prototype Pollution vulnerabilities will be published in 2021, currently it seems that maybe the upward trend will stabilize.
The pie chart above displays Mend’s contribution to the security effort of detecting Prototype Pollution vulnerabilities. WS IDs are vulnerabilities that were published on the npm advisory and haven’t been published on the NVD. “CVE by Mend” refers to vulnerabilities published on the NVD after being detected and reported by our security research team since Mend became a CNA (CVE numbering authority).
As you can see, many Prototype Pollution vulnerabilities detected in open source components are reported within the community, but are yet to be added to the NVD. This is another example of how open source vulnerabilities can often be found outside of the NVD. While the open source community works hard to detect, fix, and report on security issues, due to the decentralized nature of the community, issues are often published on community issue trackers and advisories. That’s one of the reasons Mend joined the CNA community, to help support the open source community and ensure that security issues are published across all platforms.
_proto_: Vulnerability or feature?
proto might have been a useful feature in the past, but, since it was standardized to ensure compatibility with web browsers, the feature is no longer recommended by most in the community. It’s yet to be determined whether it will be removed or kept for compatibility purposes. The popular suggestion is to avoid using this functionality — MDN goes as far as referring to it as deprecated and recommends using Object.getPrototypeof. Considering today’s best practices, it’s hard to say that proto is anything other than a vulnerability.
This situation leaves maintainers and developers with the peculiar added task of securing their code from a feature of the coding language they are using.
Mitigating Prototype pollution vulnerabilities
There are a number of ways to mitigate Prototype Pollution vulnerabilities:
– Object.freeze will mitigate almost all cases. Freezing an Object prevents new Prototypes from being added to it.
– Using schema validation to ensure that the JSON data contains the expected attributes. This will remove proto if it appears in the JSON.
– Using map primitive, which was introduced in the EcmaScript 6 standard, and is now well-supported in the NodeJS environment.
– Objects created using the Object.create(null) function won’t have the proto attribute.
– In general, pay attention when using recursive merge functions, since they are more prone to Prototype Pollution vulnerabilities than other functions.
Will Prototype pollution vulnerabilities ever be extinct?
It’s safe to say that new Prototype Pollution vulnerabilities will continue to be detected in the years to come. As we mentioned, it’s still unclear if or when proto might be removed in the future. Until then, we as a community can do our best to develop our code with this security vulnerability in mind, and mitigate the cases that already exist.