Primary tabs

My name is Stefan Michielse,  and for the past ten years I have been working as a penetration tester. This means that I perform penetration tests on business environments to find potential vulnerabilities and misconfiguration. In this blogpost I will share vulnerabilities which I found in the past, and which even nowadays are present in some applications.

In this blogpost I will discuss object prototype pollution whichis a vulnerability related to JavaScript. You will discover why this is possible and what an attacker could do with this kind of vulnerability. Furthermore, I will then focus on finding this type of vulnerability in other libraries, to help answer the question of how common this vulnerability is.

Before we dig deeper into how widespread the problem is, let’s understand what exactly causes prototype pollution. In JavaScript, prototypes define an object’s structure and properties so that the application knows how to deal with the data. However, if you modify the prototype, it can affect how the objects work throughout the entire application.

For an example see the screenshot below :

object1

If an attacker changes an existing attribute to an unexpected return type (say toString attribute to return type integer) it can cause the application to crash (Denial of Service). In the example above an attacker could become an administrator by polluting the [object].prototype.isAdmin to set equal true.

Types of attack

Short description

Denial of Service (DoS)

This is the most common attack. DoS occurs when Object holds generic function that are implicitly called for various operations (for example toString and ValueOf). The attacker pollutes Object.prototype.someAttr and alters its state such as Int or Object. In this case, the code fails and is likely to cause a denial of service. For example: if an attacker pollutes Object.prototype.toString by defining it as an integer, if the codebase at any point was reliant on someObject.toString() it would fail.

Remote Code Execution

Remote code execution is generally only possible in cases where the codebase evaluates a specific attribute of an object, and then executes that evaluation. For example: eval(someobject.someAttr). In this case, if the attacker pollutes Object.prototype.someAttr they are likely to be able to leverage this in order to execute code on the server.

Property Injection

The attacker pollutes properties that the codebase relies on for their informative value, including security properties such as cookies or tokens. For example: if a codebase checks privileges for someuser.isAdmin, then when the attacker pollutes Object.prototype.isAdmin and sets it to equal true, they can then achieve admin privileges

 

Environment Setup

The following was installed on my local machine to research this topic:

  1. Docker                                              https://docs.docker.com/docker-for-windows/install/
  2. NodeJS                                             https://nodejs.org/en/download/
  3. Some libraries                                 npm install <library>

Once docker is installed, execute the following command as shown in the screenshot below:

docker pull ubuntu

d1

Once the docker images are downloaded from the internet, you are now able to login to the docker container and install nodejs.

d2

Next  execute the following commands:

apt-get update

d3

 apt-get install nodejs npm

d4

 

Introduction to JavaScript

Prototype pollution is a term that was coined many years ago in the JavaScript community to designate libraries that added extension methods to the prototype of base object like “Object”, “String” or “Function”. This was very rapidly considered a bad practice as it introduced unexpected behavior in applications. In this blogpost, we will analyze the problem of prototype pollution from a different angle. What if an attacker could pollute the prototype of the base object with their own value? What APIs allow such pollution?

Deep dive into JavaScript

For those that have never done a deep dive into the inner workings of JavaScript, the rest of this blogpost may be hard to fully understand. So a brief introduction of how “prototype” work and a few other quirks of JavaScript are needed before starting.

What is an object?

Let’s start with the simplest way to create an object:

d5

While we haven’t declared any property for that object, it’s not empty. In fact we can see that multiple property return something (ex.: obj.__proto__, obj.constructor, obj.toString, etc.). So where are those properties coming from? To understand this part we need to look at how classes exists within the JavaScript language.

The concept of a class in JavaScript starts with a function. The function itself serves as the constructor of the class.

d6

Function available on all the instances of “MyClass” are declared on the prototype. What’s worth pointing out here is that during this declaration, the prototype is modified at runtime. By default this means that the program can at any point in time add, change, or delete entry in the prototype of a class. See screenshot above.

If we come back to our first example of the empty object, we can say that the empty object we declared is in fact an object which has the constructor “Object” and the properties, like “toString”, are defined on the prototype of “Object”. The full list of values which come default on an object can be found in the MDN documentation: https://developer.mozilla.org/nl/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype

 

Property Access

It is important to note that in JavaScript there is no distinction between a property and an instance function. An instance function is a property for which it’s type is a function. So instance functions and regular properties are accessed in the exact same way. There are two notations to access property in JavaScript: the dot notation (ex: obj.a) and the square bracket notation (ex: obj[“a”]). The second one is mostly used when the index is dynamic.

d7
d8

 

Magic Property

There’s a good amount of property that exists by default on the Object prototype. We will explore two of them : “constructor” and “__proto__”.

“constructor” is a magic property that returns the function used to create the object. What’s good to note is that on every constructor there is the property “prototype” which points to the prototype of the class.

d9

 “__proto__” is a magic property that returns the “prototype” of the class of the object. While this property is not standard in the JavaScript language, it’s fully supported in the NodeJS environment. Interesting about this property is that it’s implemented as a getter/setter property which invokes getPrototypeOf/setPrototypeOf on read and write respectively. Assigning a new value to the property “__proto__” doesn’t shadow the inherited value defined on the prototype. The only shadow it involves is using “Object.defineProperty” (or the more modern, Reflect.defineProperty). See the screenshot below.

d10

 

Identifying vulnerable library

General concept

The general idea behind prototype pollution start with the fact the attacker has control over at least the parameter “a” and “value” of any expression of the following form:

obj[a][b] = value

The attacker can set “a” to “__proto__” and the property with the name defined by “b” will be defined on all existing objects ( of the class “obj”) of the application with the value “value”. The same thing can append with the following form when the attacker has at least control of “a”, “b” and “value”.

obj[a][b][c] = value

The attacker can set “a” to “constructor, “b” to “prototype” and the property with the name defined by “c” will be defined on all existing objects of the application with the value “value”. However since this requires more complex object assignment, the first form is easier to work with.

While it’s pretty rare that you stumble on code that looks textually like the examples provided, some manipulation can provide the attacker with similar control. This will be explored in the next section.

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”)

Manipulation susceptible to prototype pollution

There are three types of API that were identified in this blogpost that can result in “prototype” pollution. While not all the implementation of those types of API’s are available on the NPM registry are affected, at least one was identified.

  • Object recursive merge
  • Property definition by path
  • Object clone

Object recursive merge

The logic of a vulnerable recursive merge function is at a high level something that looks like the following pseudo-code.

merge(target, source )

       for each property of source

             if property exists and is an object on both the target and source

                    merge(target[property], source[property])

             else

target[property] = source[property]

When the source object contains a property named “__proto__” defined with Object.defineProperty(), the condition that checks if “property exists and is an object on both target and the source” will pass and the merge will recurs with the target being the prototype of “Object” and the source an “Object” defined by the attacker. Properties will then be copied on the prototype of “Object”.

Below a screenshot of the merge function.

d11

 This could be exploited by parameter pollution, as shown in the screenshot below:

d12

 The variable d is a new object, and as can be seen the object is polluted by the prototype pollution exploit. This not only works on newly created object, this works also for the previously created objects.

 

Property definition by path

A few library offers API to define property value of an object based on a supplied path. This path is often defined with a dot notation. It’s for most part meant to be simplified value assignation on complex object. The function affected generally had the following signature:

theFunction(object, path, value)

If the attacker can control the value of “path”, she/he can set this value to “__proto__.myValue”. “myValue” will then be assigned to the prototype of the class of the object.

d13
d14

Shown in the example below it possible to execute actual JavaScript on the server, here I first print the current working directory, change it by polluting the object. When we execute again the current working directory we see it is change to home, as specified by the previous command.  

d16

 

Object clone

Prototype pollution can happen with API’s that clone object when the API implements the clone as recursive merge on an empty object. Note that merge function must be affected by the issue.

Function clone(obj) {

               return merge({}, obj);

}

Scanning for vulnerable API

Doing manual code reviews on all the NPM library is time consuming and static code analysis is very hard to use to identify such issues in libraries. However since vulnerable API will have an identifiable side-effect, a dynamic approach was used to identify a large amount of affected libraries. While this approach won’t identify all the affected libraries, it was able to identify a large amount of libraries with very minimal coding and CPU time.

The approach can be defined at a high level with the following step:

  1. Install the library to be tested with “npm”
  2. In JavaScript
    1. “require” the library by its name
    2. Recursively list all the function available
    3. For each identified function
      1. Call the function with a signature that would pollute the prototype of “object” if the implementation would be vulnerable.
      2. Once the call is done, check if the side-effect occurred. If it did, we can mark the function as affected and clean the side-effect.

The source code for the bash script which was used is :

d17

 The source code for the find-vuln.js is:

d18
d19
d20
d22
d23
d24

Is executed in the directory where the above script is executed: 

d26

So I check with a bash for loop all libraries which I installed, some of them are dependencies of libraries.  

d27

 The output of the above program for some libraries which were installed. The following vulnerable libraries where found during the research of prototype pollution:

d28
d29

 

List of confirmed (and fixed) libraries that suffered prototype pollutions

Prototype pollutions show up in large libraries every so often, and these are often reported through CVE (Common Vulnerabilities and Exposures) entries. Here I will list a couple of CVE entries that concerned Prototype pollutions. For the full list see https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=prototype+pollution.

Date reported (YYYY-MM-DD)

NPM Package Name

CVE ID

2020-01-28

lodash

CVE-2020-8203

2020-01-28

dot-prop

CVE-2020-8116

How to prevent

  1. Freeze the prototype – use Object.freeze(Object.prototype)
  2. Require schema validation of JSON input.
  3. Avoid using unsafe recursive merge functions
  4. Consider using object without prototype(for example, Object.create(null), breaking the prototype chain and preventing pollution.
  5. Some cases  Map can be used instead of Object. Please note however that Maps cannot always replace Objects due to how they work. Always be sure to use the right tool for the job.  For more information: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map#Objects_vs._Maps

 

Over de auteur

cybersecuritygame

CGI Security Awareness Game

Met de Security Awareness Game van CGI kunnen DevOps teams hun kennis van security op een leuke en interactieve manier vergroten. De web-based ‘tower defense’ game motiveert spelers nieuwe kennis te vergaren en om security daadwerkelijk een onderdeel te laten zijn van de dagelijkse werkzaamheden. ...

Voeg commentaar toe

Comment editor

  • No HTML tags allowed.
  • Lines and paragraphs break automatically.
Blog richtlijnen en gebruiksvoorwaarden