固件FUZZ
Last updated
Last updated
对IOT设备的Fuzzing可分为协议Fuzzing和二进制Fuzzing,Fuzzing一般注重输入生成和反馈,但也根据框架原理不同而侧重点不同。协议Fuzzing关键在Monitor,二进制Fuzzing关键在执行效率。Fuzzing工具(框架)数不胜数,可参见Blackarch和Fuzzing-Survey。下以Boofuzz和AFL为例,分别阐释协议和二进制Fuzzing。
通常,在对IoT
设备的固件进行分析时,固件中与提供服务如HTTP
、Telnet
、RTSP
、UPnP
等相关的二进制程序是重点分析的对象。因为一旦在这些程序中发现漏洞,其很有可能会被远程利用,进而带来严重的安全隐患。
对固件二进制程序进行分析,常见的分析方法包括模糊测试、补丁比对、工具静态扫描和人工审计等。其中,模糊测试方法具备简单易用的特点,通常也比较有效,其在业界已被广泛使用。
下面,以某型号路由器为例,基于Boofuzz
框架,介绍对常见网络协议进行fuzz
的方法。
除了网络协议外,也可以采用类似的思路对其他协议如BLE、串口协议等进行fuzz。同时,该方法不仅局限于IoT设备,也可用于对常见的服务程序进行测试。
模糊测试采用黑盒测试的思想,通过构造大量的畸形数据作为应用程序的输入,来发现程序中可能存在的安全缺陷或漏洞。
模糊测试方法的分类有很多。根据测试用例生成方式的不同,可以分为基于变异的模糊测试和基于生成的模糊测试。根据对目标程序的理解程度,可分为黑盒模糊测试、灰盒模糊测试和白盒模糊测试。常见工具与方法的对应关系如下。
针对设备,由于其资源受限和环境受限等特点,实际中常采用黑盒模糊测试的方式。在对网络协议进行测试时,可以将常见的网络协议分为两类:一类属于文本协议,如HTTP
、FTP
等,这类协议的特点是其数据包内容都是可见字符;另一类为二进制协议,其特点是数据包内容大部分是不可见字符,这类协议在工控设备如PLC
中比较常见,通常属于私有协议。针对文本协议,笔者常采用[Sulley
](https://github.com/OpenRCE/sulley) 框架进行测试,而针对二进制协议,则常采用 [kitty](<https://github.com/cisco-sas/kitty>)
框架进行测试。Sulley
框架和kitty
框架均能够对两类协议进行测试。
另外,在对IoT
设备进行模糊测试时,需要考虑如何对设备进行监控,以判断是否出现异常。最简单的方式通过设备服务的可用性进行判断,如果设备提供的服务不可访问,表明设备可能崩溃了。但这种监控方式粒度比较粗,容易漏掉一些异常行为。另外,当设备出现异常后,还需要对环境进行恢复,以便继续进行测试。常见的方式就是重启设备。现在很多设备崩溃之后都会自动重启,如果测试目标设备没用提供这种机制,则需要采用其他方式解决。
由于Sulley
框架目前已经停止更新维护,而Boofuzz
框架是Sulley
的继承者,除了修复一些bug
之外,还增加了框架的可扩展性。
由上图可知,该框架主要包含四个部分:
数据生成
:根据协议格式利用原语来构造请求
会话管理/驱动
:将请求
以图的形式链接起来形成会话,同时管理待测目标
、代理
、请求
,还提供一个 web 界面用于监视和控制
代理
:与目标进行交互以实现日志记录、对网络流量进行监控等
通常,代理是运行在目标设备上。但是,对于IoT设备而言,大部分情况下都无法在目标设备上运行代理程序。
实用工具
:独立的命令行工具,完成一些其他的功能
其中,数据生成
和会话管理/驱动
是比较重要的2个模块。对于数据生成
模块,Boofuzz
框架提供了很多原语来定义请求
,如最基础的s_string()
、s_byte()
、s_static()
等。对于会话管理/驱动
模块,其思想体现在下图中。
在上图中,节点ehlo
、helo
、mail from
、rcpt to
、data
表示 5 个请求
,路径'ehlo'->'mail form'->'rcpt to'->'data'
和'helo'->'mail from'->'rcpt to'->data'
体现了请求
之间的先后顺序关系。callback_one()
和callback_two()
表示回调函数,当从节点echo
移动到节点mail from
时会触发该回调函数,利用这一机制,节点mail from
可以获取节点ehlo
中的一些信息。而pre_send()
和post_send()
则负责测试前的一些预处理工作和测试后的一些清理工作。
理解了这几个模块的功能后,使用该框架进行测试的主要步骤如下:
根据网络数据包构造请求
;
设置会话信息(包括测试目标的ip
地址和端口等),然后按照请求的先后顺序将其链接起来;
添加对目标设备的监控和设备重启机制等;
开始fuzz
。
以某型号路由器为例,由于路由器上HTTP
服务是最为常见的,故以http
协议为例进行介绍。
模糊测试属于动态分析技术,因此需要有真实设备,或者采用对固件进行仿真的方式。
根据网络数据包构造请求
首先,需要尽可能多地与设备进行交互,然后捕获相应的http
请求数据包,如下。
以登录请求为例,对应的http
请求报文示例如下。
利用该框架中提供的原语对http
请求进行定义,部分示例如下。
是否对某个字段进行 fuzz 需根据具体情况确定。对所有字段都 fuzz,生成的畸形数据包会非常多,测试所耗费的时间比较长,但发现问题的可能性比较大;只对少部分字段进行 fuzz,生成的畸形数据包会比较少,测试所耗费的时间更短,同时发现问题的可能性也比较小。
字段的粒度大小可能也会对测试结果有所影响。比如,如果对<?xml version="1.0" encoding="utf-8"?>进行变异,是将其当作一个整体,还是拆分为更小的单元?
至于具体怎么对某个字段进行变异,如针对字符串的变异,该框架内已包含一些规则。当然,也可以自己增加规则。
类似的,对网络数据包中的其他http
接口请求进行同样的定义。
测试的接口越多,触发问题的可能性越大。
设置会话信息
根据捕获的数据包定义完请求
后,设置与会话
相关的信息,包括目标设备的 ip 地址、端口等。
然后将之前定义的请求
按照一定的先后顺序链接起来,部分示例如下。
其中,由于setsysemailsettings
、setsyslogsettings
、setschedulesettings
等请求需要在登录之后才可以正常使用,所以需要在login
请求之后发生。而setsysemailsettings
、setsyslogsettings
和setschedulesettings
这几个请求之间则没有明确的先后关系。add_auth_callback
为自定义的回调函数,主要用于从login
请求中获取用于登录认证的信息如cookie
,然后将其设置于setsysemailsettings
、setsyslogsettings
、setschedulesettings
等请求中。
添加对目标设备的监控
这里通过设备HTTP
服务的可用性来判断目标设备是否发生异常。如果HTTP
服务无法访问,说明设备可能崩溃了。前面设置的Remote_NetworkMonitor()
就是用于对服务的可用性进行监测,其核心代码如下。
Remote_NetworkMonitor()为自行添加的代码,不属于Boofuzz框架。
前面也提到过,该监测方式的粒度比较粗,可能会存在漏报,可以采用或结合一些其他的方式进行改进。
1、如果可能,在测试时对设备内部的输出日志进行记录,比如设备打印的一些输出信息;
2、如果可能,在 gdb 调试状态下进行测试。
至于对环境进行恢复,由于该设备崩溃后会自行重启,所以无须额外的操作,只需调用sleep()
等待设备重启后即可。
开始 fuzzing
最后调用session.fuzz()
驱动整个过程,然后运行脚本即可。默认情况下,会在 26000 端口开启一个 web 服务,用于控制或查看测试的进度及相关信息等。在测试完成后,可以通过查看测试记录,看是否有测试用例造成目标设备出现异常,以进行进一步分析。
在最新的 commit 中,对 web 界面进行了改进,显示的信息更丰富。
下介绍使用 afl 来 fuzzing x86 架构之外的程序环境的搭建,以及安装 afl 的必要组件来构建环境搭建的步骤。主要归纳搭建 AFL qemu 模式、AFL unicorn 模式、qiling 框架的方法步骤和纪录一下搭建过程中的一些坑。
目标程序有源码的情况下使用 AFL 进行 fuzzing 的步骤:
源码编译出目标程序
开始 fuzzing
无源码的情况下可以使用 qemu 模式来 fuzzing ,需要加上 -Q (qemu 模式)参数:
这种方法需要安装一些环境,具体步骤在下文中有相应的步骤体现。
自己改的一个支持 fuzzing mips 架构的 afl,修复了一些错误。将 qemu-2.10.0.tar.gz、mips 的 libc.so.0 和 ld.so 文件 分别打包到了 qemu_mode、mips_lib/ 目录。增加了一处 patch 在 glibc-2.27 下编译 qemu 的错误:
安装方法
官方介绍的方法: This is a simple patch to AFL to make other-arch (non-x86 based) support easy. Just run ./build.sh to get started, where can be one or more of 这里以 mips 大端架构为例子,首先使用 qemu 编译出 mips 的环境: ./build.sh mips
这里编译完成之后会在上一级目录下生成 afl-qemu-trace
这个文件,这个文件其实就是编译好的 qemu-mips
。
这里如果直接跑: ./afl-fuzz -i fuzz_in/ -o fuzz_out/ -Q -m none -- ./test @@
可能会报错(原因是没有找到相应的 lib 动态库的路径)。这里的 test 可执行文件是 mips 架构的动态链接程序,如果对使用 gcc --static
命令编译的静态链接的 mips 程序进行 fuzzing 就不会出错。
上图中还是出现了错误的原因如下: 这里还需要设置 QEMU_LD_PREFI
这个环境变量,也就是 mips 的 lib/ 目录所在的目录,见下图:
这两个 so 文件可以自己从已有的 mips 架构设备固件的文件系统根目录下取出,也可以直接指定 QEMU_LD_PREFI 的值为设备固件文件系统的根目录。 注意:这里的库文件的文件名不能更改,否则会出错。
或者这里可以使用 apt search mips | grep libc6-mips-cross
来安装 libc 库,下载好的目录位于 /usr/
目录下:
然后再重新指定 QEMU_LD_PREFI
变量为相应的目录即可。
export QEMU_LD_PREFI=
mips_lib_path
这时就可以成功 fuzzing 动态链接的 mips 程序:
./afl-fuzz -i fuzz_in/ -o fuzz_out/ -Q -m none -- ./test @@
安装 AFL unicorn 模式
git clone 项目:
bash git clone <https://gitee.com/h4lo1/AFLplusplus.git> cd AFLplusplus/
安装必要的支持库:
apt-get install libgtk2.0-dev bison libtool libtool-bin
同 AFL 的编译和安装步骤:
sudo make -j8 && sudo make install
查看相应的版本,是 2.60d 版本的就说明安装正确:
unicornafl
为了使 afl 支持 -U 参数的模式,还需要安装 unicornafl,前提是要安装好 AFL++ 这个工具。
如果直接安装官方的 afl-fuzz 2.52b 版本的话,会提示没有 -U 这个参数,所以就需要提前安装好 AFL++。
安装步骤:
git clone unicornafl:
https://gitee.com/h4lo1/unicorn--afl
编译:
make -j8
安装:
cd ./bindings/python && ./setup.py install --user
如果在 python 中 import uncornafl
没问题的话,就说明安装成功。
因为下面要说的 qiling 框架只支持 python3,所以最好使用 python3 来编译这个模块。设置默认 python 环境为 python3 的方法:
在 ~/.bashrc
文件最末尾加入下面两句话即可,分别为 python3 和 pip3 在系统下的绝对路径:
alias python="/usr/bin/python3.6" && alias pip="/usr/bin/pip3"
之后 source ~/.bashrc
即可。或者要安装 unicornafl 组件的话,这里可以直接在 AFL++ 的 unicorn_mode 目录下,运行 build_unicorn_support.sh
可执行文件即可。
qiling 框架是一个二进制分析框架,针对于框架的学习参考官方项目的 README。
官方的介绍:
麒麟框架不仅仅是一个仿真平台或逆向工程工具。它还将“二进制插桩”和“二进制仿真”结合一起。借助麒麟框架,你可以:
动态干预二进制程序执行流程
在二进制程序执行期间对其进行动态补丁
在二进制程序执行期间对其进行代码注入
局部执行二进制程序,而不是运行整个文件
任意补丁“脱壳”已加壳程序内容
首先安装带有 unicornafl 的 qiling 框架,同样直接 git clone 即可:
git clone <https://gitee.com/h4lo1/qiling-unicornafl
>
clone 完成后,进入到 afl/ 目录下,对 fuzz_x8664_linux.py
进行 fuzzing。
/root/github/AFLplusplus/afl-fuzz -i ./afl_inputs -o ./afl_outputs -m none -U -- python3 ./fuzz_x8664_linux.py @@
afl-unicorn 框架也是一个可以 fuzzing 多架构的指令集的 afl 集成框架。关于该工具的介绍可以参考这篇文章。
该工具的优势是还可以 fuzzing 一些裸机系统的指令代码,如 RTOS 系统,可以参考这篇文章。
git clone 项目到本地:
git clone <https://gitee.com/h4lo1/Afl_unicorn.git
>
编译 unicorn:
cd Afl_unicorn/unicorn_mode && ./build_unicorn_support.sh
如果编译完成没有问题的话,会提示下面的语句:
[+] All set, you can now use Unicorn mode (-U) in afl-fuzz!
推荐在 Ubuntu 16.04 测试,在 ubuntu 18.04 可能安装不上。
这里可以使用下面的命令测试能否跑起来:
cd samples/simple && afl-showmap -U -m none -o .test-instr0 -- python simple_test_harness.py ./sample_inputs/sample1.bin
如果出现下面的提示就表示没问题:
在 18.04 上会提示失败原因如: No instrumentation detected
,估计和 unicorn 组件有关:
运行界面:
对于 MIPS 某些程序,还是会提示处理用例超时:
再如:
来源: 海特实验室