使用DeepState和Eclipser进行单元测试模糊测试

本文介绍了如何将Eclipser这一新型模糊测试工具集成到DeepState中,用于C和C++的单元测试。Eclipser结合了白盒和灰盒模糊测试的优点,能够快速检测难以发现的漏洞,而无需昂贵的SMT或SAT求解器。

使用DeepState和Eclipser进行单元测试模糊测试

如果单元测试对您很重要,那么现在有另一个理由使用DeepState——我们类似Google Test的基于属性的C和C++测试工具。这个理由就是Eclipser,这是最近在ICSE 2019论文中提出的一种强大的新型模糊测试工具。我们很高兴地宣布,Eclipser现已完全集成到DeepState中。

Eclipser在模糊测试中提供了许多符号执行的优点,而没有通常与符号执行相关的高计算和内存开销。它通过仅使用轻量级插桩,并且最关键的是从不调用昂贵的SMT或SAT求解器,结合了“白盒和灰盒模糊测试的最佳部分”。Eclipser是我们希望(也许在您的帮助下)制作的一系列“一键式”前端中的第一个,这些前端适用于需要比AFL或libFuzzer更多工作才能应用的有前途的工具。Eclipser使DeepState能够快速检测更多难以发现的漏洞。

Eclipser有何特别之处?

传统的符号执行(DeepState通过Manticore和angr等工具支持)会跟踪路径约束:程序输入的条件,使得程序在输入满足约束时会采取特定路径。不幸的是,解决这些条件既困难又昂贵,尤其是许多约束是不可行的:它们无法解决。

已经提出了许多解决路径约束高成本的变通方法,但大多数基于符号执行的工具在可扩展性方面仍然有限,并且在要求生成长路径或处理复杂代码时容易失败。Eclipser基于KLEE和MAYHEM中开发的思路,用近似路径约束替代路径约束。这些条件(顾名思义)不太精确,但更容易解决。关键的是,它们不需要缓慢的求解器。Eclipser仍然需要解决这些近似的“简单”约束,但它可以假设它们要么是简单且线性的(在这种情况下,廉价的技术就足够了),要么至少是单调的,在这种情况下,Eclipser使用二分搜索而不是求解器调用。如果实际约束既不是线性的也不是单调的,Eclipser将无法生成相关的输入,但模糊测试可能会让它在这种情况下取得进展。在实践中,符号执行也常常会因为此类约束而失败,但会在浪费大量计算努力后因求解器超时而失败。Eclipser会更快地生成一些输入(尽管不一定是满足难以解决的条件的输入)。

为什么您应该关注?

Eclipser之所以有趣,主要是因为作者报告称,它在coreutils上的代码覆盖率优于KLEE,在LAVA-M基准测试中检测到的漏洞多于AFLFast、LAF-intel、VUzzer和Steelix,并且最引人注目的是,在真实Debian软件包上检测到的漏洞多于AFLFast和LAF-intel。Debian实验产生了八个新的CVE。

鉴于这种有希望的表现,我们决定将Eclipser集成到DeepState中,使其易于将Eclipser方法应用于您的单元测试。开箱即用,DeepState已经可以与Eclipser一起使用。该模糊测试工具适用于任何接受文件作为输入的二进制文件。DeepState适用于我们尝试过的所有基于文件的模糊测试工具。然而,重要的是使用正确的DeepState参数与Eclipser,否则Eclipser基于QEMU的插桩将无法工作。还需要一些手动努力来为DeepState生成独立的测试用例和崩溃输入,因为Eclipser以其他工具无法使用的自定义格式存储测试。因此,我们添加了一个简单的前端,使您(和我们)的生活更轻松。

Eclipser论文示例

DeepState示例目录中有Eclipser论文中使用的主要示例的DeepState化版本代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#include <deepstate/DeepState.hpp>
using namespace deepstate;
#include <assert.h>

int vulnfunc(int32_t intInput, char * strInput) {
   if (2 * intInput + 1 == 31337)
      if (strcmp(strInput, "Bad!") == 0)
         assert(0);
   return 0;
}

TEST(FromEclipser, CrashIt) {
   char *buf = (char*)DeepState_Malloc(9);
   buf[8] = 0;
   vulnfunc(*((int32_t*) &buf[0]), &buf[4]);
}

尝试此示例的最简单方法是构建DeepState docker镜像(是的,DeepState现在可以轻松创建功能齐全的docker镜像):

1
2
3
4
$ git clone https://github.com/trailofbits/deepstate
$ cd deepstate
$ docker build -t deepstate . -f docker/Dockerfile
$ docker run -it deepstate bash

构建docker镜像需要一段时间:DeepState、AFL和libFuzzer很快,但构建Eclipser是一个相当复杂的过程。

进入DeepState docker镜像后:

1
2
$ cd deepstate/build/examples
$ deepstate-eclipser ./FromEclipser --timeout 30 --output_test_dir eclipser-FromEclipser

Eclipser不需要完整的30秒;它几乎立即产生一个崩溃输入,并将其保存在eclipser-FromEclipser/crash-0中。我们尝试的其他模糊测试工具AFL和libFuzzer,即使给它们四个小时生成测试,也无法找到崩溃输入。它们分别生成和执行了数千万和数亿个输入,但没有一个满足产生崩溃的条件。即使使用libFuzzer的值配置文件也无济于事。

自己运行实验很容易:

1
2
$ mkdir foo; echo foo > foo/foo
$ afl-fuzz -i foo -o afl-FromEclipser -- ./FromEclipser_AFL --input_test_file @@ --no_fork --abort_on_fail

1
2
3
$ mkdir libFuzzer-FromEclipser
$ export LIBFUZZER_EXIT_ON_FAIL=TRUE
$ ./FromEclipser_LF libFuzzer-FromEclipser -use_value_profile=1

您可能需要在等待不耐烦时中断这两个运行。

angr和Manticore在几秒钟内就能找到这个崩溃输入。不同之处在于,虽然Eclipser能够像二进制分析工具一样处理这个玩具示例,但二进制分析工具无法扩展到复杂问题,如测试类似ext3的文件系统、测试Google的leveldb,或需要更长测试才能触发有趣行为的代码,如红黑树实现。Eclipser令人兴奋,因为它在文件系统和红黑树上都优于libFuzzer,但仍然可以解决像FromEclipser.cpp这样的“需要符号执行”的问题。

幕后:将Eclipser支持添加到DeepState

如上所述,原则上“无需”添加对Eclipser或大多数基于文件的模糊测试工具的支持。DeepState使得使用文件作为向程序传递输入方式的模糊测试工具可以轻松为参数化单元测试生成值。然而,找出与给定模糊测试工具一起使用的正确DeepState参数可能很困难。起初我们认为Eclipser不起作用,因为如果DeepState分叉运行测试,它就不起作用。一旦我们使用no_fork运行DeepState,一切就顺利了。我们制作像deepstate-eclipser这样的前端的部分目标是确保您永远不必处理这种神秘的失败。设置Eclipser运行、解析命令行选项(将DeepState工具参数约定转换为Eclipser的参数)以及让Eclipser从结果中生成独立测试文件的完整代码仅需57行代码。我们希望用户提交更多简单的“前端”到其他有前途的模糊测试工具,这些工具需要一些额外的设置才能与DeepState一起使用!

那么,这是最好的模糊测试工具吗?

像Eclipser这样的测试生成技术的进步是否会淘汰DeepState支持许多不同后端的目标?答案是“不太可能”。虽然Eclipser令人兴奋,但我们的初步测试表明,在文件系统和红黑树上,它的表现略逊于大家最喜欢的“驮马”模糊测试工具AFL。事实上,即使在我们使用DeepState深入探索的一小部分测试问题中,我们也看到了Eclipser表现最好的实例、libFuzzer表现最好的实例和AFL表现最好的实例。红黑树中的一些错误需要专门的符号执行测试工具才能找到(而Eclipser无济于事,我们发现)。此外,即使一个模糊测试工具在某个示例中总体上表现最好,它也可能在发现该示例中的某个特定错误时表现不佳。

研究文献和模糊测试工具使用的实际智慧一再表明,即使一个模糊测试工具足够好“击败”其他模糊测试工具(从而在ICSE上发表论文),它也总是会有表现不如“旧的”、“过时的”模糊测试工具的实例。在模糊测试中,多样性不仅有用,而且必不可少,如果您真的想找到每一个最后的错误。没有一个模糊测试工具对所有被测程序或给定真实世界程序中的所有错误都是最好的。

Eclipser论文的作者认识到了这一点,并指出他们的技术与Angora模糊测试工具中使用的技术是互补的。Angora与Eclipser有一些共同的目标,但依赖于关于分支距离的元启发式方法,而不是近似路径条件,并使用细粒度的污点分析来渗透一些Eclipser无法处理的分支。Angora还需要源代码。Eclipser的一大优势是,与AFL(在非QEMU模式下)或libFuzzer不同,它不需要您使用额外的插桩重新构建任何要使用DeepState测试的库。在撰写Eclipser论文时,Angora还无法进行比较,但它最近发布了,并且是另一个与DeepState完全集成的良好候选者。

Eclipser是添加到您的模糊测试工具库中的一匹好马,但它不会赢得每一场比赛。随着新的和令人兴奋的模糊测试工具的出现,DeepState支持许多模糊测试工具的能力只会变得更加重要。如果只需更改变量并执行FUZZER=FOO make; deepstate-foo ./myprogram,使用多种模糊测试工具就很容易,而如果为每个工具重写测试,则几乎不可能。在不久的将来,我们计划让生活更轻松,并支持自动集成模式,其中DeepState使用多个模糊测试工具更积极地测试您的代码,而您只需决定要使用多少个核心。

如果您喜欢这篇文章,请分享: Twitter LinkedIn GitHub Mastodon Hacker News

页面内容 Eclipser有何特别之处? 为什么您应该关注? Eclipser论文示例 幕后:将Eclipser支持添加到DeepState 那么,这是最好的模糊测试工具吗? 最近的帖子 Trail of Bits的Buttercup在AIxCC挑战赛中获得第二名 Buttercup现已开源! AIxCC决赛:记录 攻击者的提示注入工程:利用GitHub Copilot 在NVIDIA Triton中发现内存损坏(作为新员工) © 2025 Trail of Bits。 使用Hugo和Mainroad主题生成。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计