固件,即烧录进芯片的嵌入式程序,通常包含bootloader、内核以及文件系统。因其一般不容易更改,我们称之为“固件”。固件可以通过硬件手段获取,也可以在网上下载各种嵌入式产品的固件。但是更多时候,我们手中没有相应的设备。也就是说,获取各种固件成本低廉,而获取产品则需要花费一定财力。
对固件的仿真需求由此出现,firmadyne是诞生于2016年的一个优秀开源固件分析工具,可以较好的完成常见路由器固件的模拟。但是其仿真流程较为繁琐,后来有相应的开源项目firmware-analysis-toolkit整合了仿真流程,进行了简化。但其环境依赖存在较多问题,在不同的linux发行版上,会遇到各种奇奇怪怪的问题,可能需要耗费很长时间才能将工具的环境搭建好。
在这种情况下,笔者整合和修改了部分代码,构建了一个新的开源仿真平台 —— firmware-analysis-plus。使用该工具,可以进一步缩减搭建环境耗费的时间,让我们的精力进一步聚焦于各种路由器仿真的定制以及漏洞挖掘。
0x10 firmware-analysis-plus
上游项目支持:binwalk、firmadyne、firmware-analysis-toolkit
firmware-analysis-plus(FAP)主要用于常见路由器固件的仿真,可以进行固件的安全测试。感谢以下开源项目:binwalk提供优秀的固件提取 API,firmadyne提供优秀的固件仿真核心支持,firmware-analysis-toolkit提供简化流程的思想。

FAP只是站在巨人的肩膀上,做出改进和定制,提供一个更加高效的仿真平台。包括精简不必要组件,优化仿真流程,优化网络环境大幅压缩安装时间,修复若干bug,一键仿真固件。其原理主要包括两点
qemu提供多种架构指令的模拟,使用预先编译好的内核启动固件中的核心业务多数嵌入式设备含有一个
nvram芯片,保存一些重要的配置信息,firmadyne实现一个新的libnvram.so库文件,通过代码模拟固件启动时加载nvram配置信息的行为。
| FAP 版本 | python 版本 | 支持系统 | 安装方法 |
|---|---|---|---|
| v0.1 | python2、python3 | Ubuntu16.04、Ubuntu 18.04、kali 2020.02 | FAP v0.1 版本手册 |
| v1.0 | python2、python3 | Beta | Beta |
| v2.0 | python3 | kali 2020.04(不支持 Ubuntu 20.04,其他未测试) | 如下所示 |
0x11 安装 binwalk
以编译源码的方式安装binwalk,时至今日,binwalk构建脚本中的诸多依赖已无法正常安装,于是自己fork了一份新的binwalk,进行了修改。关于修改细节的描述,可参考:https://github.com/liyansong2018/binwalk
git clone https://github.com/liyansong2018/binwalk.git cd binwalk ./deps.sh sudo python3 setup.py install
0x12 安装 FAP
git clone https://github.com/liyansong2018/firmware-analysis-plus.git cd firmware-analysis-plus ./setup.sh
0x13 配置
修改fat.config文件中的密码,改为root系统用户的密码
0x14 运行
运行fat.py脚本,即可对相应的固件进行模拟
./fat.py -q ./2.5.0/ ./testcases/wnap320_V3.7.11.4_firmware.tar
结果
┌──(lys㉿kali)-[~/Tools/firmware-analysis-plus] └─$ ./fat.py -q ./2.5.0/ ./testcases/wnap320_V3.7.11.4_firmware.tar ______ _ ___ | ___| (_) / _ / | |_ _ _ __ ___ / /_/ / _ __ ___ | _| | | | '_ ` _ / | _ | | '_ / / __| ++ | | | | | | | | | | | | | | | | | | /__ / /_| |_| |_| |_| |_| /_| |_/ |_| |_| |___/ Welcome to the Firmware Analysis Plus - v2.0 By lys - https://blog.csdn.net/song_lee | @liyansong [+] Firmware: wnap320_V3.7.11.4_firmware.tar [+] Extracting the firmware... [+] Image ID: 1 [+] Identifying architecture... [+] Architecture: mipseb [+] Building QEMU disk image... [+] Setting up the network connection, please standby... [+] Network interfaces: [('brtrunk', '192.168.0.100')] [+] Using qemu-system-mips from /home/lys/Tools/firmware-analysis-plus/qemu-builds/2.5.0 [+] All set! Press ENTER to run the firmware... [+] When running, press Ctrl + A X to terminate qemu
通过回车键即可模拟运行目标固件,通过提供的网络接口,打开相应的路由器管理页面

0x15 重置
如果仿真次数过多,会生成较多的中间文件,直接运行reset.py删除即可
./reset.py
0x20 定制
上游的firmadyne已经设定好了一些规则,在不对源码进行修改的情况下,可以仿真以下版本的固件
wnap320_V3.7.11.4_firmware.tar
DIR-601_REVB_FIRMWARE_2.01.BIN
DIR890A1_FW103b07.bin
...
但更多时候,我们想要模拟的程序往往不在上述列表中,这就需要考虑对firmaydne进行定制,其实就是对libnvram的定制。不同设备芯片上的配置信息是不一样的,固件在启动过程中,所读取的硬件信息也是不一样的,固件在启动之初,会依靠libnvram.so提供的函数,读取(如flash)芯片上的内容。而在实际环境中,我们没有该芯片,所以就要靠定制libnvram,预先设定好一些参数,这样就达到模拟真实硬件的目的。
firmadyne提供的libnvram源码结构如下
firmware-analysis-plus/firmadyne/sources/libnvram$ tree . ├── alias.c # 一些libnvram库函数的别名 ├── alias.h ├── config.h # 预先设定好的nvram配置信息 ├── LICENSE.txt ├── Makefile ├── nvram.c # libnvram库函数 ├── nvram.h # 要初始化的所有key/value ├── README.md └── test.c
在仿真不同固件时,往往需要对上述代码进行定制,下面重点讲解几种常用方法。
0x21 设置固件封装的库函数别名
固件通常使用libvram.so提供的库函数,实现对硬件存储的配置信息的访问和修改
nvram_get,顾名思义,从内核的NVRAM模块获取值,其实就是从芯片中获取某个键对应的值,例如获取LAN_MAC对应的mac地址
nvram_set,与上述 API 相反,给硬件设置一个键值对
nvram_clear,通过写入1,覆盖所有的键对应的值
libnvram.so提供的函数还有很多,功能都是类似的。
但是,在一些固件中,往往会封装自己的API,以 Tenda AC 15为例,文件系统中的libCfm.so实现了自己的nvram库函数

而firmadyne只是实现了了libnvram.so标准库函数的hook,因此我们需要将目标固件封装的一些函数添加到firmadyne源码中
在alias.c源码中添加如下代码
/* Tenda AC15 */ int bcm_nvram_set(const char *key, const char *val) __attribute__ ((alias ("nvram_set"))); char *bcm_nvram_get(const char *key) __attribute__ ((alias ("nvram_get"))); int bcm_nvram_init(void) __attribute__ ((alias ("nvram_init"))); // ...
0x22 预先设置固件需要的 nvram 键值对
config.h文件已经包含作者设定好的一些固件需要的键值对
// Default values for NVRAM. #define NVRAM_DEFAULTS / /* Linux kernel log level, used by "WRT54G3G_2.11.05_ETSI_code.bin" (305) */ / ENTRY("console_loglevel", nvram_set, "7") / /* Reset NVRAM to default at bootup, used by "WNR3500v2-V1.0.2.10_23.0.70NA.chk" (1018) */ / ENTRY("restore_defaults", nvram_set, "1") / ENTRY("sku_name", nvram_set, "") / ENTRY("wla_wlanstate", nvram_set, "") / ENTRY("lan_if", nvram_set, "br0") / ENTRY("lan_ipaddr", nvram_set, "192.168.0.50") / ENTRY("lan_bipaddr", nvram_set, "192.168.0.255") / ENTRY("lan_netmask", nvram_set, "255.255.255.0") /
但是我们的目标固件往往是没有包含在其中的,因此需要手动添加。一般来说包含一下两种情况
一、firmadyne错误日志(./firmadyne/scratch/1/qemu.initial.serial.log)直接提供。这种情况比较简单,直接根据错误日志直接找到缺少的键值对,如下所示

直接在config.h中添加键值
/* Tenda AC15 */ / ENTRY("default_nvram", nvram_set, "1") / ENTRY("image_boot", nvram_set, "0")
二、更多时候,没有办法直接从错误日志中直接找到问题所在,那就只能分析相应的二进制,如下所示是 DIR-2640固件在仿真中产生的错误日志

根据打印的一些错误日志和调用栈,找到相应的错误代码

也就是说,缺少factory_mode键对应的值,同样的,增加键值到config.h中即可。
0x23 其他函数拦截
某些固件可能还会遇到一些其他函数带来的错误,导致固件运行失败,遇到这种情况,可以使用IDA给相应的二进制打patch,绕过该函数,不过这种方式需要重新打包固件。好在firmadyne也考虑到此类情况,可以直接hook普通函数。
例如在 AC15 中,加载如下函数遇到错误,导致malloc失败

跟踪该函数,发现该函数的目的是读取某些mtd设备的大小,很明显,我们是没有实际设备

因此需要hook该函数,返回一个恰当值,在alias.c添加相应函数的定义
int get_cfm_blk_size_from_cache(){ return 0x20000; }
0x24 编译
修改后的libnvram需要编译,拷贝到相应位置,firmadyne提供了交叉编译工具
# arm cd ./firmadyne/sources/console make clean && CC=/opt/cross/arm-linux-musleabi/bin/arm-linux-musleabi-gcc make && mv console ../../binaries/console.armel # mipseb make clean && CC=/opt/cross/mipseb-linux-musl/bin/mipseb-linux-musl-gcc make && mv console ../../binaries/console.mipseb # mipsel make clean && CC=/opt/cross/mipsel-linux-musl/bin/mipsel-linux-musl-gcc make && mv console ../../binaries/console.mipsel
这样就完成了libnvram的定制。
0x30 总结
固件仿真不是一蹴而就的,无论是 FAP还是 firmadyne,只是为我们提供了一个框架,预置代码支持的固件类型和版本有限。尝试运行不同厂商的固件往往会遇到各种奇奇怪怪的问题,需要根据 firmadyne提供的崩溃日志,定位到出错的代码,分析崩溃原因,定制libnvram。这是一个艰难而漫长的过程,但是在这个过程中,可以加深对目标固件业务的理解,并且一旦仿真成功,我们可以复现各种已知CVE,甚至可以很容易挖掘到各种0day漏洞。

北京市 1F
这个工具挺实用的,之前装firmadyne折腾了好久
浙江省 2F
mipsel架构跑起来延迟有点高,批量测试可能不太稳🤔
广东省广州市 3F
手动改密码真的烦,能不能读个配置文件或者环境变量啊
浙江省温州市 4F
前几天刚折腾DIR-2640,factory_mode设了还得补一堆其他键值
日本 5F
有谁在Ubuntu20.04上成功运行过吗?我这边一直报错
澳大利亚 6F
mipseb架构的支持怎么样?测试过性能吗?
印度 7F
之前用firmware-analysis-toolkit确实依赖问题很多,这个改进很及时
印度尼西亚 8F
要是能支持更多设备型号就好了
河南省郑州市 9F
libnvram定制那块讲得有点复杂,新手可能看不懂
陕西省西安市 B1
@ 月影残风 定制部分确实需要一定基础,建议先熟悉下基本原理
浙江省嘉兴市 10F
搞这个主要是为了漏洞挖掘吧?
韩国 11F
试了下AC15的仿真,卡在bcm_nvram_init那里了
江苏省镇江市 B1
@ 绣娘喻 AC15那个bcm_nvram_init问题我也遇到了,后来改了alias.c才解决
江苏省无锡市 12F
这种工具对物联网安全研究帮助很大
北京市 B1
@ DreamyCloud 这个对物联网安全研究确实帮助很大,能模拟真实环境
澳大利亚 13F
希望作者能出个视频教程,文字看着有点累
贵州省黔南州瓮安县 14F
配置密码那里能不能自动获取?每次都手动改有点麻烦
湖北省武汉市 15F
这工具安装比firmadyne简单多了,终于不用折腾环境了
北京市 16F
有人测试过DIR-2640吗?想知道定制难度大不大
福建省龙岩市 17F
搞物联网安全的必备工具啊,收藏了🤓
韩国 18F
之前用原版遇到各种依赖问题,这个改进很及时
天津市 19F
新手表示看到libnvram定制那里有点懵
江苏省常州市 20F
mipseb架构性能如何?跑起来卡不卡?
印度 21F
要是能自动识别缺失的nvram键值就更完美了
韩国 22F
这个在kali2020上运行稳吗?
江苏省常州市 23F
定制libnvram确实是个技术活,得慢慢摸索
台湾省 24F
之前装firmadyne折腾了好久,这个看起来方便不少
江苏省扬州市 25F
Ubuntu20.04不支持啊,可惜了,还在用老版本
台湾省 26F
mipsel架构的测试有谁做过吗?性能损耗大不大?
重庆市 27F
libnvram定制那块确实需要点耐心,多试几次就懂了
韩国 28F
这个对挖路由器漏洞帮助大吗?
韩国 29F
感觉步骤写得挺详细的,照着做应该问题不大
辽宁省抚顺市 30F
要是能出个常见固件仿真成功的列表就更好了
日本 31F
手动改密码确实有点烦,不过就一次还好
湖南省衡阳市 32F
之前搞过类似的,折腾环境就花了两天,这个省时间