固件模拟 Case Study (1)

Catalpa 网络安全爱好者

当分析物联网设备固件时,为了验证漏洞是否存在,可能需要对固件进行模拟,目前对于固件模拟比较好的开源解决方案是 firmadyne,但在实际使用 firmadyne 的过程中会遇到很多问题,这篇文章简单总结一下使用 firmadyne 可能会遇到的问题,以及部分解决方案。

关于 firmadyne 的原理

firmadyne 有一篇相关论文 ,主要作者来自 CMU,这篇论文详细介绍了 firmadyne 的实现思路和部分原理,推荐详细阅读一下,我在这里只做简单介绍。

整个框架有 4 部分组件:

  1. 爬虫组件:从各大厂商特定的固件服务器上面爬取不同设备、不同版本的固件,并下载到本地存储。
  2. 解压组件:通常固件会经过某种手段压缩为镜像格式文件,此组件主要用于将镜像还原为标准的文件系统。
  3. 模拟组件:利用 qemu 模拟成功还原的固件,构造合适的虚拟硬件环境以支持固件运行。
  4. 动态分析组件:利用已知的 poc 或 fuzz 等技术对成功模拟的固件进行安全测试,将发现的漏洞等存档。

我们在实际分析过程中常用的是第二和第三个组件。

解压组件主要用到的还是 binwalk 的 api,但是作者人为的对解压策略进行了限制和优化。

在最主要的模拟组件中,作者提到了一个关键问题:对 NVRAM 的支持。大部分路由器、交换机等类似设备硬件上都有一片称为 nvram 的存储芯片,其内部保存了一些重要配置信息,当设备启动的时候,系统会尝试从 nvram 中读取这些信息。

然而 QEMU 所提供的虚拟化环境默认并不包括这一硬件设备,所以我们用 qemu + chroot 等手段启动固件的时候经常会看到有关于 nvram 缺失的提示信息。

firmadyne 自己实现了一个 libnvram.so 库文件,里面模拟了很多和访问 nvram 有关的 API,通过代码直接限制程序读取 nvram 的行为,这样一来就可以达到“欺骗“固件的目的。

另外一个关键问题是网络的配置,成功生成一个固件镜像之后,firmadyne 会启动这个镜像,持续约 60 秒,作者称之为”学习阶段“,在此阶段,框架会分析镜像启动过程中留下的日志文件,在里面获取和网卡、vlan、网路配置有关的信息,之后利用 qemu 的 TAP 模式,在宿主机创建虚拟网卡,然后指定给 qemu 用于主机和虚拟机之间的通信。

不过这个模拟网络的策略存在一些问题,我们后文再说。

firmadyne 的安装

firmadyne 项目历史比较悠久了,在 github 上面看到某些文件的 commit 在 4 年前,加上作者在 issue 中提到自己的时间比较少,未来不会投入大量精力维护这个项目,导致目前 firmadyne 和其依赖软件(比如 qemu、binwalk 等)之间存在各种兼容性问题,在安装的时候要格外注意。

安装的具体教程在百度和 github 都有详细的介绍,这里不再赘述,只提一下需要注意的要点。

  1. 关于安装系统:推荐 Ubuntu 14.04,这也是作者推荐的版本,考虑到此项目时间久远,在新的系统上面可能会出现各种兼容问题。
  2. 关于 QEMU:最好不要从源代码编译,另外不要使用最新版本,用 apt 安装即可,原因是 firmadyne 中在启动镜像阶段会用到某些和 vlan 有关的启动参数,这些参数在 QEMU 3.0 版本中已经移除,并且新版存在很多 API 上的变化,很可能出现模拟不成功、参数错误等棘手的问题。
  3. 关于 binwalk:按照提示安装好之后,最好找一个固件测试一下(github 给出的测试固件即可),因为 binwalk 在解压固件的时候会依赖某些插件(比如 python-LZMA),当插件缺失的时候解压将失败。
  4. 关于数据库:严格按照提示的配置步骤来,特别注意密码是否正确(firmadyne)。
  5. 别忘了安装完成还需要手动修改 config 文件头部的路径。
  6. 使用 git clone 直接克隆仓库代码,不要使用旧版本的代码。

另外有一个名为 FAT 的项目可用于简化 firmadyne 操作。

可能遇到的问题以及一些注意事项

配置文件问题,firmadyne 安装完成,初次使用的时候需要修改 config 配置,忘记修改会出现变量未定义的错误。

密码问题,数据库默认密码为 firmadyne,如果配置的时候不小心输入错误会导致后续操作出现异常。

依赖组件版本问题,前面提到 firmadyne 对各依赖工具存在版本限制,在安装过程中尽量按照 GitHub 提示进行,不要自己选择版本。

网络问题,在配置网络这一步会出现很多异常情况,首先要排查这种异常是不是由其他软件造成的,推荐将 firmadyne 安装在新的虚拟机系统上面,防止软件之间产生冲突。检查当前版本是否最新,虽然作者不愿意对框架进行大幅度的改进,但是对于某些小问题还是做出了修补。

如果重新安装框架问题还是得不到解决,那么很可能是该固件内部存在某些特殊操作,firmadyne 毕竟是一款通用框架,对于一小部分”不走寻常路“的固件可能需要我们自己分析异常,进行修改。

除上面提到的,在 GitHub 的 issue 包括了很多大神的讨论,一般来说我们遇到的问题都可以在 issue 上面找到答案。

AttifyOS

github 上能找到这个虚拟机的下载地址 https://github.com/adi0x90/attifyos

说白了,它就是包含了一些分析工具的虚拟机,大神已经安装测试完毕,我们下载即可使用。attifyos 内置了 firmadyne,并且已经配置了 FAT,最简单的使用方式:把镜像拷贝到虚拟机,然后 python FAT.py <镜像名字> 即可。

如果你不想手动安装工具的话,可以直接下载 attifyos。但虚拟机里面的工具版本比较低,模拟时会出现很多问题,我个人推荐先把 firmadyne 更新到新版

固件模拟实例

这里我记录一下最近分析的一款路由器固件 DIR-846 的模拟过程。

固件可以在 dlink 官网下载到,先用 binwalk 看一下固件的内容:

很标准的格式,直接用 binwalk -Me 即可解压出来。

那么可以直接丢进 firmadyne 尝试模拟,如果你是新安装的系统和工具,先拍摄一个快照,命名为原始快照。

镜像丢到 firmadyne 目录下,执行命令解压固件

1
./sources/extractor/extractor.py -b Dlink -sql 127.0.0.1 -np -nk "DIR846A1_FW100A35.bin" images

此时你可以在 image 目录下找到刚刚生成的 1.tar.gz,请修改压缩包内的 /etc/shadow 文件,替换 root 密码为你知道的密码(例如从自己的机器上拷贝一份)。

执行命令识别固件架构

1
./scripts/getArch.sh ./images/1.tar.gz

然后创建数据库

1
./scripts/tar2db.py -i 1 -f ./images/1.tar.gz

制作镜像

1
sudo ./scripts/makeImage.sh 1

配置网络

1
./scripts/inferNetwork.sh 1

此时我遇到了问题:

1
Warning: Unmatched interface: (['br-lan',192.168.0.1,null,null])

继续向下执行

1
./scratch/1/run.sh

系统能正常跑起来,但是经过测试发现没办法访问到虚拟机内的路由器管理页面。

我在网上找了很多文章,但是都没解决问题,起初怀疑是 firmadyne 安装有问题,使用官方提供的例子固件再次测试发现并不报错,进入系统之后利用 ifconfig 查看网卡结构发现 DIR-846 固件只开启了 br-lan 网卡接口。

百度一下我找到了文章 https://blog.csdn.net/ai2000ai/article/details/79077506,文章简单介绍了各个网卡的基本含义,其中的一张图片很棒:

这个是华硕很早的一款网络设备,采用 openwrt 的 SoC。

目前市面上多数路由器网卡结构和上图类似,eth0 是实际的物理网卡,其向下会分别虚拟出 lan 口和 wan 口,另外无线部分也很会区分 2.4G 和 5G 等等。

br-lan 是虚拟设备,用于 LAN 口的桥接。简而言之,我们的 846 固件默认只开启了一个虚拟接口(br-lan),甚至连环回接口都没有开启,协议栈并没有正常工作。

找到问题的原因了,尝试手动开启各个网络接口并分配 IP 地址。再次访问还是不行。

又查找了一些资料,发现可能是 firmadyne 的网络配置策略出现问题,在它自动生成的脚本 run.sh 中会创建 tap 虚拟网卡并创建通往 QEMU 虚拟机的路由,但由于固件内部并没有自动启用网卡,这条路由无效,就算手动配置也无济于事。

于是 修改 run.sh 脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/bin/bash

set -u

ARCHEND=mipsel
IID=1

if [ -e ./firmadyne.config ]; then
source ./firmadyne.config
elif [ -e ../firmadyne.config ]; then
source ../firmadyne.config
elif [ -e ../../firmadyne.config ]; then
source ../../firmadyne.config
else
echo "Error: Could not find 'firmadyne.config'!"
exit 1
fi

IMAGE=`get_fs ${IID}`
KERNEL=`get_kernel ${ARCHEND}`
QEMU=`get_qemu ${ARCHEND}`
QEMU_MACHINE=`get_qemu_machine ${ARCHEND}`
QEMU_ROOTFS=`get_qemu_disk ${ARCHEND}`
WORK_DIR=`get_scratch ${IID}`

trap cleanup EXIT

echo "Starting firmware emulation... use Ctrl-a + x to exit"
sleep 1s

${QEMU} -m 256 -M ${QEMU_MACHINE} -kernel ${KERNEL} \
-drive if=ide,format=raw,file=${IMAGE} -append "root=${QEMU_ROOTFS} console=ttyS0 nandsim.parts=64,64,64,64,64,64,64,64,64,64 rdinit=/firmadyne/preInit.sh rw debug ignore_loglevel print-fatal-signals=1 user_debug=31 firmadyne.syscall=0" \
-nographic \
-net nic -net tap,ifname=tap0,script=no,downscript=no | tee ${WORK_DIR}/qemu.final.serial.log

结合之前写的 QEMU 网络配置手动创建 tap 虚拟网卡并通过网桥的方式接入 QEMU 虚拟机,然后在虚拟机中手动启用 eth0 等网卡并分配 IP 地址。

此时虚拟机中网卡如下:

本机网卡状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
br0       Link encap:Ethernet  HWaddr 00:0c:29:90:94:18  
inet addr:192.168.166.138 Bcast:192.168.166.255 Mask:255.255.255.0
inet6 addr: fe80::20c:29ff:fe90:9418/64 Scope:Link
UP BROADCAST RUNNING PROMISC MULTICAST MTU:1500 Metric:1
RX packets:43207 errors:0 dropped:0 overruns:0 frame:0
TX packets:2301 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:12217527 (12.2 MB) TX bytes:310242 (310.2 KB)

eth0 Link encap:Ethernet HWaddr 00:0c:29:90:94:18
inet addr:192.168.166.139 Bcast:192.168.166.255 Mask:255.255.255.0
inet6 addr: fe80::20c:29ff:fe90:9418/64 Scope:Link
UP BROADCAST RUNNING PROMISC MULTICAST MTU:1500 Metric:1
RX packets:286100 errors:0 dropped:0 overruns:0 frame:0
TX packets:71492 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:369617544 (369.6 MB) TX bytes:10566787 (10.5 MB)

lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:15207 errors:0 dropped:0 overruns:0 frame:0
TX packets:15207 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1
RX bytes:3129532 (3.1 MB) TX bytes:3129532 (3.1 MB)

tap0 Link encap:Ethernet HWaddr 32:4e:1d:e9:33:5a
inet6 addr: fe80::304e:1dff:fee9:335a/64 Scope:Link
UP BROADCAST RUNNING PROMISC MULTICAST MTU:1500 Metric:1
RX packets:22279 errors:0 dropped:0 overruns:0 frame:0
TX packets:20531 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:11080451 (11.0 MB) TX bytes:1821983 (1.8 MB)

到这里我们就可以成功访问 web 管理界面:

  • Title: 固件模拟 Case Study (1)
  • Author: Catalpa
  • Created at : 2020-01-08 00:00:00
  • Updated at : 2024-10-17 08:48:13
  • Link: https://wzt.ac.cn/2020/01/08/firmadyne1/
  • License: This work is licensed under CC BY-NC-SA 4.0.