本文是对于模糊测试方面的小总结,计划三篇;第一篇概述;第二篇afl;第三篇Winafl。非大部头书,只提取关键信息。
本篇为第二篇:afl。

Part 2 总览

  • 什么是AFL

  • 它是如何工作的?

  • Fork 模式 VS 持久性(Persistent)模式

  • 模糊测试策略

  • Sanitizers(消毒剂?)—— ASAN,UBSAN, MSAN,TSAN

  • 使用AFL(练习)

    • 如何编译一个简单的C程序?
    • 对一个简单的C程序进行AFL模糊测试
    • RCA 根本原因分析
    • 崩溃(Crash)分类
  • 使用AFL模糊测试真实世界的程序

    • 使用AFL模糊测试一个开源软件
  • 总结

什么是AFL?

  • American Fuzzy Lop
  • 由Michael Zelwaski创建的
  • 一种由插桩后覆盖率引导的Fuzzer
  • 编译后附带一套实用程序:
    • afl-fuzz, afl-cmin, afl-tmin, afl-showmapetc..
  • Fork server模式/Persistent mode模式
    • Fork server mode —— 不断创建进程的副本
    • Persistentmode —— 着重于一个程序疯狂循环
  • 根据各种策略对文件进行突变
    • Bitflip, byteflip, havoc, splice etc.

它是如何工作的?

  • 在编译时使用特殊的方法插桩

  • 它提供自带的编译器包装器(wrapper)

    • afl-gcc,afl-g++, afl-clang, afl-clang++, afl-clang-fast, afl-clang-fast++
  • 使用二进制重写技术

    • 在每个基础块(BB块)上进行插桩
      • 每个基本块都有一个唯一的随机ID
  • 其插桩可以由以下伪代码所表示:

image-20210305150453876

  • A → B →C → D→ E 与 A → B → D → C→ E

image-20210305150532627

Fork 模式 VS 持久性(Persistent)模式

模糊测试策略

  • bitflip,按位翻转,1变为0,0变为1
    • 1/1,2/1,4/1,8/8 ….32/8
      • STAGE_FLIP1 每次翻转一位(1 bit),按一位步长从头开始。
        STAGE_FLIP2 每次翻转相邻两位(2 bit),按一位步长从头开始。
        STAGE_FLIP4 每次翻转相邻四位(4 bit),按一位步长从头开始。
        STAGE_FLIP8 每次翻转相邻八位(8 bit),按八位步长从头开始,也就是说,每次对一个byte做翻转变化。
        STAGE_FLIP16每次翻转相邻十六位(16 bit),按八位步长从头开始,每次对一个word做翻转变化。
        STAGE_FLIP32每次翻转相邻三十二位(32 bit),按八位步长从头开始,每次对一个dword做翻转变化。
  • arithmetic,整数加/减算术运算
  • interest,把一些特殊内容替换到原文件中
  • dictionary,把自动生成或用户提供的token替换/插入到原文件中
  • havoc,中文意思是“大破坏”,此阶段会对原文件进行大量变异
  • splice,中文意思是“绞接”,此阶段会将两个文件拼接起来得到一个新的文件

其中,前四项bitflip, arithmetic, interest, dictionary是非dumb mode(-d)和主fuzzer(-M)会进行的操作,由于其变异方式没有随机性,所以也称为deterministic fuzzing;havoc和splice则存在随机性,是所有状况的fuzzer(是否dumb mode、主从fuzzer)都会执行的变异。

Sanitizers(消毒剂?)

  • 基于编译器的检测工具
  • 有助于发现BUGs
  • 可以发现一些正常模式不好检出的错误,例如大内存分配,堆溢出,UAF等
  • 不同类型的Sanitizers
    • ASAN
    • MSAN
    • UBSAN
    • TSAN

ASAN(Address Sanitizer)

  • 编译时指令: -fsanitize=address

  • 可以检测各种问题,例如UAF,堆缓冲区溢出,内存泄漏等

  • ASAN + Fuzzer = More bugs!

  • 有大牛表示:“AFL Fuzzing without ASAN is just a waste of CPU”

  • Ref:https://clang.llvm.org/docs/AddressSanitizer.html

image-20210305153545094

UBSAN(UndefinedBehavior Sanitizer)

image-20210305153956345

Memory Sanitizer(MSAN)

Thread Sanitizer(TSAN)

使用AFL(练习)

编译并安装AFL

如果安的是AFL++,直接用带的脚本一键安装完事儿

如何用AFL编译一个程序?

image-20210306101946651

  • afl-clang : gcc的编译器包装器,它将编译并插桩二进制文件
  • -fsanitize=address : 开启ASAN ( 用AFL_USE_ASAN=1也行)
  • -g : 调试符号支持
  • imgRead.c : 源文件
  • imgReadafl :生成可被模糊测试的可执行文件

使用AFL开始模糊测试

  • 生成一个初始输入
    • echo “IMG” > input/1.img
  • afl-fuzz –iinput –o output –m none –./imgRead@@
    • afl-fuzz : fuzzer的二进制程序
    • -i : 包含输入种子文件的目录
    • -o : Fuzzer的输出数据的目录
      • Crashes : 包含使目标程序崩溃的输入文件
      • Hangs : 包含导致目标程序挂起(hang)的输入文件
    • -m : 内存限制,如果为ASAN和64位,则将其设置为none
      • 否则使用编译器标志–m32将其编译为32位
      • 将内存限制设置为–m 800
      • 查找更多crashes
    • -M : 主实例Master模式(如果您具有多个CPU内核)
    • -S : 从实例Slaver,可以为任意n数,具体取决于您拥有的内核数

根本原因分析(RCA)

  • 使用GDB对Crashes进行分析
  • 常用命令:
    • Gdb
    • r -> 开始运行目标程序
    • s -> 步步运行程序
    • Next/fi-> 一直运行到return
    • b filename.c:linenumber -> 在对应filename.c中的 linenumber行数打断点(break)
  • 在我们的例子中:
    • r <output/crashes/filename>

崩溃(Crash)分类

  • 如果遇到很多崩溃,Crashwalk是一个有用的工具,可以对崩溃进行分类

  • 安装Crashwalk

    • sudo apt-get install golang
    • go get -u github.com/bnagy/crashwalk/cmd/…
    • ~/go/bin
  • 安装 exploitable

  • Cwtriage——分类崩溃的实用程序

    • ASAN_OPTIONS=”abort_on_error=1:symbolize=0” Cwtriage –afl –root output
    • 分析每个崩溃文件并将结果保存在crashwalk.db中
    • 使用ASAN运行,否则崩溃将无法复现
  • Cwdump——从crashwalk.db转储崩溃信息的实用程序

    • Cwdump crashwalk.db

使用AFL模糊测试一个开源软件

Fuzz tcpdump

  • 先把tcpdump和libcap的源码dump下来
  • 使用AFL对其进行编译
    • CC=afl-gcc CFLAGS=”-g -fsanitize=address -fno-omit-frame-pointer” LDFLAGS=”-g -fsanitize=address -fno-omit-frame-pointer” ./configure
    • sudo make && make install
  • 语料库从哪来??
    • 查看一下项目自带的test文件夹
    • 收集别的符合条件的文件,比如issue页面
    • 精简它们:afl-cmin–i tests –o mincorpus –m none –./tcpdump –vv –ee –nnr @@
  • Fuzz 它
    • afl-fuzz –I mincorpus –o fuzzoutput –m none – ./tcpdump –vv –ee –nnr @@

Fuzz libtiff

  • 获取源码:
  • 用AFL编译
    • ./autogen.sh
    • CC=afl-gcc CXX=afl-g++ CFLAGS=”-g -fsanitize=address -fno-omit-frame-pointer” CXXFLAGS=”-g -fsanitize=address -fno-omit-frame-pointer” LDFLAGS=” -g -fsanitize=address -fno-omit-frame-pointer” ./configure
    • sudo make && make install
  • 收集语料库
  • 精简它们
    • afl-cmin–itiff –o mintiff–./tiff2rgba @@ test.tiff
  • Fuzz 它们
    • afl-fuzz –i <*input> -o fuzzoutput –m none – tiff2rgba @@ test.tiff

总结

  • 使用AFL在linux上fuzz开源软件十分简单
  • 大多数的lib/开源软件的源代码都是可以获取的
  • 在编译时插桩是可行的
  • 在Fuzz时要用ASAN,MSAN,UBSAN
  • 有时最需要花费时间的过程是项目编译
    • 缺失引用的第三方库(lib)
    • 编译过程中的各种错误
    • 不同项目不同的编译方法与各种选项