Fuzz Testing in Practice: Obstacles and Solutions

Fuzz Testing in Practice: Obstacles and Solutions

1 介绍

本文主要介绍了作者与两位华为工程师如何将模糊测试的技术应用到libmsg这个信息传输库上。着重描述了在应用这个基础的过程中的主要障碍,和对应的解决方法。解决了所有障碍后,本文使用SAFL进行模糊测试并成功找到9个新的漏洞。

2 背景

本文采用SAFL作为模糊测试的工具(发现给出的下载地址已经不见了。。。)。SAFL的算法是FairFuzzAFLFast的结合。FairFuzz解决了种子输入选择不公平的问题。AFLFast解决了种子输入变异慢的问题,较少执行路径的测试用例更有可能触发crash,但是AFL的默认行为浪费了大量的计算力。

3 测试目标和过程

选择模糊测试的入口主要依据4个方面:

  1. 复杂度

  2. 完整性

  3. 便利性
  4. 准确性:相对于可信的内部API调用,没有数据传输或调用惯例。这样排除了误报:所有异常的系统表现都是漏洞。

选定测试入口后,模糊测试流程大致如下:

QQ截图20181023161914.png

  1. 准备环境和设置模糊测试器
  2. 测试工程师写模糊测试驱动
  3. 编译阶段,编译成测试程序以供fuzz
  4. 运行测试阶段
  5. 产品运行阶段

但是天不遂人愿,在实际执行过程中存在很多问题。

4 典型的阻碍和解决方法:

阻碍1:环境准备阶段配置不一致

解决方案1:从源代码重新构建系统

阻碍2:在测试驱动开发过程缺乏训练

模糊测试是个比较新的技术。写测试驱动需要训练。就libmsg这个库来说,一方面需要简单的应用初始化库,注册回调函数以调用接口;另一方面需要有简单的请求发送工具

解决方案2:将测试用例和样例代码转换为模糊测试驱动

阻碍3:在编译软件阶段构建系统的复杂性

解决方案3:开发自动化的工具链支持复杂的编译和构建

作者提供自动化的工具链,这个工具链识别不同的构建系统,并加入插桩相关的编译器选项。配置好的工具链隐藏了内部的复杂性,减少了用户使用的难度。

阻碍4:运行测试用例中的浅层bug

随机合法的人工输入可用来验证模糊测试驱动器正确填入了参数,符合API conventions。但是许多简单的输入会使libmsg崩溃。

解决方案4:让开发者修复浅层bug

本文作者认为如果bug常常(在frequent paths上触发)在浅层crash,那么深层的路径就不会执行到。所以本文中作者协助开发者修复浅层次的bug抑制过快的crash。

阻碍5:在产品运行阶段隐藏了bug的代码

QQ截图20181023192617.png

这里给出一个实例,此处应该有一个Failure,打印stacktrace,但是exit(0),把这个bug隐藏起来。

解决方案5:给程序和Fuzzer打补丁以检测隐藏bug

对于程序打补丁的策略是:移除隐藏了error报告机制的commit。

5 结果

QQ截图20181023193036.png

找到了9个单元测试没发现的新bug。

6 讨论

模糊测试准备阶段:

  1. 许多产品环境很难或者根本不能适应fuzzers的先决条件
  2. 对于复杂构建系统的支持是紧缺但是很重要的

写模糊测试驱动阶段:

  1. 写高质量的模糊测试驱动的训练是必要的
  2. 样例代码和单元测试可以帮助工程师写测试用例驱动

运行模糊测试:

  1. 代码可能刻意隐藏了bug
  2. 应该监控资源的使用信息:因为有时资源使用可能暗示bug的存在。例如在调查大量的timeout测试用例时,作者发现了内存分配不受控制的漏洞。内存的过度投入同时也会降低存取速度。

7 结论