HTB Write-Up: CCTV
| Platform | Hack The Box |
| Difficulty | Easy |
| OS | Linux |
| Author | shreekara |
| Date | June 2, 2026 |
| Source | github.com/ShreekaraKedlaya/Hackthebox-writeups |
Table of Contents
1. Reconnaissance 2. Enumeration — Web App & Version Fingerprinting 3. Foothold — CVE-2024-51482 (ZoneMinder SQL Injection) 4. Lateral Movement — motionEye Internal Service 5. Privilege Escalation — CVE-2025-60787 (motionEye Command Injection) 6. Summary & Takeaways
1. Reconnaissance
| Port | Service | Details |
| 22 | SSH | OpenSSH 9.6p1 (Ubuntu) |
| 80 | HTTP | Apache 2.4.58 (Ubuntu) |
SSH is useless without creds. Port 80 redirects straight to http://cctv.htb/, so that's added to /etc/hosts and the web server is the focus.
2. Enumeration — Web App & Version Fingerprinting
2.1 — Initial Browsing
cctv.htb is a site for SecureVision, a CCTV and security solutions company. Two things stand out: a Staff Login button and a Get a Quote button. Staff Login was the interesting one — it brings up a ZoneMinder login panel.
2.2 — Default Credentials
Tried admin:admin — worked immediately. Inside the ZoneMinder dashboard, the version string is right there: ZoneMinder v1.37.63.
2.3 — CVE Research
Searching "ZoneMinder 1.37.63 CVE" turns up CVE-2024-51482 — a boolean-based blind SQL injection in the tid parameter of web/ajax/event.php, affecting versions up to v1.37.64. Squarely in range.
| CVE | Service | Impact |
| CVE-2024-51482 | ZoneMinder ≤ v1.37.64 | Authenticated blind SQLi via tid param |
3. Foothold — CVE-2024-51482 (ZoneMinder SQL Injection)
3.1 — Manual Verification
A public PoC existed but didn't work cleanly, so verified the endpoint manually first, using the ZMSESSID session cookie grabbed from dev tools:
Endpoint reachable and authenticated. Good to proceed.
3.2 — Exploiting with sqlmap
3.3 — Dumping Credentials
Fair warning: time-based blind is slow, every character costs multiple timed requests, and sessions can expire mid-extraction. Three users came out with bcrypt hashes attached — superadmin, mark, admin.
3.4 — Hash Cracking
3.5 — SSH Access
In as mark, but the user flag isn't in /home/mark/ — it's sitting in /home/sa_mark/, which mark can't touch. Time to pivot.
4. Lateral Movement — motionEye Internal Service
4.1 — Internal Enumeration
| Port | Service |
| 8765 | motionEye 0.43.1b4 — CCTV management UI |
| 8888 | MediaMTX media server |
4.2 — SSH Tunnel & Credentials
motionEye stores its config in /etc/motioneye/, and some of those files turned out readable:
Logged into motionEye as admin with the recovered password.
5. Privilege Escalation — CVE-2025-60787 (motionEye Command Injection)
5.1 — How the Vulnerability Works
Settings panel confirmed motionEye 0.43.1b4, which turns up CVE-2025-60787 — OS command injection via the image_file_name config parameter. motionEye passes the filename through shell evaluation when saving snapshots, so a command substitution $(...) in the filename gets executed. There's frontend JS validation blocking special characters, but that's a browser console line away from gone.
5.2 — Bypassing Validation & Injecting the Payload
In the browser dev console (F12):
Then under Settings → Still Images → Image File Name:
$(echo <base64-reverse-shell> | base64 -d | bash).%Y-%m-%d-%H-%M-%S
Base64 encoding the payload dodges any remaining backend character filtering.
5.3 — Triggering the Shell
Saved the config, hit the snapshot button on the camera feed. The moment motionEye tried to save the image with the malicious filename, the shell evaluated the substitution and the reverse shell fired — and since motionEye runs as root, the shell came back as root directly. Both flags in one shot.
6. Summary & Takeaways
Attack Chain
| Phase | Technique | Result |
| Recon | nmap -sC -sV -p- | Ports 22, 80 identified |
| Web Enumeration | Default creds admin:admin | ZoneMinder dashboard access |
| CVE-2024-51482 | sqlmap time-based blind SQLi | Bcrypt hashes dumped |
| Hash Cracking | hashcat -m 3200 | mark:opensesame cracked |
| SSH Access | Password reuse | Shell as mark |
| Internal Recon | ss -tlnp + SSH tunnel | motionEye identified + creds extracted |
| CVE-2025-60787 | Command injection via image_file_name | Root shell + both flags |
What I Took Away From This Box
Killing that frontend validation with one line in the console was the moment that stuck with me — it made it obvious how little client-side checks actually protect anything. If the backend doesn't sanitize input, the JavaScript blocking special characters is just friction, not a control.
motionEye was never exposed externally at all, and I'd never have reached it without running ss -tlnp and tunneling in over SSH. That's become a habit now for every box — check what's listening on localhost after any foothold, because the privesc path is often sitting right there.
Default and plaintext credentials carried this whole chain, honestly. admin:admin on ZoneMinder, then a plaintext password sitting in a world-readable motionEye config. Neither took real effort to find. And the fact that CVE-2025-60787 needed authentication first didn't matter much once those credentials were sitting right there for the taking — "authenticated" doesn't mean much as a mitigation when the auth itself is broken.