一
介绍
上次我们写博客时,我们有一个简单的模糊测试器,它会测试一个故意有漏洞的程序,该程序会对文件进行一些检查,如果输入文件通过了检查,它会继续进行下一个检查,如果输入通过了所有检查,程序就会发生段错误。我们发现了代码覆盖的重要性,以及它如何帮助将模糊测试过程中指数级罕见的事件减少为线性罕见的事件。让我们直接进入如何改进我们的简单模糊测试器!
特别感谢@gamozolabs的所有内容,让我对这个话题产生了兴趣。
二
性能
首先,我们的简单模糊测试器非常慢。如果你还记得,我们的简单模糊测试器平均每秒大约进行1,500次模糊测试。在我的测试中,AFL在QEMU模式下(模拟没有可用的源代码进行编译插桩)每秒大约进行1,000次模糊测试。这是有道理的,因为AFL做的事情远比我们的简单模糊测试器多,尤其是在QEMU模式下,我们在模拟CPU并提供代码覆盖。
我们的目标二进制文件(https://gist.github.com/h0mbre/db209b70eb614aa811ce3b98ad38262d)会执行以下操作:
-
从磁盘上的文件中提取字节到缓冲区;
-
对缓冲区执行3次检查,查看检查的索引是否与硬编码值匹配;
-
如果通过所有检查则发生段错误,如果有一个检查失败则退出。
我们的简单模糊测试器会执行以下操作:
-
从磁盘上的有效jpeg文件中提取字节到字节缓冲区;
-
通过随机字节覆盖变异缓冲区中2%的字节;
-
将变异后的文件写入磁盘;
-
通过在每次模糊测试迭代中执行
fork()
和execvp()
将变异后的文件传递给目标二进制文件。
如你所见,这涉及大量的文件系统交互和系统调用。让我们在我们的漏洞二进制文件上使用strace
,看看二进制文件发出了哪些系统调用(为了便于测试,在这篇文章中,我已经将.jpeg
文件硬编码到漏洞二进制文件中,这样我们就不必使用命令行参数):
execve("/usr/bin/vuln", ["vuln"], 0x7ffe284810a0 /* 52 vars */) = 0
brk(NULL) = 0x55664f046000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=88784, ...}) = 0
mmap(NULL, 88784, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f0793d2e000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "177ELF2113 3 >