
Este es un breve informe de una vulnerabilidad (CVE-2019-12489) descubierta jugando con un módem/router Fastgate de Fastweb y Ghidra. La vulnerabilidad permite la ejecución de una inyección de comandos a través de una petición HTTP y puede utilizarse para habilitar un shell SSH.
Todos los archivos de firmware se encuentran en http://59.0.121.191:8080/ACS-server/file/FILENAME
Donde FILENAME es igual a:
Por lo tanto, por ejemplo, para obtener el último firmware simplemente ve a http://59.0.121.191:8080/ACS-server/file/0.00.89_FW_200_Askey
Para extraer los archivos necesitamos la herramienta en https://github.com/nlitsme/ubidump (esta fue la primera que me funcionó).
Al ejecutar binwalk sobre el archivo obtenemos:
$ 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
No necesitamos el sistema de archivos JFFS2, solo la parte UBI. Para ello podemos cortar el archivo usando dd.
Para eso necesitamos la dimensión total y el punto de inicio de UBI.
La dimensión del archivo se puede obtener 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
El valor 57147506 es el que necesitamos.
Observando la salida de binwalk podemos ver que la parte UBI comienza en 3276894. Con estos dos valores podemos usar dd de la siguiente manera:
$ dd if=0.00.89_FW_200_Askey of=0.00.89_FW_200_Askey.UBI bs=1 skip=3276894 count=57147506
Ahora podemos usar ubidump (después de cierta configuración inicial dependiendo de la configuración del 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
Ahora tenemos el sistema de archivos en bump/rootfs_ubifs/
Ahora podemos avanzar un poco más en el análisis.
Al explorar el sistema de archivos podemos ver que la interfaz web está gestionada por 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
Y podemos ver que mini_httpd depende de un archivo .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,
Entonces, usando Ghidra, podemos empezar a buscar en status.cgi
Omitiendo muchos detalles, la vulnerabilidad se describe a continuación.
Lo primero que hay que buscar es un método main como el de esta imagen:

Podemos ver que se llama a setup. Después de buscar un poco, run_service(...) parece ser el núcleo de todas las funcionalidades.

Buscando &service_tab podemos ver que algunas cadenas específicas están vinculadas al código real ejecutado por run_service(...)

Después de revisar estos métodos uno por uno, me topé con usb_remove

En detalle:
mountVal = (char *)find_val(httpparams,"mount");
Toma la cadena de la petición de la URL directamente en una variable
sprintf((char *)&mountLocation,"/mnt/%s",mountVal);
Crea una ruta completa a partir del parámetro en la petición de la URL
mountLocation
mountLocation se usa directamente en un comando
Ahora bien, hay comprobaciones para prevenir la inyección de comandos. En particular:
invalidChars = 0x60273b7c;
Se usa para comprobar algunos caracteres no válidos como ` ' ; | pero & no está presente en la lista, por lo que si el parámetro mount es igual a &ping -c 10 192.168.1.172&, la comprobación permitirá la ejecución de /statusapi/bin/umount -lf &ping -c 10 192.168.1.172&.
El comando mount fallará, pero el ping posterior se ejecutará.
El parche publicado simplifica el código eliminando las comprobaciones de caracteres especiales y limitándose a comprobar si la carpeta existe.

El código para explotar esta vulnerabilidad y habilitar SSH está en fastwebEnableSSH.py https://github.com/garis/Fastgate/blob/master/fastwebEnableSSH.py