使用honggfuzz进行Android模糊测试

admin 2024年2月23日17:23:30评论27 views字数 6901阅读23分0秒阅读模式
honggfuzz是由Google维护的高效反馈驱动模糊测试引擎,可以良好支持Linux、 FreeBSD、NetBSD、Mac OS、Windows和Android等平台使用,被包括OSS-Fuzz在内的多个大型模糊测试项目使用,斩获过众多CVE漏洞。对于Android平台,honggfuzz可以直接复用AOSP的模糊测试用例代码,易于学习与移植。本文将简单介绍honggfuzz在Android平台的使用方法,通过源码解释关键模块实现原理。

使用honggfuzz进行Android模糊测试
一、honggfuzz使用
1.1 在AOSP部署honggfuzz

honggfuzz目前没有像libFuzzer/AFL++一样直接集成在AOSP中,需要单独编译后合入AOSP环境:

1. clone honggfuzz源码(仓库地址见参考链接2),按官方文档交叉编译arm或arm64版本,应包含三个静态库libhfcommon.a、libhfuzz.a、libhfnetdriver.a,以及一个手机端使用的测试启动器honggfuzz。

2. 将上述三个静态库拷贝到AOSP中,编写Android.bp或Android.mk以预编译静态库模块添加到soong。

使用honggfuzz进行Android模糊测试

图1-1

3.在AOSP根目录使用cat build/soong/cc/config/global.go | grep ClangDefaultVersion命令查看AOSP使用的clang工具版本,如果对应版本lib/clang/XX/lib/linux目录下存在libclang_rt.fuzzer-aarch64-android.a静态库,需要以空文件替换防止插桩冲突。

进行完以上步骤后,就得到了可以进行honggfuzz插桩的AOSP编译环境。在编译包含LLVMFuzzerTestOneInput()函数的模糊测试用例时,使用cc_fuzz(Android.bp)或BUILD_FUZZ_TEST(Android.mk)标签,并在静态库中指定链接libhfcommon.a、libhfuzz.a、libhfnetdriver.a。

模糊测试时,通过启动器honggfuzz指定测试运行参数,多线程启动模糊测试用例,监测并记录异常。

使用honggfuzz进行Android模糊测试

图1-2

1.2 在Android运行honggfuzz

在adb shell运行honggfuzz模糊测试用例,以AOSP提供的fmq_fuzzer源码,按上文方法编译的fmq_fuzzer_honggfuzz,运行命令:

honggfuzz -z -i input -W result --crashdir crash -- ./fmq_fuzzer_honggfuzz
使用honggfuzz进行Android模糊测试

图1-3

Iterations: 目前已进行的测试轮数。
Mode: 测试模式。共有三种主要模式,Feedback Driven Dry Run模式从输入用例目录(语料库)中读文件并输入测试入口;Feedback Driven Run模式基于保存的输入用例进行反馈驱动的变异,产生新输入;而Switching to the Feedback Driven模式是在多线程模式下,等待所有线程切换到Feedback Driven Run模式的过度模式。
Target: 模糊测试用例名。
Threads: 线程数及CPU使用情况。
Speed: 每秒输入的语料数量。
Crashes: 崩溃次数。honggfuzz不会在崩溃发生时停止,而是记录并继续,根据堆栈信息判断是否为相同崩溃情况。
Timeouts: 超时次数及目前设置的超时时间。
Corpus Size: 当前输入用例大小。
Cov Update: 据上次覆盖率变化的间隔时长。
Coverage: 覆盖率信息。统计边、块和比较指令触发情况综合反馈。
LOGS:这部分为各子线程输入用例大小、单次执行耗时、覆盖率新增及总数情况,若检测到异常也将在该区域打印。

使用honggfuzz进行Android模糊测试
二、honggfuzz关键源码解析

截至本文编写时,honggfuzz更新到2.6版本,下文均以该版本进行源码分析。下图为honggfuzz的框架,本章节涉及到的模块用深绿色标记。

使用honggfuzz进行Android模糊测试

图2-1

2.1 多线程启动
honggfuzz在启动器中实现了多线程创建和调控。

honggfuzz.h头文件定义了的honggfuzz_t结构体,用来存储用户从命令行输入的模糊测试参数,比如用例运行参数、输入输出参数、变异参数等等。

使用honggfuzz进行Android模糊测试

图2-2

启动器main()函数在分割命令行输入,初始化honggfuzz_t结构体后,会将其传输入fuzz.c的fuzz_threadsStart()函数,根据命令行指定的线程数创建线程池。
使用honggfuzz进行Android模糊测试

图2-3

子线程调用subproc.c的subproc_prepareExecvArgs()函数来准备模糊测试用例执行命令,此处run->args拷贝的run->global->exe.cmdline字符串数组,就是命令行参数分割后,“--”后指定的模糊测试用例elf及其运行参数。

使用honggfuzz进行Android模糊测试

图2-4

然后子线程就可以通过arch.c的arch_launchChild就可以通过execve()函数执行模糊测试用例了。

使用honggfuzz进行Android模糊测试

图2-5

persistent.c实现了模糊测试用例的main()函数,使用从启动器传来的run->args中elf的启动参数初始化用例。子线程每次数据变异后都只通过HonggfuzzRunFromFile()函数执行一次输入测试。

使用honggfuzz进行Android模糊测试

图2-6

最后通过LLVMFuzzerTestOneInput()函数执行我们实现的单次测试逻辑。

使用honggfuzz进行Android模糊测试

图2-7

特别强调,启动器创建的每一个子线程都具有相互独立的内存空间,模糊测试用例源码的全局变量和LLVMFuzzerInitialize初始化的参数均位于各子线程的内存空间中。这导致honggfuzz即使实现了多线程,也无法识别部分条件竞争漏洞。

2.2 跨线程覆盖率共享

honggfuzz支持多种代码覆盖率获取方式,在Linux中它可以通过基于硬件的计数器、Intel BTS或Intel PT以及GCC/Clang的软件覆盖率插桩实现反馈驱动。但对于Android平台而言,使用软件覆盖率插桩是唯一可行的方法。

图2-1列举了部分honggfuzz修改的覆盖率插桩函数,它们在libhfuzz.a的instrument.c实现,沿用了LLVM交叉编译工具链覆盖率插桩的函数名。

使用honggfuzz进行Android模糊测试

图2-8

编译honggfuzz获得静态库及启动器的同时,也会产生插桩编译器hfuzz-cc,它的主要作用是调用默认编译工具,并以编译选项的形式链接honggfuzz静态库,进行各类插桩。而AOSP编译环境中需要以cc_fuzz或BUILD_FUZZ_TEST来链接模糊测试用例相关库时,会自动链接到LLVM工具链静态库,导致相关函数无法正常被libhfuzz.a库hook。这导致上文部署时必须以空文件替换原始工具链中包含插桩函数的静态库,否则编译时会发生函数符号重定义错误。

为了保证多线程之间覆盖率共享,honggfuzz通过跨线程的全局结构体globalCovFeedback存储信息。

使用honggfuzz进行Android模糊测试

图2-9

使用honggfuzz进行Android模糊测试

图2-10

pcGuardMap数组反映了插桩函数被调用情况。以__sanitizer_cov_trace_pc_guard()函数为例,其执行流程中会将localCovFeedback->pcGuardMap[*guard_ptr]计数加1。若调用次数已经超过100,honggfuzz就会认为这部分已经经过足够测试,通过原子操作将guard_ptr指向的值置为0,未来不再记录相关信息。接下来会依据pcGuardMap数组已存储的值更新总覆盖率和子线程覆盖率情况,以便honggfuzz进行状态监测。

使用honggfuzz进行Android模糊测试

图2-11

2.3 变异策略

模糊测试子线程的每轮迭代中,在通过subproc_Run()函数调用LLVMFuzzerTestOneInput()函数前,会先调用fuzz_fetchInput()函数决定该轮喂给模糊测试用例的具体语料。

使用honggfuzz进行Android模糊测试

图2-12

fuzz_fetchInput()函数的三个条件分支,分别实现了最小化语料库、动态模糊测试和静态模糊测试的逻辑。最小化语料库不涉及输入用例的变异,我们主要关心后两者。静态模糊测试不启用任何覆盖率反馈,也就是进行Dumb Fuzz;动态模糊测试则是我们一般使用的反馈驱动模糊测试模式。无论哪种模式,都会先判断run->global->exe.externalCommand和run->global->exe.feedbackMutateCommand的值,他们表示两种自定义变异策略加载模式,由命令行输入。如果这两个参数有值,input_prepareDynamicInput()和input_prepareStaticFile()函数的最后一个参数就会置为false,不启用honggfuzz的mangle_mangleContent()函数进行语料变异。

使用honggfuzz进行Android模糊测试

图2-13

以反馈驱动模糊测试模式的为例,input_prepareDynamicInput()函数会遍历语料库,并计算两个关键参数。skip_factor表征当前语料的重要程度,在语料未变异出其他有效语料、长度过大时,honggfuzz会把它输入的概率降低;speed_factor通过平均每轮迭代运行时长和总运行时长综合算出,动态调整每次语料变异的幅度。

使用honggfuzz进行Android模糊测试

图2-14

mangle.c目前实现了20个变异函数接口,在mangle_mangleContent()函数中以函数指针的形式存储在mangleFuncs数组中。

使用honggfuzz进行Android模糊测试

图2-15

run->global->mutate.mutationsPerRun是可通过命令行指定的变异率参数,默认为5。在每轮不同的speed_factor控制下,从mangleFuncs数组中随机取函数对语料进行changesCnt次修改,实现语料变异。

使用honggfuzz进行Android模糊测试

图2-16

学习honggfuzz的变异策略有助于我们实现自定义变异规则,提高模糊测试效率。下面分析各变异函数作用。

mangle_Shrink

选取随机偏移位置和随机长度确定一个数据块,从语料中删除这块数据。

使用honggfuzz进行Android模糊测试

图2-17

使用honggfuzz进行Android模糊测试

图2-18

mangle_Expand

选取随机偏移位置和随机长度确定一个数据块,通过mangle_Inflate()函数将语料从偏移位置off到结尾向后移动随机长度,效果上是将数据块进行了一次拷贝并插入偏移位置。printable参数由命令行指定,代表是否需要honggfuzz生成的语料是可打印的,如果为true,则将插入的数据块全部替换为空格。

使用honggfuzz进行Android模糊测试

图2-19

这种扩展语料长度的形式被称为膨胀(Inflate)。

使用honggfuzz进行Android模糊测试

图2-20

mangle_Bit

随机选取语料的一个字节,将其随机一位取反。

使用honggfuzz进行Android模糊测试

图2-21

如果传入的printable参数为true,则通过util_turnToPrintable()函数将该字节映射为ASCII码表32~126中的一个可打印字符。

使用honggfuzz进行Android模糊测试

图2-22

mangle_IncByte

随机选取语料的一个字节,将其值加1。如果传入的printable参数为true,则将该字节映射为可打印字符。

使用honggfuzz进行Android模糊测试

图2-23

mangle_DecByte

随机选取语料的一个字节,将其值减1。如果传入的printable参数为true,则将该字节映射为可打印字符。

使用honggfuzz进行Android模糊测试

图2-24

mangle_NegByte

随机选取语料的一个字节,将该字节取反。如果传入的printable参数为true,会将其变为ASCII码表32~126可打印字符中关于79的对称位置字符(比如33’!’变为125’}’)。

使用honggfuzz进行Android模糊测试

图2-25


mangle_AddSub

选取语料中一个偏移位置,随机对1/2/4/8字节内容进行加值。如果传入的printable参数为true,则将涉及到的所有字节映射为可打印字符,映射不对1字节加值操作生效。

使用honggfuzz进行Android模糊测试

图2-26


mangle_MemSet

选取语料中一个偏移位置,50%概率将后续随机长度用val随机值覆盖,50%概率对偏移位置随机长度数据块进行膨胀。如果传入的printable参数为true,则val为可打印字符。

使用honggfuzz进行Android模糊测试

图2-27


mangle_MemClr

选取语料中一个偏移位置,50%将后续随机长度用0覆盖,50%对偏移位置随机长度数据块进行膨胀。如果传入的printable参数为true,则val为空格。

使用honggfuzz进行Android模糊测试

图2-28


mangle_MemSwap

随机确定两个数据块,以这两个数据块中较小的长度交换两块内容。

使用honggfuzz进行Android模糊测试

图2-29

使用honggfuzz进行Android模糊测试

图2-30

mangle_MemCopy

选取一个随机数据块,mangle_UseValue()函数有50%概率将它插入到语料的随机位置,50%概率使用它覆盖语料随机位置。如图所示,该操作的多种情况中有可能影响到原数据块位置,所以honggfuzz将原始数据块事先存放在tmpbuf中供拷贝使用,防止出现非预期变异效果。

使用honggfuzz进行Android模糊测试

图2-31

如图所示,该操作的多种情况中有可能影响到原数据块位置,所以honggfuzz将原始数据块事先存放在tmpbuf中供拷贝使用,防止出现非预期变异效果。

使用honggfuzz进行Android模糊测试

图2-32

mangle_Bytes

在语料随机位置插入或覆盖1~2个字节。如果传入的printable参数为true,则这些随机字节是可打印字符。

使用honggfuzz进行Android模糊测试

图2-33

mangle_ASCIINum

在语料随机位置插入或覆盖2~8个随机字节。如果传入的printable参数为true,则这些随机字节是可打印字符。

使用honggfuzz进行Android模糊测试

图2-34

mangle_ASCIINumChange

在语料中找到一个十进制数字字符串,最大长度为20位。

使用honggfuzz进行Android模糊测试

图2-35

然后随机对这个十进制值进行加1/减1/加倍/减半/重新随机/加随机值/减随机值/取反的操作,并在原始字符串的起始位置进行插入或覆盖。如果传入的printable参数为true,则先将处理后数字临时字符串按位映射为可打印字符后再插入或覆盖。

使用honggfuzz进行Android模糊测试

图2-36


mangle_ByteRepeat

在语料中随机选取偏移位置后,50%概率不改变语料长度将偏移位置后全部内容替换为偏移位置字节,50%概率对偏移位置随机长度数据块进行膨胀。如果偏移位置是语料的结尾,则进行mangle_Bytes。如果传入的printable参数为true,膨胀时会将数据块内容映射为可打印字符。

使用honggfuzz进行Android模糊测试

图2-37

mangle_Magic

mangleMagicVals数组存放了多种长度、不同字节序的特殊边界值。

使用honggfuzz进行Android模糊测试

图2-38

在mangleMagicVals数组中随机选取一个边界值,在语料中随机位置插入该边界值或使用该边界值覆盖原数据。如果传入的printable参数为true,则将边界值按位映射为可打印字符后再插入或覆盖。

使用honggfuzz进行Android模糊测试

图2-39

mangle_StaticDict

从命令行指定的字典文件中读取一条内容,插入或覆盖语料的随机位置。如果传入的printable参数为true,则将内容按位映射为可打印字符后再插入或覆盖。

使用honggfuzz进行Android模糊测试

图2-40

mangle_ConstFeedbackDict

honggfuzz可以通过命令行启用常量反馈字典,自动收集对覆盖率增长起到有效作用的值。mangle_ConstFeedbackDict方法将常量反馈字典中的一条内容在语料的随机位置插入或覆盖。若反馈字典不存在,则执行mangle_Bytes。如果传入的printable参数为true,则将内容按位映射为可打印字符后再插入或覆盖。

使用honggfuzz进行Android模糊测试

图2-41


mangle_RandomBuf

选取语料随机位置,50%概率对随机大小的数据块进行膨胀,50%概率将数据块以随机值重新填充。如果传入的printable参数为true,膨胀时会将数据块内容映射为可打印字符。

使用honggfuzz进行Android模糊测试

图2-42

mangle_Splice

通过input_getRandomInputAsBuf()函数读取输入文件的内容存入buf,在语料的随机位置插入buf的随机一块数据,任意一处不成功将执行mangle_Bytes。如果传入的printable参数为true,则将buf随机数据块的内容按位映射为可打印字符后再插入或覆盖。

使用honggfuzz进行Android模糊测试

图2-43

使用honggfuzz进行Android模糊测试
三、总结

honggfuzz以多线程模式充分利用了CPU性能,在同平台下超过了AFL与libFuzzer。它也为我们提供了一种面向多平台的模块化模糊测试架构,使我们可以方便地定制各个模块,包括模糊测试引擎、覆盖率反馈插桩、信息收集和处理等等。安全研究人员可以根据实际需求进行二次开发,编写针对性的变异策略,进一步提升模糊测试效率,发现更多漏洞。

相关链接

1. honggfuzz主页: https://honggfuzz.dev

2. honggfuzz仓库: https://github.com/google/honggfuzz.git

3. honggfuzz Android平台使用手册: https://github.com/google/honggfuzz/blob/master/docs/Android.md

END

原文始发于微信公众号(OPPO安珀实验室):使用honggfuzz进行Android模糊测试

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年2月23日17:23:30
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   使用honggfuzz进行Android模糊测试http://cn-sec.com/archives/2519798.html

发表评论

匿名网友 填写信息