Fuzzing初学者指南:教你如何使用zzuf(一)

jopen 9年前
 

模糊测试(Fuzzing),是一种通过向目标系统提供非预期的输入并监视异常结果来发现软件漏洞的方法,通常情况下我们会使用一个有效的输入和添加随机误差来完成。在今天,很多软件项目潜在的漏洞都可以使用模糊测试检测出来。而我写这篇教程的目的就是希望去告诉大家模糊测试是非常简单的,并且希望模糊测试可以在多数项目中成为不可或缺的一部分。

如何进行模糊测试

首先我们需要生成一些输入样本,建议这里选择一些小文件来进行模糊测试。在这里我们可以创建一个任意格式的小图片文件,比如3*3的PNG文件,命名为“example.PNG”.现在我们尝试将其转换为其他格式,使用 ImageMagick即可(本次教程中我将以 ImageMagick 为例,这是一个命令行工具,它能够转换图像文件的格式):

convert example.png example.gif  convert example.png example.xwd  convert example.png example.tga

尽可能多的去转换成你喜欢的格式(-list可以显示出所有支持的格式)。然后我们需要生成这些example的畸形样本。

这时我们可以使用工具zzuf,这是一款简单的模糊测试工具,并且在大多数Linux环境下可用。Zzuf有很多不同的使用方法,但在这里我们只需要让它从我们的example中创建出大量的畸形文件。可以用一个简单的bash循环:

for i in {1000..3000}; do for f in example.*; do zzuf -r 0.01 -s $i < "$f" > "$i-$f"; done; done

每个example我都创建了大约2000个畸形文件,这里我都是以“[数字]-example.[扩展名]”的形式进行命名的。

在zzuf中,参数-r可以改变你想要的数量,0.01意味着1%的文件会被随机改变。参数-s则是种子值,每一个不同的s值都会得到一个不同的输出。当然,你可以换成其他数字试试,但经验告诉我们2000是比较合适的。

接下来我们进行下一步:将ImageMagick的这些畸形文件输入可执行文件从而使其被执行。而要做到这一点,我们只需要执行与这些文件相关的任意命令就好。这时我们仍然可以对其进行转换,通过转换来指导调整这些畸形图片的大小,从而可以增加得到bug的机会。

这里我把输出重定向到了一个日志文件上,稍后我们可以进行检查:

LC_ALL=C;; for f in *-example.*; do timeout 3 convert -resize 2 "$f" /tmp/test.png; echo $f; done &> fuzzing.log

LC_ALL=C;可确保我们输出的语言是英文。这样做的目的是可以得到grep的错误信息,超时命令可以确保我们在单个文件花费太多时间及时停止。当然这样可能会导致漏掉那些死循环的bug,在后面的教程中我会讲解怎么解决这一问题。同时每个经过调用转换的文件都需要输出当前的文件名,因为这使得我们可以在问题出现时知道是哪个文件造成的。

当然这可能需要很长时间,因为很有可能你所使用的输出文件会变得非常大,所以你还要确保有足够的空间。

现在,我们可以检查看下我们发现了什么? 尝试在日志中寻找下Segmentation faults:

grep -C2 "Segmentation fault" fuzzing.log

这时如果我们发现任何崩溃,我们就会直接看到,并且能够看到导致崩溃文件的文件名。

直接使用zzuf

上述的方法是通过有用的工具来进行许多文件格式的输入,但这其实并不是最有效的。Zzuf本身能够并行的处理任务,并且能够检测到正在挂起的测试软件。因此,其实完全可以让zzuf来运行你想要进行模糊测试的工具。

因此下面我们来使用另外一个软件 objdump,这是一款调试可执行文件的工具。它是 binutils的一部分,其输入应该为可调试文件。而在测试的时候,我们则需要使用 windows EXE 文件,你可以选择从文件格式存档中找一个无用的EXE文件。现在,我们在objdump上运行zzuf以及可执行文件。

zzuf -s 0:1000000 -c -C 0 -q -T 3 objdump -x win9x.exe

上图中的-s表示需要去尝试1000000种子值,而-c则意味着zzuf仅仅只需要对命令行中给出的文件进行模糊测试。这是很有必要的,因为这可以防止工具通过阅读配置以及其他的来反馈错误信息。而他们也并不会真的模糊化我们的输入。-C 0则意味着第一次发现崩溃后不用停止, -q 抑制了模糊测试命令的输出, -T 3则是超时3秒的设定,如果我们陷入到一个无休止的循环中,zzuf是不会被挂起的。

这里你所看到的输出时这样的:

zzuf[s=215,r=0.004]: signal 11 (SIGSEGV)

这意味着zzuf发现了一段错误的参数 -s 215 and -r 0.004,现在我们重新创建有缺陷的文件:

zzuf -r 0.004 -s 215 < win9x.exe > crash.exe

分析和报告

现在我们已经有了一个由应用程序崩溃生成的模糊文件,我们可以将其发送给应用程序的作者,告诉他该应用程序存在大量问题,应该及时修复。

然后我们还可以借助一款名为 valgrind的工具做更多的分析,只需要在你崩溃的命令前运行valgrind -q命令即可(-q 可以抑制一些不必要的输出)。

valgrind -q objdump -x crash.exe

它将会显示如下输出:

==22449==     Process terminating with default action of signal 11 (SIGSEGV)  ==22449==     Access not within mapped region at address 0x7715FF3 ==22449==    at 0x4E7FAC0: bfd_getl16 (libbfd.c:570)  ==22449==    by 0x4EE356D: pe_print_idata (peigen.c:1328)  ==22449==    by 0x4EE356D: _bfd_pe_print_private_bfd_data_common (peigen.c:2160)  ==22449==    by 0x4EDE1F8: pe_print_private_bfd_data (peicode.h:335)  ==22449==    by 0x408504: dump_bfd_private_header (objdump.c:2643)  ==22449==    by 0x408504: dump_bfd (objdump.c:3214)  ==22449==    by 0x408AA7: display_object_bfd (objdump.c:3313)  ==22449==    by 0x408AA7: display_any_bfd (objdump.c:3387)  ==22449==    by 0x40AB22: display_file (objdump.c:3408)  ==22449==    by 0x405249: main (objdump.c:3690)

我个人认为将问题报告给应用程序的开发者是一个非常好的想法,因为如果你所测试的软件没有启用调试符号,你所能够获得的输出中细节将会很少。如果你在模糊测试进行时对软件进行编译,那么你完全可以把 -ggdb 列到你的编译器标志寄存器中,这样可以确保你可以得到更多的调试信息。另外在编译时禁用共享库也是一个好主意,然后你就可以毫无顾忌的使用系统库对你的程序进行模糊测试了。软件的配置脚本将会以如下方式进行工作:

CFLAGS="-ggdb" CXXFLAGS="-ggdb" ./configure --disable-shared

以上就是本教程的第一部分,更多详细的内容可以前往zzuf的官网查看。第二部分中,我将向大家介绍如何通过Address Sanitizer提高发现BUG的能力。

* 原文链接: fuzzing-project ,编译:ch4nge,转载请注明来自FreeBuf黑客与极客(FreeBuf.COM)