我应该如何编译程序进行模糊测试?

信息安全 模糊测试
2021-08-26 05:32:18

我正在对我有源代码的程序进行一些模糊测试。我应该使用任何特定的编译器选项编译程序,以使模糊测试更有效吗?如果是这样,传递给编译器的正确的命令行标志列表是什么,以使模糊测试尽可能有效?

我应该尝试显式启用堆栈 cookie(gcc -fstack-protector)吗?检查堆栈分配缓冲区的长度(gcc -D_FORTIFY_SOURCE=2)?Malloc/自由检查 (env MALLOC_CHECK_=2)?Glib 的调试分配器(G_SLICE=debug-blocks)?

你会推荐我使用Mudflap(例如,gcc -fmudflap -fmudflapir + MUDFLAP_OPTIONS='-mode-check -viol-abort -check-initializations -ignore-reads')吗?

我对 Linux 上 gcc 的答案特别感兴趣,但也可以随意回答其他平台的问题。

3个回答

没有人提出明确的答案,所以我做了一个小实验。基于这个实验,到目前为止,这是我的建议:

推荐。进行模糊测试时,您可以考虑设置环境变量LIBC_FATAL_STDERR_=1 MALLOC_CHECK_=3此设置在我的实验中没有可测量的性能影响,并且根据我的结果,此设置可能会略微增加您检测到的错误数量。

其他设置都没有在我的实验中产生任何可检测的差异。

选修的。如果需要,您可以使用-fstack-protectoror -fstack-protector-all、 with-O2 -D_FORTIFY_SOURCE=2和/或 mudflap 进行编译;并且您可以使用环境变量运行G_SLICE=debug-blocks在我的实验中,它们都没有任何可衡量的性能影响。但是,它们都没有对发现的错误集产生任何影响。因此,虽然我的实验没有成本,但也没有任何好处。

实验方法和细节。在每次运行中,我ffmpeg使用zzuf一个种子文件对 5000 次迭代进行模糊测试。每个编译器标志/环境变量设置运行一次。我确保模糊测试会在每次运行中生成完全相同的变体文件集,所以唯一的区别是编译器标志/环境变量。为了衡量性能影响,我测量了完成 fuzzing 的 CPU+系统时间。为了衡量对检测错误能力的影响,我记录了哪些变体文件触发了可检测到的崩溃。

我测量了性能,但没有一个选项对性能有任何可检测的影响(在所有情况下差异均 < 1%,可能是由于随机噪声)。

对于错误检测能力,我MALLOC_CHECK_=3略占优势,但其他标志或设置都没有对错误检测能力产生任何影响:

  • MALLOC_CHECK_=3确实对哪些变体文件导致崩溃有影响。在没有标志的情况下,5000 次迭代中有 22 次导致崩溃。另外 2 次迭代导致了一条警告消息 ( *** glibc detected ***...),如果您知道要查找它,可以使用它来检测错误,因此如果您足够聪明地 grep 模糊日志以获取该消息,则 5000 次迭代中有 24 次会提供错误的迹象——而如果您不知道 grep 日志以获取该特定警告消息,那么 5000 次迭代中只有 22 次提供了错误的指示。相反,当我启用时MALLOC_CHECK_=3,5000 次迭代中有 25 次导致崩溃,并且不需要 grep 日志。因此,MALLOC_CHECK_=3两者在发现错误迹象方面稍微更有效,并且还减少了专门对模糊日志进行后处理的需要。

    有趣的是,有一个变体文件在没有设置的情况下使程序崩溃,但没有使程序崩溃MALLOC_CHECK_=3,这证实了@this.josh 的假设,即在某些情况下额外检查可能会导致我们错过一些错误——但同时,有 2 个变体文件在没有设置的情况下不会使程序崩溃,但确实会导致程序崩溃MALLOC_CHECK_=3因此,它的好处MALLOC_CHECK_=3超过了它的成本。

  • 除了MALLOC_CHECK_,其他任何设置都不会对哪些变体文件触发可检测到的崩溃产生任何影响。导致基线程序(无特殊标志)崩溃的变体文件集与使用特殊标志编译时导致程序崩溃的变体文件集完全相同。因此,至少在这个实验中,那些其他设置并没有让我们付出任何代价(在性能方面)——但也没有给我们带来任何好处(在错误检测能力方面)。

我的实验远非权威。要做到这一点,人们应该真正尝试使用许多不同的程序(不仅仅是一个)和多个不同的种子文件(不仅仅是一个)。所以我提醒你不要从这个小实验中得出太多结论。但我认为结果仍然很有趣。

如果应用程序将因测试而崩溃,编译器选项不会保存它。如果您使用它编译应用程序,gcc -D_FORTIFY_SOURCE=2 -O2 那么由于轻微的内存违规通常不会使进程崩溃,该进程将更频繁地被终止。一个很好的例子是,您将拥有更高的检测相邻堆溢出的能力。

GCC Mudflap是一种更高级的调试工具,用于查找悬空指针、双释放、缓冲区溢出和其他潜在的内存损坏漏洞。此 GCC 编译器选项使应用程序非常慢,因此不适用于生产。但是,它会记录所有内存违规,甚至是不会导致应用程序崩溃的违规。它为您提供详细信息,以帮助缩小您正在处理的内存违规类型。

但是它是鸡还是蛋。在您发现其中一个缺陷之后,您将不得不击败这个安全系统,这不是一项小任务(或不使用此安全措施的人)。您将无法找到简单的“为了乐趣和利润而破坏堆栈”风格的攻击。现代技术包括链接内存损坏漏洞 和使用ROP 链来击败 ASLR。

多么有趣的问题。我收集它只是 unix,但如果不是,我建议的第一件事是在两种环境中测试它。Windows 有一套非常好的工具来进行堆调试、危险的 api 管理等等——如果你有兴趣,可以使用谷歌 App Verifier。

但总的来说,我会说您至少应该尝试发布版本,并使用您计划在生产中使用的相同优化器设置。您希望在测试中有一个配置,您不会被增强的调试检查(如应用程序验证程序或 ma​​lloc_check 的东西)保存。毕竟,攻击者将针对您优化的发布构建代码。

你甚至可以尝试一些其他的 linux 编译器,然后对这些结果进行模糊测试。

除此之外,我和你一样有兴趣听到这个问题的答案。:-)