Attacking Node.js through Manipulating JavaScript Object Prototypes

Researchers at the Helmholtz Center for Information Security (CISPA) and the Royal Institute of Technology (Sweden) analyzed the applicability of the JavaScript prototype object contamination technique (“prototype pollution”) to create attacks on the Node.js platform and popular applications based on it, leading to code execution.

The prototype polluting method uses a feature of the JavaScript language that allows you to add new properties to the root prototype of any object. In applications, there may be code blocks (gadgets) whose work is affected by a substituted property, for example, in the code there may be a construction like 'const cmd = options.cmd || "/bin/sh"', the logic of which will be changed if the attacker manages to substitute the "cmd" property in the root prototype.

A successful attack requires that the application be able to use input from outside to create a new property in the object's root prototype, and that a gadget that depends on the changed property is encountered during execution. Changing the prototype is done by Node.js handling the "__proto__" and "constructor" utility properties. The "__proto__" property returns the object's class prototype, and the "constructor" property returns the function used to create the object.

If the application code encounters the assignment "obj[a][b] = value" and the values ​​are set from external data, the attacker can set "a" to the value "__proto__" and achieve the setting of his property with the name "b" and the value "value" in the root prototype of the object (obj.__proto__.b = value;), while the property set in the prototype will be visible in all objects. Similarly, if in the code there are expressions like "obj[a][b][c] = value", by setting "a" to the value "constructor", and "b" to "prototype" in all existing objects, you can define a new property with the name "c" and the value "value".

An example of changing the prototype: const o1 = {}; const o2 = new Object(); o1.__proto__.x = 42; // create the "x" property in the root prototype console.log(o2.x); // refer to the property "x" from another object // we get 42 at the output, since the root prototype was changed through the o1 object, which is also used in the o2 object

Vulnerable code example: function entryPoint (arg1, arg2, arg3){ const obj = {}; const p = obj[arg1]; p[arg2] = arg3; return p; }

If the arguments of the entryPoint function are formed from the input data, then the attacker can pass the value "__proto__" to arg1 and create a property with any name in the root prototype. If you pass the value "toString" to arg2 and 3 to arg1, you can define the "toString" property (Object.prototype.toString=1) and cause the application to crash when the toString() function is called.

As an example of situations that can lead to the execution of attacker code, the creation of the "main", "shell", "exports", "contextExtensions" and "env" properties is given. For example, an attacker can create a "main" property in the object's root prototype, writing the path to his script into it (Object.prototype.main = "./../../pwned.js") and this property will be called at the time of execution in the code of the require("my-package") construct, if the included package does not explicitly define the property "main" in package.json (if the property is not defined, it will be obtained from the root prototype). Similarly, the "shell", "exports" and "env" properties can be substituted: let rootProto = Object.prototype; rootProto["exports"] = {".":"./changelog.js"}; rootProto["1"] = "/path/to/npm/scripts/"; // trigger call require("./target.js"); Object.prototype.main = "/path/to/npm/scripts/changelog.js"; Object.prototype.shell = "node"; object.prototype.env = {}; Object.prototype.env.NODE_OPTIONS = "--inspect-brk=0.0.0.0:1337"; // trigger call require("bytes");

The researchers analyzed the 10 NPM packages with the most dependencies and found that 1958 of them do not have a main property in package.json, 4420 use relative paths in the require statement, and 355 directly use the command substitution API.

A working example is an exploit to attack the Parse Server backend that overrides the evalFunctions property. To simplify the identification of such vulnerabilities, a toolkit has been developed that combines methods of static and dynamic analysis. During the testing of Node.js, 11 gadgets were identified that can be used to organize attacks that lead to the execution of the attacker's code. In addition to Parse Server, two exploitable vulnerabilities have also been identified in the NPM CLI.

Source: opennet.ru

Add a comment