【固件分析】DIR-823G 漏洞学习

参考链接:

看了篇论文,就想试试分析一个具体的例子。

准备工作

下载固件 DIR-823G: https://gitee.com/hac425/blog_data/blob/master/iot/DIR823GA1_FW102B03.bin

安装分析工具 **Firmware Analysis Toolkit **:

1
2
3
git clone https://github.com/attify/firmware-analysis-toolkit
cd firmware-analysis-toolkit
./setup.sh

这个装完之后 binwalk,qemu 什么的都有了。

然后用 binwalk 对固件解包,可以看到比如文件系统之类的东西。

1
binwalk -Me DIR823GA1_FW102B03.bin

Firmware Analysis Toolkit 提供了模拟执行固件的入口。在 firmware-analysis-toolkit 目录中有一个 fat.config 文件,需要填写 sudo 的密码:

Untitled

1
./fat.py  ../DIR823GA1_FW102B03.bin

Untitled

可以看到网络接口和镜像都准备好了,我们可以通过访问 192.168.0.1 来管理路由器了。

Untitled

每次运行 fat.py 启动固件时,似乎都是完全新建了硬盘镜像和网络接口。简单查看了 fat.py 脚本,它是 firmadyne 的一层 wrapper。在 firmware-analysis-toolkit/firmadyne/scratch/ 目录下保存了每次运行 fat.py 时的工程:

1
2
ubuntu@ubuntu:~/Desktop/firmware-analysis-toolkit/firmadyne/scratch/2$ ls
image image.raw qemu.final.serial.log qemu.initial.serial.log run.sh

直接运行这个 run.sh 就可以启动固件了。

固件漏洞分析

squashfs 文件系统根目录下有 /init -> /bin/init -> /bin/busybox,所以 squashfs 其实和 busybox 有关系?

/etc/init.d/rcS 中启动了 goahead 进程,是一个开源的 Web 服务器。

1
2
3
4
5
6
7
... ...
#boa

goahead &

#Turn off the power led of orange
... ...

这里在启动 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,也许是版本差别比较大吧)

text
1
route uri=/cgi-bin dir=/apps/goahead/web  handler=cgi

说明 goahead 是可以指定 cgi 目录的。/web_mtn/cgi-bin 目录下就有若干个 CGI 程序:

text
1
2
ubuntu@ubuntu:~/Desktop/_DIR823GA1_FW102B03.bin.extracted/squashfs-root/web_mtn/cgi-bin$ ls
ExportSettings.sh GetDownLoadSyslog.sh upload_firmware.cgi upload_settings.cgi

其中 ExportSettings.sh 的内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/sh


#output HTTP header
now_day=`date +%Y%m%d`
echo "Pragma: no-cache\n"
echo "Cache-control: no-cache\n"
echo "Content-type: application/octet-stream"
echo "Content-Transfer-Encoding: binary" # "\n" make Un*x happy
echo "Content-Disposition: attachment; filename=\"$1-$2-${now_day}-backup.dat\""
echo ""

#echo "Default_2860"
#ralink_init show 2860 2>/dev/null
#echo "Default_rtdev"
#ralink_init show rtdev 2>/dev/null

/bin/saveConfigToFile
cat /var/config.dat 2>/dev/null

可以看到,最终调用 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
2
3
4
5
6
7
8
9
10
static void buy(Webs *wp)
{
websSetStatus(wp, 200);
websWriteHeaders(wp, 0, 0);
websWriteEndHeaders(wp);
websWrite(wp, "Name %s", websGetVar(wp, "name", ""));
websWrite(wp, "Age %s", websGetVar(wp, "age", ""));
websDone(wp);
}
websDefineAction("buy", buy);

首先定义了一个函数 buy,参数 wp 可以简单理解为包含了用户的请求信息,例如 cookie 等。buy 函数负责处理用户的请求,返回对应的信息。然后通过 websDefineAction() 函数来注册路径 /buy ,这样用户就可以通过访问 /action/buy?name=John&item=candy 来使用 buy 函数了。

其实就像是 python-flask 里写 @app.route('/index') ,这样访问 /index 就会直接调用这个被装饰的函数了。感觉道理是一样的。

固件使用的 goahead 版本是旧版本,叫做 goforms。其实和 goactions 差不多。

路由器的登录界面长这样:

Untitled

burpsuite 抓包,发现是在给 /HNAP1 发数据,登录信息以 XML 的形式发送。

Untitled

Untitled

接下来分析 HNAP1 是怎么处理登录请求的:

先找到字符串 HNAP1,然后定位对应引用的代码:

Untitled

所以这个 sub_40B1F4 就是 websUrlHandlerDefine 函数。(虽然我也不知道这个是怎么确定的)。MIPS 函数调用的前四个参数保存在 a0~a3 中,超出部分的参数保存在栈中。第四个参数 a3 中保存的应该就是 HNAP1 的处理函数。即:addiu $a3, $v0, (sub_42383C - 0x420000)

计算出的 HNAP1 的地址为:(0x42<<16+0x42383c-0x420000)=0x42383c。

查看该函数,发现了一个命令行注入漏洞:

Untitled

先用 sprintf 生成命令行字符串,然后调用 system 函数执行。接下来的问题就是找到这个 %s 来自谁。这里原作者提到“通过运行过程查看日志文件,可以猜测出来( %s 里保存的就是 HTTP 报文)”,我其实很好奇是怎么查看日志文件的。

使用 burp 发一个包,实现命令行注入:

Untitled

Untitled

总结

从复现的角度来说成功了。但是还有一些待解决的问题:

  1. 固件运行环境搭建:

    完完全全依赖 fat 搭建运行环境。fat 已经编写好 qemu 的启动命令,创建网桥,我只需要运行 run.sh 即可。但我并不知道具体是如何模拟执行的。也许我不能太依赖 fat。

  2. 漏洞分析过程:

    漏洞主要出现在固件程序提供的服务上,也就是 goahead 服务器。能够找到漏洞需要对 goahead 本身有一定了解。但总体上还是以 system 函数的调用点作为突破口的。针对路由器类型的固件程序,可能分析 Web 服务器还是最主要的。不过这个倒不是重点。

  3. 额外的分析手段:

    在分析 goahead 时我就在考虑,我能否进入这个 qemu 模拟的环境,获得一个 shell,甚至使用 gdb 调试 goahead 程序。这也许能对分析固件程序有帮助。

  4. Linux 的运行机制:

    我对 Linux 系统的启动知之甚少,需要好好补习。