Skip to main content
Vulnerability Database/CVE-2026-30971

CVE-2026-30971: ZoneMinder RCE Vulnerability

CVE-2026-30971 is a remote code execution flaw in ZoneMinder that allows authenticated users to execute arbitrary commands through unvalidated monitor Device fields. This post covers technical details, affected versions, impact, and mitigation steps.

Published:

CVE-2026-30971 Overview

ZoneMinder, a widely deployed open source closed-circuit television (CCTV) application, contains an OS command injection vulnerability [CWE-78] in its monitor control paths. An authenticated user with monitor-create or monitor-edit permission can save a Local monitor whose Device path contains shell metacharacters. When the monitor control or startup path runs, the stored Device value reaches Perl qx() and backtick execution before zmdc.pl argument detainting. Separate PHP command paths rely on bypassable escapeshellcmd defenses. The attacker can execute arbitrary commands as the web server user. Fixed versions are 1.39.2, 1.38.2, and 1.36.40.

Critical Impact

Authenticated attackers with monitor edit rights can achieve remote code execution as the web server user through unsanitized Device path interpolation in Perl and PHP shell commands.

Affected Products

  • ZoneMinder versions prior to 1.39.2
  • ZoneMinder versions prior to 1.38.2
  • ZoneMinder versions prior to 1.36.40

Discovery Timeline

  • 2026-10-16 - CVE-2026-30971 published to NVD
  • 2026-10-16 - Last updated in NVD database

Technical Details for CVE-2026-30971

Vulnerability Analysis

The vulnerability stems from unsafe shell command construction in ZoneMinder's monitor control flow. When a Local monitor is saved, the Device field is persisted without validation. Perl control paths in ZoneMinder/Monitor.pm and zmpkg.pl.in interpolate $monitor->{Device} directly into strings passed to runCommand, which routes through qx() and backtick execution. Argument detainting in zmdc.pl happens after the shell has already parsed the string. In parallel PHP paths, escapeshellcmd is used, but it does not neutralize shell metacharacters within argument boundaries, leaving the command line injectable.

Root Cause

The root cause is missing input validation on the monitor Device attribute combined with shell string interpolation. The code assumed Device would contain a legitimate /dev/* path. No regex constraint or allowlist validated the field at save time, and the downstream Perl execution context invoked the shell rather than passing arguments as a list.

Attack Vector

An authenticated user with monitor creation or edit permissions supplies a crafted Device value such as /dev/video0; <command>. The value persists to the database. When control actions, package start, or startup paths execute, Perl constructs a shell command containing the attacker payload. The shell interprets the injected metacharacters and executes arbitrary commands under the ZoneMinder web server account.

perl
# Patch excerpt: scripts/ZoneMinder/lib/ZoneMinder/Monitor.pm
  my $command = shift;
  my $process = shift;

+ my $valid_device = (defined $monitor->{Device} and $monitor->{Device} =~ /^\/dev\/[\w\/.\-]+$/);
+ if ($monitor->{Type} eq 'Local' and !$valid_device) {
+   Error("Invalid device path rejected: $monitor->{Device}");
+   return;
+ } elsif (!$valid_device and defined $monitor->{Device} and length($monitor->{Device})) {
+   Warning("Monitor $$monitor{Id} has invalid device path: $monitor->{Device}");
+ }

  if ($command eq 'stop') {
    if ($process) {
      ZoneMinder::General::runCommand("zmdc.pl stop $process -m $$monitor{Id}");
    } else {
      if ($monitor->{Type} eq 'Local') {
-       ZoneMinder::General::runCommand('zmdc.pl stop zmc -d '.$monitor->{Device});
+       ZoneMinder::General::runCommand("zmdc.pl stop zmc -d '$monitor->{Device}'");
      } else {
        ZoneMinder::General::runCommand('zmdc.pl stop zmc -m '.$monitor->{Id});
      }

Source: ZoneMinder commit 419846c

The patch adds a strict regex ^\/dev\/[\w\/.\-]+$ allowlist, rejects non-matching values for Local monitors, and quotes the device argument.

Stop Remote Code Execution Before It Runs

Singularity™ Endpoint detects and blocks exploit attempts at machine speed, including zero-days and novel attack techniques.

Stop Exploit Attempts

Detection Methods for CVE-2026-30971

Indicators of Compromise

  • Monitor records in the ZoneMinder database with Device values containing shell metacharacters such as ;, |, `, $(, or newlines.
  • Child processes of the ZoneMinder web server user spawning shells (sh, bash) or network utilities (curl, wget, nc) that are not part of normal CCTV operation.
  • ZoneMinder logs showing runCommand entries with device paths outside the /dev/ tree.
  • Unexpected outbound connections from hosts running zmdc.pl or zmpkg.pl.

Detection Strategies

  • Audit the Monitors database table for any Device column value that does not match ^/dev/[\w/.\-]+$.
  • Monitor process creation events for ZoneMinder service accounts and alert on non-whitelisted child executables.
  • Correlate ZoneMinder admin login events with subsequent monitor configuration changes to identify abuse of monitor-edit permissions.

Monitoring Recommendations

  • Enable verbose logging in zmdc.pl and ship logs to a central analytics platform for query against injection patterns.
  • Alert on web server user shell invocations that are not initiated by expected ZoneMinder binaries.
  • Track configuration changes to the Monitors table with database audit logging.

How to Mitigate CVE-2026-30971

Immediate Actions Required

  • Upgrade ZoneMinder to 1.39.2, 1.38.2, or 1.36.40, whichever matches your release branch.
  • Revoke monitor-create and monitor-edit permissions from accounts that do not require them until the upgrade is complete.
  • Inspect existing monitor configurations and remove or correct any Device values that fall outside the /dev/ allowlist.

Patch Information

The fix is tracked in GitHub Security Advisory GHSA-g66m-77fq-79v9 and implemented in ZoneMinder commit 419846c. The patch enforces a device path allowlist in ZoneMinder/Monitor.pm and zmpkg.pl.in, rejects invalid Local device paths, and single-quotes the device argument passed to zmdc.pl.

Workarounds

  • Restrict access to the ZoneMinder web console to trusted administrators via network segmentation or VPN.
  • Place the ZoneMinder web server behind a reverse proxy and block POST requests to monitor edit endpoints from untrusted sources until patched.
  • Manually review and sanitize any Device values stored in the Monitors table before the next monitor control or startup execution.
bash
# Database audit query to surface injectable Device values
mysql -u zmuser -p zm -e \
  "SELECT Id, Name, Type, Device FROM Monitors \
   WHERE Type='Local' AND Device NOT REGEXP '^/dev/[A-Za-z0-9/._-]+$';"

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.