Shoppy HTB Walkthrough
Intro
I have been spending some time over the holiday break working on CTF challenges - including both Hack The Box, PicoCTF and learning a little binary exploitation. Taking notes for my own review & learning seemed like a good opportunity to post up CTF walkthroughs and get back into the habit of blogging again.
So, here is the first of hopefully many walkthroughs I will post up as time permits, and when machines on Hack The Box are retired.
Enumeration
As always, we connect to the HTB VPN using OpenVPN, and join the machine to be given an IP address. All work is done from a Parrot OS VirtualBox VM.
Firstly, a quick nmap scan:
|
|
Browsing to http://10.10.11.180 redirects to http://shoppy.htb, so I added this to my /etc/hosts file.
Browsing to http://shoppy.htb gives a landing page with a countdown:
Gobuster output using common.txt wordlist:
|
|
Foothold
I attempted to run sqlmap against http://shoppy.htb/login, but kept getting timeout errors:
|
|
I next ran sqlmap through burp suite and checked the intercepted traffic:
|
|
At this point, it appeared to my novice eyes as though a WAF (web app firewall) was blocking SQL injection attempts (maybe?) as ’normal’ login attempts result in a 302 redirect, not a 504 gateway timeout.
I did notice the timeouts only occur when injecting into user, not password – so I suspected that this was the exploitable input field..
But the above was a modified intercepted request from Firefox with a legitimate user agent string along with other headers – so perhaps not a WAF and something else?..
After a little while of unsuccessful SQL Injection both with SQLMap and y, I back tracked a little to look at other injection techniques and started reading NoSQL injection - HackTricks
I then tried the following to see if I was dealing with some document database that could be injected into:
|
|
The initial example of username=admin' || 1==1//&password=foobar
gave a 504, but the above worked and gave the following response!:
|
|
After the redirect I ended up at /admin:
The page looks fairly static, apart from the Search for users button which does a GET http://shoppy.htb/admin/search-users .
If i inject into this with ' || 1==1//
, I get two users in the download link that shows - http://shoppy.htb/exports/export-search.json:
|
|
After running hashcat against a file containing both MD5 sums:
|
|
I was unable to SSH into the machine with josh/remembermethisway, and here I got a little stuck.. I reviewed the server, looking for other clues but couldn’t find much.
I decided to scan all ports with nmap -sC -sV -oN nmap/all -p- 10.10.11.180
, and saw 9093 was an open HTTP server
Searching around, it looked like Prometheus go metrics – however there was nothing that appeared particularly useful.
I’ll admit that here I ‘cheated’ and checked the HTB discussion thread for this box – and saw people hinting at another subdomain.
I noticed when searching for some of the terms in the above prometheus alerts, particularly around playbooks, I saw reference to mattermost: GitHub - mattermost/mattermost-plugin-playbooks: Mattermost Playbooks enable reliable and repeatable processes for your teams using checklists, automation, and retrospectives.
With this in mind, I ran a vhost scan in gobuster using SecList DNS lists..
|
|
Luckily the first wordlist found the subdomain, and I ended the scan and added mattermost.shoppy.htb to my /etc/hosts. Now I’m able to use josh/remembermethisway credentials to log in to http://mattermost.shoppy.htb/:
After a little poking around, in the Deploy Machine channel we find credentials jaeger/Sh0ppyBest@pp!:
Immediately jumping to SSH, we now have a user flag!:
|
|
Privilege Escalation
Checking sudo access, I can run /home/deploy/password-manager, which was mentioned somewhere in the mattermost chat:
|
|
Running a `strings /home/deploy/password-manager shows a credentials text file, which I cannot cat myself directly:
|
|
So I am able to run the password manager, but need the master pass, or to somehow bypass this..
|
|
In /home/deploy, there is a password-manager.cpp which we cannot read. This is in line with the mattermost chat where Josh mentions creating a password manager in C++.
We have access to the compiled binary, so let’s take a copy to play with locally:
|
|
Taking a look at the decompiled main function in Ghidra, it looks like we call the += operator in order to concatenate and build a string stored in variable local_68, which we compare to local_48 (which is std::cin – i.e. the standard input stream).
So, the app expects “Sample” – let’s try that:
|
|
I can now ssh into the deploy account, and confirm the password manager logic above based on the c++ source:
|
|
I tried clobbering cat in $PATH to privesc, when logged in as jaeger and entering the password – but remembered sudoers is set so you cannot preserve the environment, and /home/deploy/password-manager only runs as the deploy user anyway.
I then remembered in matter most chat, there was mention of the deploy account being used and it would be deploying via docker.. So, let’s see if we can access docker, if there are any images (HTB boxes do not have internet access, so cannot pull down container images from dockerhub), and if we can run a privileged container to try get root on the host.
Docker based privilege escalation is new to me, and not something I have experience with – but I was able to consult the always excellent HackTricks - Docker Breakout / Privilege Escalation - HackTricks
Luckily, we have access to an alpine image we can use to spin up a privileged container. Inside the container we run nsenter:
|
|
We get contexts from our init process (PID 1), and enter the IPC, network and PID namespaces – which are located in /proc/1/ns/ within the container’s filesystem.
Excellent, we have the root flag!…
Conclusion
A fun little VM that required back tracking and checking assumptions a couple of times. It’s easy to jump to the conclusion of SQL injection and become fixated on this, but these assumptions need to be tested and confirmed early on. I went down a little bit of a rabbit hole looking at WAF bypass techniques assuming the gateway timeouts were actually a WAF I needed to work around before SQL injection would work.
Fuzzing with fuff or similar early on and trying out different special characters while checking the HTTP response for a 302 may have helped confirm this was a NoSQL datbase.
Finding the mattermost virtualhost was also a little tricky, and sometimes you just have to rely on getting lucky with scans and using the right wordlist.. Although sticking to common lists made this easy. I found the Prometheus logs after revisiting open ports with a full nmap scan, but didn’t think much of it at the time – but I should have searched more keywords here, which may have helped identify the mattermost virtual host without having to bruteforce using a wordlist.
Docker based privilege escalation and nsenter
is not something I’ve seen or used before, so I’m keen to deep dive a little more.