|5 MIN READ

JavaScript Prototype Pollution: Detection, Exploitation Techniques & Real CVE Examples

A practical guide to prototype pollution: how pollution sources reach gadgets, how to test with ?__proto__[key]=value and constructor.prototype, and how real-world CVEs have been exploited in the wild.

JavaScript Prototype Pollution: Detection, Exploitation Techniques & Real CVE Examples

We invite you to follow along by using your Browser's Console to execute JavaScript code. Just open your favorite browser, press F12, and go to the 'Console' tab. Happy coding!

First of all, what is a JavaScript Object?

JavaScript objects are used to store various key:value pairs. They can be seen as collections of properties, where each property is an association between a key and a value. These values can be a function, an array, another object, or any other data type available in JavaScript, including numbers, strings, and booleans.

What is surprising is that all of the data structures are Objectsunder the hood, even functions!

What is a Prototype in JavaScript?

Let's use a student and teacher analogy.

Imagine a Classroom

  1. Student (Object): knows some things, may not know others.
  2. Teacher (Prototype): knows a lot of things and can teach students.

Example

If you ask the student to solve a multiplication problem:

  1. The student first tries to solve it on their own.
  2. If they can't, they ask the teacher.
  3. The teacher explains how to do multiplication.
  4. Now, the student can solve multiplication problems too.

Objects (students) like strings, functions, arrays are linked to other objects (teachers) through prototypes.

For example, all strings are assigned the String.prototype, where they inherit the toUpperCase() method. As a result, all strings automatically have a ready-to-use method for converting them to uppercase, amongst others.

How Prototype Inheritance works

In programming terms, when we reference a property of an object (asking the student to solve a multiplication problem), JavaScript tries to access it directly first (step 1 of our analogy). But if said object doesn't have a matching property to return, then it will start to look (ask the teacher) for it on the object's prototype instead.

That's why we could call toUpperCase function on input variable without defining it first.

The Prototype Chain 😱

  • Everything is an Object in JavaScript
  • Every Object gets assigned a Prototype from where it inherits properties and methods
  • Then... every Prototype is an Object
  • Therefore... every Prototype has a Prototype assigned to it. And so on

This madness is called "The Prototype Chain" and it ultimately leads back to the top-level Object.prototype, whose prototype is simply null. (Don't look at us like that, alright? It's not our fault.)

Most importantly, objects inherit properties and methods from all the objects' prototypes above them in the chain. This means that our input object has access to the properties and methods of both String.prototype and Object.prototype.

Modifying Prototypes

You can access an object's prototype by using its __proto__ property. And you can use it to read or reassign any prototype's properties. (You can also play around the prototype chain)

Understanding Client Prototype Pollution Vulnerabilities

Prototype Pollution vulnerabilities arise when a JavaScript function recursively merges an object containing user-controllable data into an existing object, without first sanitizing the keys. This may allow a bad actor to inject the __proto__ key along with arbitrary nested properties.

Then, the merge operation may assign these nested properties to the object's prototype instead of the target object itself, allowing the bad actor to pollute prototypes with harmful values.

For successful exploitation of Prototype Pollution you require three key components:

  • A prototype pollution source --> This is any input that enables you to poison prototype objects with arbitrary properties.
  • An exploitable gadget -->This is any property that is passed into a sink without proper filtering or sanitization.
  • A sink --> A JavaScript function or DOM element that enables arbitrary code execution.

But don't worry about those for now, we'll learn more about them in a future blog post. Stay tuned for more experienced exploits!

Exploiting Client Prototype Pollution Vulnerabilities

On your web application add the ?__proto__[vulnerable]=true parameter as follows:

1https://website.com/?__proto__[vulnerable]=true

In this case, the JS engine will treat __proto__ as a getter for the object's prototype and assign the vulnerable key to it. Assuming that the target object uses the default Object.prototype, all objects in the JavaScript runtime will now inherit vulnerable, unless they already have a property of their own with a matching key.

In our analogy terms, we showed the teacher the vulnerable payload, so all the students will now learn the same. Making the whole classroom vulnerable.

In practice, injecting a property called vulnerable is unlikely to have any effect on the web app. However, a bad actor can use the same technique to pollute the prototype with properties that are used by the application, or any imported libraries.

💡

But for you, our beginner friend, this is enough to start playing around checking which applications you can pollute. After sending the URL parameter, open the Browser's Console and create a new object. Then check if that new object of yours has the vulnerable property into it (or any other property name you tested).

Other useful tips

  • If the property was not added to the prototype, try switching to dot notation rather than bracket notation, or vice versa: https://website.com/?__proto__.vulnerable=true
  • If neither of these techniques is successful, you may still be able to pollute the prototype via its constructor. We'll cover this in a future blog post, but have in mind that myObject.constructor.prototype is equivalent to myObject.__proto__.
  • So https://website.com/?constructor.prototype.vulnerable=true or https://website.com/?constructor[prototype][vulnerable]=true provide an alternative vector for Prototype Pollution.

Real-world CVE examples

Prototype pollution vulnerabilities have been found in widely-used JavaScript libraries. Here are three notable examples:

  • CVE-2019-10744 — Lodash (_.merge, _.defaultsDeep): The merge and defaultsDeep functions in Lodash versions before 4.17.12 were vulnerable to prototype pollution via crafted __proto__ properties. Lodash is one of the most downloaded npm packages, making this a high-impact supply chain vulnerability. Fixed in 4.17.12.
  • CVE-2019-7609 — Kibana (arbitrary code execution): A prototype pollution flaw in Kibana allowed an attacker to execute arbitrary code via the Timelion visualizer. This demonstrated how a "harmless" pollution of Object.prototype can reach a code execution gadget deep in the application stack — a real-world chain of the type described in this article.
  • CVE-2020-8203 — Lodash _.zipObjectDeep: A second Lodash pollution vector via zipObjectDeep, again patched in 4.17.16. Illustrates that even a well-maintained library can have multiple pollution entry points.

Testing checklist

Use this as a quick reference when testing for prototype pollution:

TechniquePayload
Bracket notation via query param?__proto__[key]=value
Dot notation via query param?__proto__.key=value
Constructor chain?constructor.prototype.key=value
Constructor bracket?constructor[prototype][key]=value
JSON body (POST){"__proto__": {"key": "value"}}
Nested merge payload{"constructor": {"prototype": {"key": "value"}}}

After injecting, verify in browser console:

1// If pollution succeeded, a new empty object will have your injected key 2let obj = {}; 3console.log(obj.key); // Should return "value" if polluted

Related reading: for the same class of DOM manipulation issues, see our CORS vulnerabilities guide and Hacking Swagger UI XSS post.

Conclusion

We have shared some insights about Prototype Pollution vulnerabilities so you can start looking for them in the wild. That way, we are part of your Cyber Security journey. We would love to see you succeed.

Share:

Ready to secure your application?

Vidoc finds and fixes vulnerabilities in real-time.
Ship secure applications faster.

Try VIDOC

More articles

Explore insights, trends, and tips to stay ahead in cybersecurity.