
Questo è un breve report di una vulnerabilità (CVE-2019-12489) scoperta giocando con un modem/router Fastgate di Fastweb e Ghidra. La vulnerabilità consente l'esecuzione di un command injection tramite una richiesta http e può essere utilizzata per abilitare una shell SSH.
Tutti i file del firmware si trovano in http://59.0.121.191:8080/ACS-server/file/FILENAME
Dove FILENAME è uguale a:
Quindi, ad esempio, per ottenere l'ultimo firmware basta andare su http://59.0.121.191:8080/ACS-server/file/0.00.89_FW_200_Askey
Per estrarre i file ci serve l'utility in https://github.com/nlitsme/ubidump (è stata la prima che ha funzionato per me).
Eseguendo binwalk sul file otteniamo:
$ binwalk 0.00.89_FW_200_Askey
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
94 0x5E JFFS2 filesystem, little endian
3276894 0x32005E UBI erase count header, version: 1, EC: 0x0, VID header offset: 0x800, data offset: 0x1000
Non abbiamo bisogno del filesystem JFFS2, ma solo della parte UBI. Per farlo possiamo tagliare il file usando dd. Per farlo abbiamo bisogno della dimensione totale e del punto di inizio della UBI.
La dimensione del file può essere recuperata usando ls:
$ ls -la
total 55836
drwxr-xr-x 3 riccardo users 4096 set 24 10:07 .
drwx------ 83 riccardo users 12288 set 24 10:08 ..
-rw-r--r-- 1 riccardo users 57147506 set 24 10:05 0.00.89_FW_200_Askey
Il valore 57147506 è quello che ci serve. Guardando l'output di binwalk possiamo vedere che la parte UBI inizia a 3276894. Con questi due valori possiamo usare dd in questo modo:
$ dd if=0.00.89_FW_200_Askey of=0.00.89_FW_200_Askey.UBI bs=1 skip=3276894 count=57147506
Ora possiamo usare ubidump (dopo una configurazione iniziale a seconda della configurazione di sistema):
$ git clone https://github.com/nlitsme/ubidump.git
$ sudo pip install crcmod
$ yay -S python-lzo
$ python ubidump/ubidump.py -s bump 0.00.89_FW_200_Askey.UBI
Ora abbiamo il filesystem in bump/rootfs_ubifs/
Ora possiamo procedere un po' oltre nell'analisi.
Guardando nel filesystem possiamo vedere che la GUI web è gestita da mini_httpd:
$ grep -i -r "mini_httpd"
etc/sniproxy/urlfilter3.sh:ln -sf /usr/sbin/mini_httpd $URLFILTER_WEBD_PATH
etc/sniproxy/urlfilter3.sh:$URLFILTER_WEBD_PATH -d $URLFILTER_WEB_PATH -c "**.cgi" -u admin -S -E /web/mini_httpd.pem -p $webd_https_port
bin/web.sh:#mv /tmp/mini_httpd /usr/sbin
bin/web.sh:killall -9 mini_httpd
bin/web.sh:mini_httpd -d /web -c "STATUSAPI/**.cgi|STATUSAPI/*" -u admin -T "UTF-8" -p 8080
E possiamo vedere che mini_httpd dipende da un file .cgi:
$ grep -i -r "status.cgi"
web/factory/mtcmd/mt_info.htm: "url":"/status.cgi",
web/fw-router/app/app.js:.value('apiServiceUrl', '/status.cgi')
**web/js/status.js:var CGI_NAME='status.cgi';**
web/js/status.js: "url":"http://192.168.101.93/STATUSAPI/status.cgi",
web/js/status.js: "url":"http://192.168.101.93/STATUSAPI/status.cgi",
web/js/status.js: "url":http://192.168.101.93/STATUSAPI/status.cgi,
Quindi, usando Ghidra, possiamo iniziare a guardare in status.cgi
Tralasciando molti dettagli, la vulnerabilità è descritta qui sotto.
La prima cosa da cercare è un metodo main come quello in questa immagine:

Possiamo vedere che viene chiamato setup. Dopo un po' di ricerca, run_service(...) sembra essere il cuore di tutte le funzionalità.

Cercando &service_tab possiamo vedere che alcune stringhe specifiche sono collegate al codice effettivamente eseguito da run_service(...)

Dopo aver esaminato questi metodi uno per uno, mi sono imbattuto in usb_remove

In dettaglio:
mountVal = (char *)find_val(httpparams,"mount");
Prende la stringa nella richiesta url direttamente in una variabile
sprintf((char *)&mountLocation,"/mnt/%s",mountVal);
Crea un percorso completo dato il parametro nella richiesta URL
mountLocation
mountLocation viene usato direttamente in un comando
Ora, ci sono dei controlli per prevenire l'iniezione di comandi. In particolare:
invalidChars = 0x60273b7c;
Viene usato per controllare alcuni caratteri non validi come ` ' ; | ma & non è presente nella lista, quindi se il parametro mount è uguale a &ping -c 10 192.168.1.172& il controllo permetterà l'esecuzione di /statusapi/bin/umount -lf &ping -c 10 192.168.1.172&.
Il comando mount fallirà, ma il ping successivo verrà eseguito.
La patch pubblicata semplifica il codice eliminando i controlli per i caratteri speciali, limitandosi a verificare se la cartella esiste.

Il codice per sfruttare questa vulnerabilità e abilitare ssh è in fastwebEnableSSH.py https://github.com/garis/Fastgate/blob/master/fastwebEnableSSH.py