RCE Leveraging Server-Side Prototype Pollution

Ams._.Ghimire
5 min readFeb 15, 2024

--

INTRODUCTION TO PROTOTYPE

In JavaScript, a prototype is an object linked to another object, providing a basis for inheritance. Each object is automatically assigned a built-in prototype, from which objects inherit properties and methods unless overridden.

For instance, let’s examine a code snippet. Here, a string ams is assigned to a variable named name. Generally, we can call the toUpperCase() method on the variable, even if it is not explicitly added to the string ams. This is possible because in JavaScript, a parent prototype, specifically in this case String.prototype, is endowed with such functions or methods. Thus, we can easily capitalize ams, showcasing the power of prototype.

This method is inherited automatically, and if a property or method is not found in name, JavaScript looks for it in Object.prototype. This chaining mechanism is called prototype chaining, and it allows for the efficient reuse and extension of properties and methods throughout the inheritance chain.

DECLARING & UNDERSTANDING PROTOTYPES

Here, an object named ams is created with various attributes. Now, let’s explore how we can explicitly declare a prototype attribute for the ams object. Similarly, let’s see how declaring a prototype for the ams object adds the attribute to the prototype chain, allowing every object to access the attribute unless a similar attribute is already present in an object. In JavaScript, we can explicitly declare a prototype attribute for any object using the __proto__ property.

Here, declaring an isAdmin attribute for prototype of ams object has added the attribute to the prototype chain, allowing every object to access the attribute from the runtime, so the newly created object trainee can also fetch the respective attribute from the prototype chain.

PROTOTYPE POLLUTION

Prototype pollution vulnerabilities commonly occur when a JavaScript function recursively combines an object with properties controlled by the user into an already existing object. Let us see how we can detect server-side prototype pollution vulnerabilities.

SERVER-SIDE PROTOTYPE POLUTION

Server-side prototype pollution occurs in server-side JavaScript environments when malicious user’s function reclusively merges to the user accessible objects. This is able to manipulate the prototype of objects used by the server. Let us now see how we can detect a prototype pollution and exploit it. The findings and methodology are based on PortSwigger labs.

1. Detecting the sink or source for prototype pollution.

Browsing the application, we could see a form in the My account section where we could submit the billing address, lets press the submit button and inspect the request that is sent from the application.

A POST request was sent with the body containing the billing address data inputs of the form. In the response we could see the reflection of those objects. Let’s try to add a prototype attribute in this JSON data.­­

Here, a new attribute leveraging the __proto__ property was added to the Javascript object. Now this is globally accessible attribute for that specific runtime. Hence, the source for prototype pollution is detected.

2. Exploitation (Remote Code Execution)

Now that we have confirmed the vulnerability, let’s exploit it to perform Remote Code Execution on the backend server. Let’s craft an object that can call a process to execute a shell command in the node server. Let’s breakdown our payload:

This attempts to exploit prototype pollution by injecting a property named __proto__ into an object. This property contains an array named execArgv, which in turn contains a string specifying a command-line argument for Node.js. A new child process is created and execSync() function is called, which in this case runs a curl command making a request to a specified URL. The -d flag is used to send data over the request.

Let’s send the payload, adjusting it within the json data.

On the application when Run Maintenance jobs is clicked, the POST request is sent to /admin endpoint.

This executed the command successfully. Let’s view the request that our URL has received, which contains the output of ls command.

Now, let’s send cat command to read the file morale.txt.

Let’s now see the cURL request we have received.

This is the content of the file. So, an improperly handled prototype allowed object can be chained to perform RCE in the backend javascript runtime.

REMEDIES

  1. Sanitize property keys to block injection of risky keys like __proto__.
  2. Secure objects from modifications by employing Object.freeze().
  3. Opt for safer alternatives such as Map or Set objects.

This blog was my take on prototype pollution and its potential impact to leverage RCE — a cool bug but a dangerous one, especially when it sneaks into the server side.

--

--

No responses yet