# 固件FUZZ

对IOT设备的Fuzzing可分为协议Fuzzing和二进制Fuzzing，Fuzzing一般注重输入生成和反馈，但也根据框架原理不同而侧重点不同。协议Fuzzing关键在Monitor，二进制Fuzzing关键在执行效率。Fuzzing工具(框架)数不胜数，可参见[Blackarch](https://blackarch.org/fuzzer.html)和[Fuzzing-Survey](https://fuzzing-survey.org/)。下以Boofuzz和AFL为例，分别阐释协议和二进制Fuzzing。

### 一、协议Fuzzing之Boofuzz

通常，在对`IoT`设备的固件进行分析时，固件中与提供服务如`HTTP`、`Telnet`、`RTSP`、`UPnP`等相关的二进制程序是重点分析的对象。因为一旦在这些程序中发现漏洞，其很有可能会被远程利用，进而带来严重的安全隐患。

对固件二进制程序进行分析，常见的分析方法包括**模糊测试**、**补丁比对**、**工具静态扫描**和**人工审计**等。其中，模糊测试方法具备简单易用的特点，通常也比较有效，其在业界已被广泛使用。

下面，以某型号路由器为例，基于`Boofuzz`框架，介绍对常见网络协议进行`fuzz`的方法。

除了网络协议外，也可以采用类似的思路对其他协议如BLE、串口协议等进行fuzz。同时，该方法不仅局限于IoT设备，也可用于对常见的服务程序进行测试。

#### 1.1 **模糊测试简介**

**模糊测试**采用黑盒测试的思想，通过构造大量的畸形数据作为应用程序的输入，来发现程序中可能存在的安全缺陷或漏洞。

![](/files/bY2Qkw7w9UPHu0JmZFqg)

模糊测试方法的分类有很多。根据测试用例生成方式的不同，可以分为基于**变异**的模糊测试和基于**生成**的模糊测试。根据对目标程序的理解程度，可分为**黑盒**模糊测试、**灰盒**模糊测试和**白盒**模糊测试。常见工具与方法的对应关系如下。

![](/files/BC6wZ2xTN1bQDU95pgJe)

针对设备，由于其资源受限和环境受限等特点，实际中常采用黑盒模糊测试的方式。在对网络协议进行测试时，可以将常见的网络协议分为两类：一类属于文本协议，如`HTTP`、`FTP`等，这类协议的特点是其数据包内容都是可见字符；另一类为二进制协议，其特点是数据包内容大部分是不可见字符，这类协议在工控设备如`PLC`中比较常见，通常属于私有协议。针对文本协议，笔者常采用`[Sulley`]\(<https://github.com/OpenRCE/sulley>) 框架进行测试，而针对二进制协议，则常采用 `[kitty](<https://github.com/cisco-sas/kitty>)`框架进行测试。`Sulley`框架和`kitty`框架均能够对两类协议进行测试。

另外，在对`IoT`设备进行模糊测试时，需要考虑如何对设备进行监控，以判断是否出现异常。最简单的方式通过设备服务的可用性进行判断，如果设备提供的服务不可访问，表明设备可能崩溃了。但这种监控方式粒度比较粗，容易漏掉一些异常行为。另外，当设备出现异常后，还需要对环境进行恢复，以便继续进行测试。常见的方式就是重启设备。现在很多设备崩溃之后都会自动重启，如果测试目标设备没用提供这种机制，则需要采用其他方式解决。

#### 1.2 Boofuzz**框架简介**

由于`Sulley`框架目前已经停止更新维护，而`Boofuzz`框架是`Sulley`的继承者，除了修复一些`bug`之外，还增加了框架的可扩展性。

由上图可知，该框架主要包含四个部分：

* `数据生成`：根据协议格式利用原语来构造`请求`
* `会话管理/驱动`：将`请求`以图的形式链接起来形成会话，同时管理`待测目标`、`代理`、`请求`，还提供一个 web 界面用于监视和控制
* `代理`：与目标进行交互以实现日志记录、对网络流量进行监控等

通常，代理是运行在目标设备上。但是，对于IoT设备而言，大部分情况下都无法在目标设备上运行代理程序。

* `实用工具`：独立的命令行工具，完成一些其他的功能

其中，`数据生成`和`会话管理/驱动`是比较重要的2个模块。对于`数据生成`模块，`Boofuzz`框架提供了很多原语来定义`请求`，如最基础的`s_string()`、`s_byte()`、`s_static()`等。对于`会话管理/驱动`模块，其思想体现在下图中。

![](/files/CMY55WUuFa7MWP6zk3Wi)

在上图中，节点`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`。

#### 1.3 **协议fuzzing实战**

以某型号路由器为例，由于路由器上`HTTP`服务是最为常见的，故以`http`协议为例进行介绍。

模糊测试属于动态分析技术，因此需要有真实设备，或者采用对固件进行仿真的方式。

* 根据网络数据包构造请求

首先，需要尽可能多地与设备进行交互，然后捕获相应的`http`请求数据包，如下。

![](/files/WhKzeub3FNcWaEtsIToj)

以登录请求为例，对应的`http`请求报文示例如下。

![](/files/kmFVSiIDDxtorc5o0uRY)

利用该框架中提供的原语对`http`请求进行定义，部分示例如下。

![](/files/VfJL4qPmbDZ4RKCQwEj4)

是否对某个字段进行 fuzz 需根据具体情况确定。对所有字段都 fuzz，生成的畸形数据包会非常多，测试所耗费的时间比较长，但发现问题的可能性比较大；只对少部分字段进行 fuzz，生成的畸形数据包会比较少，测试所耗费的时间更短，同时发现问题的可能性也比较小。

字段的粒度大小可能也会对测试结果有所影响。比如，如果对\<?xml version="1.0" encoding="utf-8"?>进行变异，是将其当作一个整体，还是拆分为更小的单元?

至于具体怎么对某个字段进行变异，如针对字符串的变异，该框架内已包含一些规则。当然，也可以自己增加规则。

类似的，对网络数据包中的其他`http`接口请求进行同样的定义。

**测试的接口越多，触发问题的可能性越大。**

* 设置会话信息

根据捕获的数据包定义完`请求`后，设置与`会话`相关的信息，包括目标设备的 ip 地址、端口等。

![](/files/Yr27wt2kb9IpBeDDHK9A)

然后将之前定义的`请求`按照一定的先后顺序链接起来，部分示例如下。

![](/files/XzkC4DOMxcKM7wxhuS30)

其中，由于`setsysemailsettings`、`setsyslogsettings`、`setschedulesettings`等请求需要在登录之后才可以正常使用，所以需要在`login`请求之后发生。而`setsysemailsettings`、`setsyslogsettings`和`setschedulesettings`这几个请求之间则没有明确的先后关系。`add_auth_callback`为自定义的回调函数，主要用于从`login`请求中获取用于登录认证的信息如`cookie`，然后将其设置于`setsysemailsettings`、`setsyslogsettings`、`setschedulesettings`等请求中。

![](/files/OgjHdix570Kt18B5doQP)

* 添加对目标设备的监控

这里通过设备`HTTP`服务的可用性来判断目标设备是否发生异常。如果`HTTP`服务无法访问，说明设备可能崩溃了。前面设置的`Remote_NetworkMonitor()`就是用于对服务的可用性进行监测，其核心代码如下。

![](/files/E7YPT4Ku3j3OxEHBJtfN)

Remote\_NetworkMonitor()为自行添加的代码，不属于Boofuzz框架。

前面也提到过，该监测方式的粒度比较粗，可能会存在漏报，可以采用或结合一些其他的方式进行改进。

1、如果可能，在测试时对设备内部的输出日志进行记录，比如设备打印的一些输出信息；

2、如果可能，在 gdb 调试状态下进行测试。

至于对环境进行恢复，由于该设备崩溃后会自行重启，所以无须额外的操作，只需调用`sleep()`等待设备重启后即可。

* **开始 fuzzing**

最后调用`session.fuzz()`驱动整个过程，然后运行脚本即可。默认情况下，会在 26000 端口开启一个 web 服务，用于控制或查看测试的进度及相关信息等。在测试完成后，可以通过查看测试记录，看是否有测试用例造成目标设备出现异常，以进行进一步分析。

![](/files/EwEGWmTHkRTX0cvw6R0W)

在最新的 commit 中，对 web 界面进行了改进，显示的信息更丰富。

### 二、Mips Fuzzing环境搭建

下介绍使用 afl 来 fuzzing x86 架构之外的程序环境的搭建，以及安装 afl 的必要组件来构建环境搭建的步骤。主要归纳搭建 AFL qemu 模式、AFL unicorn 模式、qiling 框架的方法步骤和纪录一下搭建过程中的一些坑。

#### 2.1 有源码情况下的 fuzzing

目标程序有源码的情况下使用 AFL 进行 fuzzing 的步骤:

* 源码编译出目标程序

```c
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char **argv,char **env){

    char buf[20];
    read(0,buf,0x100);
    return 1;
}
```

```bash
afl-gcc test.c -o test
```

* 开始 fuzzing

```bash
afl-fuzz -i in/ -o out/ -m none ./test @@
```

#### 2.2 无源码情况下的 fuzzing

无源码的情况下可以使用 qemu 模式来 fuzzing ，需要加上 -Q （qemu 模式）参数：

```bash
gcc test.c -o test
afl-fuzz -i in/ -o out/ -m none -Q ./test_no_source @@
```

这种方法需要安装一些环境，具体步骤在下文中有相应的步骤体现。

#### 2.3 MIPS 架构 fuzzing 环境搭建

自己改的一个支持 fuzzing mips 架构的 [afl](https://gitee.com/h4lo1/afl-other-arch)，修复了一些错误。将 qemu-2.10.0.tar.gz、mips 的 libc.so.0 和 [ld.so](http://ld.so) 文件 分别打包到了 qemu\_mode、mips\_lib/ 目录。增加了一处 patch 在 glibc-2.27 下编译 qemu 的错误：

```bash
 --- a/util/memfd.c +++ b/util/memfd.c @@ -31,9 +31,7 @@
#include "qemu/memfd.h"
-#ifdef CONFIG_MEMFD -#include  
-#elif defined CONFIG_LINUX 
+#if defined CONFIG_LINUX && !defined CONFIG_MEMFD #include  #include
```

* 安装方法

官方介绍的方法： 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`。

![](/files/2CziTDaur43WoSfduQwe)

这里如果直接跑： `./afl-fuzz -i fuzz_in/ -o fuzz_out/ -Q -m none -- ./test @@`

可能会报错（原因是没有找到相应的 lib 动态库的路径）。这里的 test 可执行文件是 mips 架构的动态链接程序，如果对使用 `gcc --static` 命令编译的静态链接的 mips 程序进行 fuzzing 就不会出错。

![](/files/x1YujTHqhMDHkB7OTX8e)

上图中还是出现了错误的原因如下： 这里还需要设置 `QEMU_LD_PREFI` 这个环境变量，也就是 mips 的 lib/ 目录所在的目录，见下图：

![](/files/7spYTcIr8yZK4YmDVIZG)

这两个 so 文件可以自己从已有的 mips 架构设备固件的文件系统根目录下取出，也可以直接指定 QEMU\_LD\_PREFI 的值为设备固件文件系统的根目录。 **注意：这里的库文件的文件名不能更改，否则会出错。**

或者这里可以使用 `apt search mips | grep libc6-mips-cross` 来安装 libc 库，下载好的目录位于 `/usr/` 目录下：

![](/files/2Y5eZruB2002cK6gYzxH)

然后再重新指定 `QEMU_LD_PREFI` 变量为相应的目录即可。

`export QEMU_LD_PREFI=`mips\_lib\_path

这时就可以成功 fuzzing 动态链接的 mips 程序：

`./afl-fuzz -i fuzz_in/ -o fuzz_out/ -Q -m none -- ./test @@`

![](/files/mLeXkPElBkbwzvCrbeRL)

* 安装 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 版本的就说明安装正确：

![](/files/IkOD3LnS61dOKT8vCtOz)

* unicornafl

为了使 afl 支持 -U 参数的模式，还需要安装 unicornafl，前提是要安装好 AFL++ 这个工具。

如果直接安装官方的 afl-fuzz 2.52b 版本的话，会提示没有 -U 这个参数，所以就需要提前安装好 AFL++。

![](/files/3bBVPxXKvNZUFBOa4Pqx)

安装步骤：

git clone unicornafl：

`https://gitee.com/h4lo1/unicorn--afl`

编译：

`make -j8`

安装：

`cd ./bindings/python && ./setup.py install --user`

如果在 python 中 `import uncornafl` 没问题的话，就说明安装成功。

![](/files/YjJpJ0KuQzRgJWokS6T0)

因为下面要说的 qiling 框架只支持 python3，所以最好使用 python3 来编译这个模块。设置默认 python 环境为 python3 的方法：

在 `~/.bashrc` 文件最末尾加入下面两句话即可，分别为 python3 和 pip3 在系统下的绝对路径：

`alias python="/usr/bin/python3.6" && alias pip="/usr/bin/pip3"`

![](/files/AY0wujp1Y8C9ZSBOnBRd)

之后 `source ~/.bashrc` 即可。或者要安装 unicornafl 组件的话，这里可以直接在 AFL++ 的 unicorn\_mode 目录下，运行 `build_unicorn_support.sh` 可执行文件即可。

![](/files/mTMlaER3KPCqQH5eaJaZ)

#### 2.4 使用 qiling-afl fuzzing mips 程序

* qiling 框架是一个二进制分析框架，针对于框架的学习参考官方项目的 [README](https://github.com/qilingframework/qiling)。

官方的介绍：

麒麟框架不仅仅是一个仿真平台或逆向工程工具。它还将“二进制插桩”和“二进制仿真”结合一起。借助麒麟框架，你可以：

* 动态干预二进制程序执行流程
* 在二进制程序执行期间对其进行动态补丁
* 在二进制程序执行期间对其进行代码注入
* 局部执行二进制程序，而不是运行整个文件
* 任意补丁“脱壳”已加壳程序内容

首先安装带有 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 @@`

#### 2.5使用 afl-unicorn 框架 fuzzing mips 程序

afl-unicorn 框架也是一个可以 fuzzing 多架构的指令集的 afl 集成框架。关于该工具的介绍可以参考[这篇文章](https://xz.aliyun.com/t/5968)。

该工具的优势是还可以 fuzzing 一些裸机系统的指令代码，如 RTOS 系统，可以参考[这篇文章](https://www.anquanke.com/post/id/170078)。

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`

如果出现下面的提示就表示没问题：

![](/files/Tktb0kdTmp7I1f75KJ1Z)

在 18.04 上会提示失败原因如： `No instrumentation detected`，估计和 unicorn 组件有关：

![](/files/rhbW03ycUTiM44GaA99F)

运行界面：

![](/files/fJ6uX5Q1mhg9vBNoJzuy)

对于 MIPS 某些程序，还是会提示处理用例超时:

![](/files/AR1K1gCAv3ZFxpecZNhi)

再如：

![](/files/W9GvJ2fd0RZUHi1VwOjT)

来源： [***海特实验室***](https://github.com/DasSecurity-HatLab/HatLab_IOT_Wiki)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://wokough.gitbook.io/iot-firmware-aio/wiki/gu-jian-fuzz.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
