Stocker
Stocker
Difficulty: Easy
Classification: Official
Synopsis
Stocker is a medium difficulty Linux machine that features a website running on port 80 that advertises
various house furniture. Through vHost enumeration the hostname dev.stocker.htb is identified and
upon accessing it a login page is loaded that seems to be built with NodeJS . By sending JSON data and
performing a NoSQL injection, the login page is bypassed and access to an e-shop is granted. Enumeration
of this e-shop reveals that upon submitting a purchase order, a PDF is crafted that contains details about
the items purchased. This functionality is vulnerable to HTML injection and can be abused to read system
files through the usage of iframes. The index.js file is then read to acquire database credentials and owed
to password re-use users can log into the system over SSH . Privileges can then be escalated by performing
a path traversal attack on a command defined in the sudoers file, which contains a wildcard for executing
JavaScript files.
Skills Required
vHost enumeration
System Enumeration
Skills Learned
NoSQL injection
HTML Injection
Wildcard Injection
Path Traversal
Enumeration
Nmap
Let's begin by scanning for open ports using Nmap .
The scan reveals ports 22 ( SSH ) and 80 ( Nginx ) open. Let's check out port 80 using a browser. Upon
accessing the server via its IP address, we are redirected to stocker.htb , so let's add this vHost to our
hosts file.
Upon accessing the new vHost we see a login page. Various default credentials do not seem to work, but if a
login page exists there must be a Database in the backend that handles the users. Attempting to perform
an SQL Injection does not seem to allow us to log in, so let's take a closer look at the headers that the server
is sending us.
curl -v dev.stocker.htb
From the output of the above command, we can see the X-Powered-By header that lists Express as the
software in use. Express is a NodeJS web application framework that is used to serve web pages.
Express usually supports JSON- as well as URL-encoded requests. Let's test this out by intercepting a login
request with BurpSuite .
After catching the request let's switch the Content-Type header to application/json and convert the
POST data to JSON, as well. We can also send this request to the Repeater tab by pressing Ctrl + r so
that we can more easily send requests. The request becomes as follows.
{"username":"admin","password":"admin"}
After clicking Forward , we get the error message Invalid username or password , which means that our
payload was accepted, however, our credentials are still not valid.
When an application is built on NodeJS and Express it is common for databases other than MySQL to be
in use, such as PostgreSQL and MongoDB . The latter is a NoSQL database, which means it is a type of
database that does not use relational tables. NoSQL databases can also suffer from SQL Injection attacks
(called NoSQL Injection ), similar to how SQL databases can suffer from SQL Injection attacks. If such a
database is being used here, which is a common occurrence for NodeJS , it might be possible for us to inject
the parameters and bypass the login page.
The $ne value is an operator similar to != that checks for inequality. In the above payload, this means that
the username and password values are checked to make sure they are not equal to NULL . Let's copy the
above payload, perform another login attempt, catch the request on BurpSuite , and then switch the URL-
encoded payload to JSON, as shown previously. The final request is as follows.
After clicking Forward one more time, we are successfully logged in and are redirected to /stock .
This page contains a few items that are apparently being sold and we can add them to our basket and
submit a purchase request for them.
After clicking on Submit Purchase we see the following message pop up.
Clicking on the link to view our purchase order redirects us to /api/po , followed by what seems to be
random hexadecimal characters, and loads a PDF file.
Foothold
Let's add an item to our basket and catch the purchase request in BurpSuite to see what is going on
behind the curtains.
{"basket":[{"_id":"638f116eeb060210cbd83a8d","title":"Cup","description":"It's a red
cup.","image":"red-cup.jpg","price":32,"currentStock":4,"__v":0,"amount":2}]}
The purchase request seems to be sending a lot of information about each item, but the most interesting
parameter is the title of the item as it is directly displayed in the PDF. We can deduce from this that a PDF
generator is being used to create the PDF from the specific information about each file. PDF generators can
typically also parse and render HTML tags, which opens up the possibility of an HTML injection.
We can easily try this by changing the item title to <script>alert(1)</script> . After clicking on the link
to redirect to the PDF the server sends back an Internal Server Error message, meaning that the
generator tried to render the script tags but failed.
Taking the above information into consideration, it might be possible for us to make the PDF generator load
and display system files by using iframes . Consider the following HTML payload.
iframes , or Inline Frames, are HTML elements that can load another HTML page within the same
document. The src attribute is the origin of the content from the external or internal server.
The above HTML loads the /etc/passwd file and displays it inside an iframe that is 1000 pixels wide and
1000 pixels tall. Let's try this once more by capturing another purchase submission and inputting the above
payload into the title field.
After sending the request and opening the PDF, as shown previously, we see the following.
We have successfully managed to load the passwd file and we take note of a user called angoose that
exists in the system. With the ability to read system files, we can attempt to read various potentially
interesting files but it might also be worthwhile to check out the web files for potential database passwords
since we know the application uses NoSQL for the user login.
We come upon a problem, however, which is that we do not know where the development application is
located. An easy trick we can use to potentially determine this location is to send incorrectly formatted JSON
in one of the application's endpoints to see if it throws an error. Let's use the Purchase endpoint as we did
previously. To do this we can catch another request in Burp and remove one of the brackets } . After
removing the bracket and sending the request we get the following error message.
SyntaxError: Unexpected end of JSON input<br>
at JSON.parse (<anonymous>)<br>
at parse (/var/www/dev/node_modules/body-parser/lib/types/json.js:89:19)
<br>
at /var/www/dev/node_modules/body-parser/lib/read.js:128:18<br>
at AsyncResource.runInAsyncScope (node:async_hooks:203:9)<br>
at invokeCallback (/var/www/dev/node_modules/raw-body/index.js:231:16)<br>
at done (/var/www/dev/node_modules/raw-body/index.js:220:7)<br>
at IncomingMessage.onEnd (/var/www/dev/node_modules/raw-
body/index.js:280:7)<br>
at IncomingMessage.emit (node:events:513:28)<br>
at endReadableNT (node:internal/streams/readable:1359:12)<br>
at process.processTicksAndRejections
(node:internal/process/task_queues:82:21)
From the error, we can see that the application is hosted in the /var/www/dev/ folder and since this is a
NodeJS application we can try to read various default filenames to find the main function of the
application. Typically this is called index.js or main.js or even server.js .
Trying the above names we manage to load the file called index.js and get a big part of the code.
In the code we can see the connection string for the Mongo database as well as the password being used,
namely IHeardPassphrasesArePrettySecure . Owed to password re-use we can log into the machine over
SSH with the username we identified earlier.
ssh [email protected]
The user flag can be found in /home/angoose/ .
Privilege Escalation
Let's check if the user can run any commands using sudo .
sudo -l
The results show that the user is able to run any script in /usr/local/scripts using node as every user
on the system. Let's see what this folder contains.
cd /usr/local/scripts
ls -al
The folder contains a few scripts that seem to be used to generate reports about orders and profit from the
website, however, we cannot write to this folder.
We do note though that because the sudo command that we can run contains a wildcard character, we
might be able to perform a path traversal in our command through the usage of ../ and execute a
JavaScript file of our own choice and potentially spawn a root shell.
Let's create a file called shell.js in /tmp and paste the above code inside it. Then, we can execute this file
as follows: