[afl-training] libxml2
libxml2 是一个流行的 XML 库,这类库是非常适合用来做 fuzzing ,理由如下:
- 经常需要解析用户提供的数据
- 库是由不安全语言编写(例如 C、C++)
- 无状态
- 没有网络和文件系统交互
- 官方提供的 API 就是很好的 fuzz 目标,无需额外去分析和识别库内部的组件关系
- 运行速度快
这次 fuzz 挑战的目标是在库中寻找 CVE-2015-8317,需要使用 AFL 对库源代码进行编译插桩,并且加上 ASAN 选项:
1 | git submodule init && git submodule update |
使用 AFL_USE_ASAN=1 是开启 ASAN 辅助,这是基于 clang 的一个内存错误检测器,可以检测到常见的内存漏洞,例如栈溢出、堆溢出、double free、uaf 等等。
编写 harness
在之前的 harness 章节就讲到,fuzz 一个库的基本流程是:
- 对库使用 AFL 进行编译插桩
- 通过相关的官方文档知道库中的 API 是如何被正常调用的
- 写一个类似的 harness 调用 API,使得 AFL 产生的输入可以喂给 API 执行,并编译插桩 harness
- 使用 afl-fuzz 对 harness 进行 fuzz
我们已经使用 afl-clang-fast 编译了 libxml2 库,那么接下来就是去官方文档中查看正常情况下正确调用库 API 的案例,libxml2 的官方文档在此处,在 fuzz 的时候可以参考此案例,对库的 xmlReadMemory 函数进行 fuzz。如下是官方提供的 API 调用案例,读取 XML 文件到树上,并释放。
1 | /** |
那么我们可以根据上面的案例写出一个 harness,这个在挑战的 ANSWERS.md 中有:
1 |
|
编写完 harness.c 后插桩编译,-I 选项是指定包含的头文件目录,然后接上 libxml2 的静态链接库,-lz 是使用 zlib 库,-lm 是使用 math 库,然后编译出来的是一个将 libxml2 静态链接的可执行文件,这样在编译插桩的时候就可以直接对 libxml2 的汇编代码进行插桩(如果之前已经对 libxml2 进行了插桩编译,应该就不需要再静态编译了)。
1 | AFL_USE_ASAN=1 afl-clang-fast ./harness.c -I libxml2/include libxml2/.libs/libxml2.a -lz -lm -o fuzzer |
编写完 harness 后,就需要使用高质量的种子来启动 afl-fuzz,afl 的源码中提供了一个不错的 XML 字典,可以就使用它来作为初始种子。
1 | mkdir input |
然后开始 fuzz,-x 是设定 fuzzer 的字典,@@ 类似于占位符,表示输入的位置,因为 harness 使用的是 argv 作为输入。
1 | afl-fuzz -i in -o out -x ~/Code/AFL/dictionaries/xml.dict ./fuzzer @@ |
然后让 AFL 在后台运行吧,等待结果,用虚拟机跑了一天,挖出来 16 个 crashes
处理 crashes
我们写出来的 harness 是通过命令参数读取文件,那么直接将 output/crashes 中的文件给程序,就会报错。而且因为编译的时候使用了 ASAN 标志,会有详细的报错信息提醒。跑出来的 16 个 unique creashes 都是相同的报错:
通过上图中的函数堆栈回溯,我们可以定位错误的性质是一个字节的堆溢出,而且漏洞是发生在 libxml2/parse.c 文件中。个人对堆了解得不是很深入,就在这个地方吧,埋一个坑,以后有空更新。