RCE Leveraging Server-Side Prototype Pollution
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
- Sanitize property keys to block injection of risky keys like
__proto__
. - Secure objects from modifications by employing
Object.freeze()
. - Opt for safer alternatives such as
Map
orSet
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.