这是一份关于在玩 Fastweb 的 Fastgate 调制解调器/路由器和 Ghidra 时发现的一个漏洞(CVE-2019-12489)的简要报告。 该漏洞允许通过 HTTP 请求执行命令注入,并可被用来启用 SSH shell。
所有固件文件都位于 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/ 中有了文件系统。
现在我们可以进一步进行分析。
在文件系统中查看,我们可以看到 Web 图形界面由 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