Merger: Node.js Prototype Pollution

Platform: UTCTF 2024

Category/Tags: Web, Prototype Pollution, Command Injection

Difficulty: N/A (I guess Medium)

Introduction

Tired of getting your corporate mergers blocked by the FTC? Good news! Just give us your corporate information and let our unpaid interns do the work!

Information Gathering

This is a simple web application that can create company details and merge original details into a new one.

landing page

I downloaded a zip file containing the source code, and I saw the package.json file. It uses Express.js for the server-side, as expected. No vulnerabilities were found in these packages.

This is the app.js file responsible for running the web application, and it contains some API endpoints to process inputs from the user.

/api/makeCompany endpoint simply creates the key-value pair attributes and returns an id (cid) for the company. /api/getAll endpoint is the one being called after creating a company to display all companies that the user created. /api/absorbCompany/:cid endpoint is for merging the two company details into one new object based on their cid.

/api/makeCompany
/api/absorbCompany/:cid

And I tried to view the response of /api/absorbCompany/:cid in BurpSuite. It has a cmd key that has the value of a shell file; this means that after merging company details, it will run the shell file. Moreover, I saw that when I run a command, I receive stdout and stderr results.

BurpSuite response

Under the /api/absorbCompany/:cid, I saw that it forks the merger.js script, creating and executing an independent process.

This is the merger.js file, responsible for merging the original company details to create a new object using a loop. Also, it has an exec function that is used to run shell commands within the environment.

Vulnerability

Under merger.js, this line of code is vulnerable to command injection because it can execute commands.

Command injection is an attack in which the goal is execution of arbitrary commands on the host operating system via a vulnerable application. From OWASP.

The problem here is I cannot change the value of cmd to a different command.

But if we look at the if statement, I can execute different commands other than ./merger.sh. I should modify the secret object and assign the command to its cmd property. BUT HOW???

I remember when I was studying web development that almost everything in JavaScript is object. So, from this idea, I guess I can modify the secret object.

Exploit

With the use of prototype pollution attack, I can now add a property and value to the object.

Prototype pollution is a JavaScript vulnerability that enables an attacker to add arbitrary properties to global object prototypes, which may then be inherited by user-defined objects. From PortSwigger.

To give you a quick overview of what I know about prototypes in JavaScript, here it is. I created a Car class that has the property of color, and I created an instance of Car class and setting the color to "blue". As expected, the color of my_car is blue.

But what happens if I use __proto__ in my instance and set a new property?

By setting my_car.__proto__.isHacked = true, you are adding a property isHacked to the prototype object of the Car class, and when I log the my_second_car.color, my_second_car.isHacked is yellow true. All instances of Car class will now have isHacked property.

Now, back to the problem, I can now modify the secret object and add cmd property and a value. First, we need to craft a body to create a new company. Set the attributes value to object name and values to __proto__, send it to /api/makeCompany.

This is ready to merge. In the /api/absorbCompany/:cid, I need to send another body. Under merger.js, I need the loop wherein it iterates and creates a new object. To pass the first condition;

  • orig[first key] is not undefined -> __proto__

  • orig[first key] is object -> __proto__ is an object

  • data.values[0] is object -> [{"cmd": "id"}] and I need to send this as array of objects

So, the body will be,

Now, I can inject commands!

successful command injection

Last updated