【固件分析】DIR-823G 漏洞学习
参考链接:
看了篇论文,就想试试分析一个具体的例子。
准备工作
下载固件 DIR-823G: https://gitee.com/hac425/blog_data/blob/master/iot/DIR823GA1_FW102B03.bin
安装分析工具 **Firmware Analysis Toolkit **:
1 | git clone https://github.com/attify/firmware-analysis-toolkit |
这个装完之后 binwalk,qemu 什么的都有了。
然后用 binwalk 对固件解包,可以看到比如文件系统之类的东西。
1 | binwalk -Me DIR823GA1_FW102B03.bin |
Firmware Analysis Toolkit 提供了模拟执行固件的入口。在 firmware-analysis-toolkit 目录中有一个 fat.config 文件,需要填写 sudo 的密码:
1 | ./fat.py ../DIR823GA1_FW102B03.bin |
可以看到网络接口和镜像都准备好了,我们可以通过访问 192.168.0.1 来管理路由器了。
每次运行 fat.py 启动固件时,似乎都是完全新建了硬盘镜像和网络接口。简单查看了 fat.py 脚本,它是 firmadyne 的一层 wrapper。在 firmware-analysis-toolkit/firmadyne/scratch/ 目录下保存了每次运行 fat.py 时的工程:
1 | ubuntu@ubuntu:~/Desktop/firmware-analysis-toolkit/firmadyne/scratch/2$ ls |
直接运行这个 run.sh 就可以启动固件了。
固件漏洞分析
squashfs 文件系统根目录下有 /init -> /bin/init -> /bin/busybox,所以 squashfs 其实和 busybox 有关系?
/etc/init.d/rcS
中启动了 goahead 进程,是一个开源的 Web 服务器。
1 | ... ... |
这里在启动 goahead 的时候没有任何参数。那它是如何指定目录的?默认目录和默认配置文件是什么?我都没有找到。
总之博客里说,web 目录是 /web_mtn 。
CGI 的未授权访问
参考链接:当我们在谈到cgi的时候,我们在讨论什么
最早的Web服务器简单地响应浏览器发来的HTTP请求,并将存储在服务器上的HTML文件返回给浏览器,也就是静态html。事物总是不断发展,网站也越来越复杂,所以出现动态技术。但是服务器并不能直接运行 php,asp这样的文件,自己不能做,外包给别人吧,但是要与第三做个约定,我给你什么,然后你给我什么,就是握把请求参数发送给你,然后我接收你的处理结果给客户端。那这个约定就是 common gateway interface,简称cgi。这个协议可以用vb,c,php,python 来实现。cgi只是接口协议,根本不是什么语言。
根据 GoAhead下的CGI程序使用说明 中提到的,在 route.txt 中可以设置:(虽然这个固件中也没找到 route.txt,也许是版本差别比较大吧)
1 | route uri=/cgi-bin dir=/apps/goahead/web handler=cgi |
说明 goahead 是可以指定 cgi 目录的。/web_mtn/cgi-bin 目录下就有若干个 CGI 程序:
1 | ubuntu@ubuntu:~/Desktop/_DIR823GA1_FW102B03.bin.extracted/squashfs-root/web_mtn/cgi-bin$ ls |
其中 ExportSettings.sh 的内容如下:
1 | !/bin/sh |
可以看到,最终调用 cat 打印配置文件的结果。和前文提到的从标准输入/环境变量输入,从标准输出流输出的方式匹配。ExportSettings.sh 和 GetDownLoadSyslog.sh 是 shell 脚本,而 upload_firmware.cgi upload_settings.cgi 则应该是可执行文件。
文中提到对 cgi 的使用没有限制权限,因此存在越权问题。这里不细说了。
自定义请求处理函数的漏洞
goahead 源码在这里。
1 | wget https://s3.amazonaws.com/embedthis.public/goahead-5.2.0-src.tgz |
官方文档介绍了 GoActions :
在使用 CGI 时,需要启动一个新的进程来执行对应的操作。然而对于嵌入式设备来说这样的开销可能过大了。Goahead 提供了一个更高性能的替代者,即 GoActions 。GoActions 是 C 语言的函数,它和某个特定的 URI 绑定,负责响应客户的请求,但不必为每个请求创建一个新的进程。看下面的例子:
1 | static void buy(Webs *wp) |
首先定义了一个函数 buy,参数 wp 可以简单理解为包含了用户的请求信息,例如 cookie 等。buy 函数负责处理用户的请求,返回对应的信息。然后通过 websDefineAction()
函数来注册路径 /buy ,这样用户就可以通过访问 /action/buy?name=John&item=candy
来使用 buy 函数了。
其实就像是 python-flask 里写
@app.route('/index')
,这样访问 /index 就会直接调用这个被装饰的函数了。感觉道理是一样的。
固件使用的 goahead 版本是旧版本,叫做 goforms。其实和 goactions 差不多。
路由器的登录界面长这样:
burpsuite 抓包,发现是在给 /HNAP1 发数据,登录信息以 XML 的形式发送。
接下来分析 HNAP1 是怎么处理登录请求的:
先找到字符串 HNAP1,然后定位对应引用的代码:
所以这个 sub_40B1F4 就是 websUrlHandlerDefine 函数。(虽然我也不知道这个是怎么确定的)。MIPS 函数调用的前四个参数保存在 a0~a3 中,超出部分的参数保存在栈中。第四个参数 a3 中保存的应该就是 HNAP1 的处理函数。即:addiu $a3, $v0, (sub_42383C - 0x420000)
。
计算出的 HNAP1 的地址为:(0x42<<16+0x42383c-0x420000)=0x42383c。
查看该函数,发现了一个命令行注入漏洞:
先用 sprintf 生成命令行字符串,然后调用 system 函数执行。接下来的问题就是找到这个 %s 来自谁。这里原作者提到“通过运行过程查看日志文件,可以猜测出来( %s 里保存的就是 HTTP 报文)”,我其实很好奇是怎么查看日志文件的。
使用 burp 发一个包,实现命令行注入:
总结
从复现的角度来说成功了。但是还有一些待解决的问题:
固件运行环境搭建:
完完全全依赖 fat 搭建运行环境。fat 已经编写好 qemu 的启动命令,创建网桥,我只需要运行 run.sh 即可。但我并不知道具体是如何模拟执行的。也许我不能太依赖 fat。
漏洞分析过程:
漏洞主要出现在固件程序提供的服务上,也就是 goahead 服务器。能够找到漏洞需要对 goahead 本身有一定了解。但总体上还是以 system 函数的调用点作为突破口的。针对路由器类型的固件程序,可能分析 Web 服务器还是最主要的。不过这个倒不是重点。
额外的分析手段:
在分析 goahead 时我就在考虑,我能否进入这个 qemu 模拟的环境,获得一个 shell,甚至使用 gdb 调试 goahead 程序。这也许能对分析固件程序有帮助。
Linux 的运行机制:
我对 Linux 系统的启动知之甚少,需要好好补习。