
Это краткий отчет об уязвимости (CVE-2019-12489), обнаруженной при изучении модема/роутера Fastgate от Fastweb с помощью Ghidra. Уязвимость позволяет выполнить внедрение команды через HTTP-запрос и может быть использована для включения SSH-оболочки.
Все файлы прошивок находятся по адресу http://59.0.121.191:8080/ACS-server/file/FILENAME
Где FILENAME равно:
Так, например, чтобы получить последнюю прошивку, просто перейдите по адресу http://59.0.121.191:8080/ACS-server/file/0.00.89_FW_200_Askey
Для извлечения файлов нам нужна утилита из https://github.com/nlitsme/ubidump (это первая, которая заработала у меня).
Выполнение binwalk для файла дает нам:
$ 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
Нам не нужна файловая система JFFS2, только часть UBI. Для этого мы можем обрезать файл с помощью dd. Для этого нам нужен общий размер и начальная точка UBI.
Размер файла можно получить с помощью 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
Значение 57147506 — это то, что нам нужно.
Глядя на вывод binwalk, мы видим, что часть UBI начинается с 3276894. Имея эти два значения, мы можем использовать dd следующим образом:
$ dd if=0.00.89_FW_200_Askey of=0.00.89_FW_200_Askey.UBI bs=1 skip=3276894 count=57147506
Теперь мы можем использовать ubidump (после некоторой первоначальной настройки в зависимости от конфигурации системы):
$ 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
Теперь у нас есть файловая система в bump/rootfs_ubifs/
Теперь мы можем немного продвинуться в анализе.
Изучая файловую систему, мы видим, что веб-интерфейс управляется 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
И мы видим, что mini_httpd зависит от файла .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,
Итак, используя Ghidra, мы можем начать изучение status.cgi
Пропуская множество деталей, уязвимость описана ниже.
Первое, на что нужно обратить внимание, — это метод main, похожий на тот, что на этой картинке:

Мы видим, что вызывается setup. После некоторого изучения run_service(...) кажется ядром всех функций.

Ища &service_tab, мы видим, что некоторые конкретные строки связаны с реальным кодом, выполняемым run_service(...)

После просмотра этих методов один за другим я натыкаюсь на usb_remove

В деталях:
mountVal = (char *)find_val(httpparams,"mount");
Берет строку из URL-запроса напрямую в переменную
sprintf((char *)&mountLocation,"/mnt/%s",mountVal);
Создает полный путь, заданный параметром в URL-запросе
mountLocation
mountLocation напрямую используется в команде
Теперь есть проверки для предотвращения внедрения команд. В частности:
invalidChars = 0x60273b7c;
Используется для проверки некоторых недопустимых символов, таких как ` ' ; |, но & отсутствует в списке, поэтому если параметр mount равен &ping -c 10 192.168.1.172&, проверка разрешит выполнение /statusapi/bin/umount -lf &ping -c 10 192.168.1.172&.
Команда mount завершится ошибкой, но последующий ping будет выполнен.
Опубликованный патч упрощает код, устраняя проверки специальных символов, просто проверяя, существует ли папка.

Код для эксплуатации этой уязвимости и включения SSH находится в fastwebEnableSSH.py https://github.com/garis/Fastgate/blob/master/fastwebEnableSSH.py