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.

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.


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.

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.
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.
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 objectdata.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!

Last updated