
Un'implementazione di un proof-of-concept per CVE-2020-12124
originariamente su https://www.klogixsecurity.com/scorpion-labs-blog/anatomy-of-an-iot-exploit-from-hands-on-to-rce
di David E. Baker, pubblicato il 1 giugno 2023
Questo studio riguarda il firmware del router Wavlink Wireless-AC1200 Gigabit a partire da giugno 2020. Le vulnerabilità discusse qui potrebbero essere state corrette o meno dal fornitore, ma si tratta di un caso di ricerca di vulnerabilità in cui il viaggio è la ricompensa, non la conclusione. L'autore ha condotto questa ricerca prima della divulgazione pubblica delle vulnerabilità, ma dopo che erano state scoperte indipendentemente da altri ricercatori e segnalate al fornitore.
Il fornitore rende disponibile il firmware per i propri prodotti nella sezione di supporto del proprio sito web; questo è un modo comune per ottenere il firmware IoT e un'alternativa utile rispetto all'estrazione dalla memoria del dispositivo. Il firmware non è crittografato, quindi può essere facilmente estratto con binwalk. L'analisi dinamica è stata effettuata con accesso a un esemplare fisico del dispositivo, e l'analisi statica è stata effettuata tramite Ghidra.
L'interfaccia web del router Wavlink Wireless-AC1200 Gigabit ha diversi endpoint vulnerabili che consentono la copia illimitata di dati forniti dall'utente nello stack dell'applicazione o addirittura direttamente nella riga di comando per ottenere l'esecuzione arbitraria di comandi.
Le scansioni iniziali del dispositivo suggeriscono che l'unica risorsa esposta fosse la console di amministrazione web, accessibile agli utenti autenticati sull'interfaccia LAN via HTTP sulla porta TCP 80. Il dispositivo può offrire più servizi, ma non sono abilitati out-of-the-box e per impostazione predefinita. Di conseguenza, questa indagine si concentra esclusivamente sull'interfaccia web.
Una scansione nmap del dispositivo esemplare, che mostra solo l'interfaccia web in ascolto.
I test usuali — come le tipiche iniezioni di comandi che si trovano nei pannelli di
diagnostica del dispositivo che consentono un'iniezione di comandi nei parametri di un
ping o del comando traceroute — non hanno prodotto risultati immediatamente interessanti,
il che è stato deludente.
Opzioni di gestione disponibili dopo l'autenticazione al pannello di amministrazione web. “USB Storage” è visibile come seconda opzione.
La prima interfaccia (alla fine sfruttabile) esaminata è stata trovata nel pannello “USB Storage”, visibile come seconda opzione nello screenshot sopra. Il dispositivo ha una porta USB adiacente ai connettori Ethernet 802.2, suggerendo che potrebbe offrire funzionalità di archiviazione collegata in rete (NAS).
Una foto del retro dell'esemplare reale, che mostra la disponibilità della USB.
Un principio semplice nella ricerca di vulnerabilità è che più componenti interagiscono con un pezzo di codice e più parti mobili ha, più è probabile che ci sia codice sfruttabile nelle vicinanze. La presenza di funzionalità NAS è promettente perché indica la presenza di codice che interagisce contemporaneamente con il livello software del dispositivo, il livello hardware e la periferia collegata (l'archiviazione USB stessa).
L'interfaccia di gestione per la console di archiviazione USB è mostrata di seguito.
La sola presenza del campo “Workgroup” è promettente, poiché suggerirebbe che questo
router WiFi possa persino tentare di interagire tramite Server Message Block (SMB) —
un grande sforzo per un router IoT. Non riesco a contare quante volte
ho visto input fornito dall'utente inviato direttamente alla riga di comando
come argomento alla funzione Unix smbpasswd.
Opzioni di archiviazione USB disponibili per gli utenti autenticati.
I tentativi iniziali di manipolare queste impostazioni sono falliti a causa del mancato rilevamento di un'unità USB da parte del dispositivo, come mostrato di seguito.
Le modifiche di configurazione alle opzioni di archiviazione USB non verranno salvate a meno che un'unità formattata correttamente non venga collegata manualmente alla porta USB del dispositivo.
Tuttavia, una volta collegata un'unità formattata correttamente, il dispositivo ha permesso di impostare un nome utente e una password FTP. Come sospettato, ha inserito questo input fornito dall'utente nella riga di comando:
Un'iniezione di comandi nel campo 'password' fornisce il primo accesso shell direttamente al sistema operativo del dispositivo.
Sebbene interessante, questa vulnerabilità è difficile da suscitare grande entusiasmo: richiede non solo l'accesso con credenziali all'interfaccia di amministrazione del dispositivo, ma anche l'accesso fisico al dispositivo per manipolare la sua unità USB. L'exploit sopra menzionato consente a un ricercatore di interagire con i singoli componenti del sistema operativo (e di esfiltrarli a fini di reverse engineering).
Il dispositivo era un sistema Linux incentrato su busy box con un'interfaccia web
alimentata da Lighttpd. La funzionalità Common Gateway Interface (CGI)
era fornita da singoli binari in /etc\_ro/lighttpd/www/cgi-bin/,
con richieste web agli URI CGI che avviavano direttamente questi binari. Uno sguardo
rapido a nas.cgi in Ghidra mostra l'iniezione di comandi alla riga 38 qui sotto,
che invia una password fornita dall'utente direttamente alla funzione do\_system
(essa stessa semplicemente un wrapper attorno alla chiamata di sistema standard libc).
L'input utente viene inserito nella riga di comando come argomento dello script chpasswd.sh alla riga 38, risultando in un'iniezione di comandi e nell'accesso shell direttamente al sistema operativo del dispositivo.
Esaminando la directory /cgi-bin/ si riduce il compito di trovare un
exploit più interessante all'enumerazione delle interfacce CGI disponibili per
l'utente, mostrate di seguito:
Un elenco esaustivo dei binari CGI messi a disposizione sul dispositivo, preso live dalla shell stabilita dall'exploit descritto in questa sezione. L'input utente viene inserito nella riga di comando come argomento dello script chpasswd.sh alla riga 38, risultando in un'iniezione di comandi e nell'accesso shell direttamente al sistema operativo del dispositivo.
Diverse cose risaltano all'esame iniziale. La prima cosa importante
da notare è che i binari CGI spesso chiamano la funzione check_valid_user.
Questo metodo verifica se l'indirizzo IP che effettua la richiesta è
memorizzato in un particolare file temporaneo sul filesystem. Test minimi
mostrano che lo stato di autenticazione di un client non viene verificato fino a quando
non viene effettuata una chiamata a questo metodo, quindi l'intera superficie di codice
in ogni binario CGI prima dell'invocazione di questa funzione è accessibile senza autenticazione.
La decompilazione di adm.cgi che mostra il metodo check\_valid\_user. Tutto il codice precedente a questa chiamata viene eseguito prima di verificare lo stato di autenticazione del richiedente.
Un'altra osservazione interessante è il gran numero di metodi che copiano
l'input fornito dall'utente direttamente sullo stack. Ad esempio, la
decompilazione di wireless.cgi mostra il parametro argomento NewName preso
dal corpo di una richiesta web alle righe 14 e 15, seguito da un
strcpy senza protezione di questo input utente sullo stack alla riga 34, mostrato di seguito:
Le righe 14 e 34 dimostrano un strcpy senza protezione del parametro NewName dal corpo della richiesta direttamente sullo stack del programma. La decompilazione di adm.cgi che mostra il metodo check\_valid\_user. Tutto il codice precedente a questa chiamata viene eseguito prima di verificare lo stato di autenticazione del client.
Questa copia dell'input utente sullo stack da sola suggerisce un exploit di corruzione della memoria, che il comando seguente può verificare:
curl –XPOST --data "page=SetName&NewName=\`python3 –c 'print(\\"A\\"\*(512)'\`" http://target-ip/cgi-bin/wireless.cgi
Sebbene promettente, un attacco return-oriented non è ottimale qui. Usando l'accesso shell al sistema operativo già ottenuto, il seguente comando mostra '1', indicando che Address Space Layout Randomization (ASLR) è applicato debolmente, lasciando a una catena ROP una probabilità di 1 su 256 di atterrare sul gadget desiderato:
# cat /proc/sys/kernel/randomize\_va\_space
1
Inoltre, il comando seguente mostra che i binari little-endian sono compilati con il byte stack-guard impostato, quindi solo l'ultimo gadget in una catena può atterrare in una posizione predeterminata all'interno del binario. Se è presente un processo watchdog per riavviare il server web dopo un crash, non è fuori questione lanciare ripetutamente un exploit return-oriented e aspettarsi successo alla fine, ma è anche possibile che ci siano bug migliori nascosti altrove nel codice.
xxd /etc\_ro/lighttpd/www/cgi-bin/wireless.cgi | head -n 10
Continuando a esaminare le funzioni CGI, si arriverà infine a
live\_api.cgi. Questo binario non effettua alcuna chiamata a check\_valid\_user, quindi
qualsiasi richiesta web all'URI /cgi-bin/live\_api.cgi esegue questa applicazione CGI
senza autenticazione. La riga 9 nella Figura 11 mostra che la variabile d'ambiente
QUERY\_STRING, che (secondo la specifica CGI di Apache) è la
parte dell'URI della richiesta immediatamente successiva a un punto interrogativo e
pertanto fornita dall'utente, è memorizzata in pcVar1 e alla riga 19 della Figura 11
viene inviata al metodo satellite_status.
Una decompilazione di live\_api.cgi che mostra l'input utente preso dall'URI alla riga 9 e inviato a satellite\_status alla riga 19. La decompilazione di adm.cgi che mostra il metodo check\_valid\_user. Tutto il codice precedente a questa chiamata viene eseguito prima di verificare lo stato di autenticazione del client.
La decompilazione del metodo satellite_status, mostrata nella Figura 12, mostra
che la stringa di query stessa (ora param\_1) viene analizzata per i parametri
page, id e ip. Il parametro ip viene copiato tramite la funzione
sprintf in una variabile locale alla riga 38 della Figura 12 e, alla riga 39,
viene passato alla funzione do\_system. L'assenza di una chiamata a
check\_user\_auth indica che l'input arbitrario del client da un
utente non autenticato nell'URI verrà inserito direttamente nella riga di comando
nel parametro ip dell'URI, confermato di seguito:
Una decompilazione della funzione satellite\_status, che mostra la stringa di query (ora param\_1) analizzata per il parametro ip alle righe 22 e 23, quindi una chiamata a do\_system alle righe 38 e 39.
La prova è nel pudding, mostrando l'exploit utilizzato per effettuare una presa di controllo remota del dispositivo.
Trovare exploit in un dispositivo IoT appena arrivato sul mercato può sembrare frutto di poco sforzo, come accennato all'inizio di questo post, ma il valore di questa indagine è stato nel viaggio piuttosto che nella destinazione.
Una comprensione dell'elusione dell'autenticazione e della posizione dell'iniezione di comandi sarebbe stata improbabile, o addirittura impossibile, senza l'analisi statica del firmware. Se non fosse stato disponibile online, avrebbe richiesto l'accesso dinamico al sistema operativo del dispositivo per ottenerlo, per il quale ci sarebbero state scarse speranze di accesso dinamico al dispositivo. Questo livello di accesso stesso si basava sia sull'accesso fisico al dispositivo, sia su hardware specializzato o su un'ipotesi guidata sui probabili punti nel codice in cui potrebbero essere stati commessi errori.
Speriamo che questo viaggio ti sia piaciuto e che torni per altri. Buon hacking!
Autore:
David Baker, Consulente Senior per la Sicurezza, Testing, K logix