LibFuzzer 基本使用

news2024/12/29 7:43:48

文章目录

  • 前言
  • 环境搭建
  • 基础使用
    • 编写 fuzz target
    • 编译链接
    • demo 测试 && 输出日志分析
    • 心脏滴血漏洞测试
  • 提高代码覆盖率和测试速度
    • 指定种子语料库
    • 多核并行 Fuzz
    • 使用字典
  • 参考

前言

相较于 AFL 来说,LibFuzzer 在单个进程内完成模糊测试,以此来避免的反复启动进程的开销,所以理论上其比 AFL 的效率更高

对于 LibFuzzer 官方给出如下定义

LibFuzzer is in-process, coverage-guided, evolutionary fuzzing engine.
LibFuzzer is linked with the library under test, and feeds fuzzed inputs to the library via a specific fuzzing entrypoint (aka “target function”);
the fuzzer then tracks which areas of the code are reached, and generates mutations on the corpus of input data in order to maximize the code coverage.
The code coverage information for libFuzzer is provided by LLVM’s SanitizerCoverage instrumentation.

按照官方定义,libFuzzer 是一个 in-process(进程内的),coverage-guided(以覆盖率为引导的),evolutionary(进化的) 的 fuzz 引擎,是 LLVM 项目的一部分

  • in-process(进程内的):LibFuzzer 并没有为每一个测试用例都开启一个新进程,而是直接在内存中变异数据

  • coverage-guided(以覆盖率为引导的):LibFuzzer 对每一个输入都进行代码覆盖率的计算,并且不断积累这些测试用例以使代码覆盖率最大化

  • evolutionary(进化的):fuzz 按照类型分为3类,这是最后一种

    • 第一类是基于生成的 Generation Based。通过对目标协议或文件格式建模的方法,从零开始产生测试用例,没有先前的状态
    • 第二类是基于突变的 Evolutionary。基于一些规则,从已有的数据样本或存在的状态变异而来
    • 第三类是基于进化的 Evolutionary。包含了上述两种,同时会根据代码覆盖率的回馈进行变异

LibFuzzer 和要被测试的库链接在一起,通过一个特殊的模糊测试进入点(目标函数),用测试用例 feed(喂)要被测试的库fuzzer 会跟踪哪些代码区域已经测试过,然后在输入数据的语料库上产生变异,来最大化代码覆盖。其中代码覆盖的信息由 LLVMSanitizerCoverage 插桩提供

环境搭建

参考 官方 进行搭建即可

git clone https://github.com/Dor1s/libfuzzer-workshop
cd libfuzzer-workshop/libFuzzer
./Fuzzer/build.sh

编译成功后就获得了一个静态库文件 libFuzzer.a

LibFuzzerLLVM 项目的一部分,所以如果你安装了最新版的 clang,其是默认带有 LibFuzzer 库的,所以你可以不需要自己编译

基础使用

编写 fuzz target

LibFuzzer 已经提供了数据样本生成模块和异常检查模块,所以我们需要做的事情就是将 LibFuzzer 生成的数据样本提供给被测试目标,即实现模糊测试入口点,官方提供的编写模板如下:

// fuzz_target.cc
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
  DoSomethingInterestingWithMyAPI(Data, Size);
  return 0;  // Non-zero return values are reserved for future use.
}

注:函数名称、参数、返回值类型都不能动,并且注意参数中传来的字节数组 Data 是通过底层const修饰了的,也就是不允许修改其中数据

  • DataLibFuzzer 生成的测试数据,size 是数据的长度
  • DoSomethingInterestingWithMyAPI 函数就是我们实际要进行 fuzz 的函数(被测试目标)

官方文档中对被测试目标有如下要求:

  • 函数会在同一进程中多次执行,即被循环调用
  • 必须接受所有格式的输入
  • 不允许主动退出(即运行在内部直接调用 exit),前面说了是循环调用,退出了就没法循环了
  • 可以开线程,但返回之前必须结束它,原因还是那个——循环调用,自己的线程自己关
  • 其执行必须结果必须是具有确定性的,两次的 Data 如果一致,则两次执行的结果也必须一致
  • 不允许修改全局变量,因为在同一个进程里,修改全局变量会导致下一次运行时读取的是修改后的结果,可能会违反前面说的确定性原则
  • 尽量窄范围测试,如果测试处理多种数据格式的目标,还是分割成多个子目标为好。这既是处于速度考量,也是出于模糊测试数据变异的效果考量

编译链接

clang++ -g -O1 -fsanitize=fuzzer,address fuzz_target.cc /path/libFuzzer.a -o fuzzer_target

这里也可以不指定 libFuzzer.a 库,直接
clang++ -g -O1 -fsanitize=fuzzer,address fuzz_target.cc -o fuzzer_target

  • -g-O1gcc/clang 的通用选项,这两个选项不是必须的
  • -fsanitize=fuzzer 才是关键,通过这个选项启用 libFuzzer,向 libFuzzer 提供进程中的覆盖率信息,并与libFuzzer 运行时链接。
  • 还可以附加其他 sanitize 选项也可以加进来,如 -fsanitize=fuzzer,address 同时启用了地址检查

常用内存错误检测工具:

AddressSanitizer(ASAN): 检测 uaf, 缓冲区溢出,stack-use-after-return, container-overflow等内存访问错误,使用 -fsanitize = address

MemorySanitizer(MSAN): 检测未初始化内存的访问,使用 -fsanitize = memoryMSAN 不能与其他 sanitize 结合使用,应单独使用

UndefinedBehaviorSanitizer(UBSAN): 检测一些其他的漏洞,整数溢出,类型混淆等,检测到 C/C++ 的各种功能的使用,这些功能已明确列出来导致未定义的行为。使用 -fsanitize = undefined,也可以将 ASANUBSAN 合并到一个版本中

关于地址 sanitize 详细作用可以查看 llvm 的官方文档 AddressSanitizer

demo 测试 && 输出日志分析

这里给出一个 demo 进行测试:

#include <stdint.h>
#include <stddef.h>

bool FuzzMe(const uint8_t *Data, size_t DataSize) {
  return DataSize >= 3 &&
      Data[0] == 'X' &&
      Data[1] == 'X' &&
      Data[2] == 'O' &&
      Data[3] == 'L' &&
      Data[4] == 'M';  
}

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
  FuzzMe(Data, Size);
  return 0;
}

进行编译:

clang++ -g -O1 -fsanitize=fuzzer,address demo.cpp -o demo

编译完成后,生成的 demo 是一个可接受用户参数的命令行程序,格式如下:

./target -flag1=val1 -flag2=val2 ... dir1 dir2 ...

flags 代表控制测试过程的选项参数,可以提供零到任意个,但必须是严格的 -flag=value 形式

  • 选项前导用单横线,即使选项是一个词而非单个字符
  • 选项必须要提供对应的值,即使只是一个开关选项如 -help,必须要写作 -help=1,且选项与值中间只能用等号,不能用空格

dirs 表示语料库目录,它们的内容都会被读取作为初始语料库,但测试过程中生成的新输入只会被保存到第一个目录下

然后就可以直接运行 demo 开始 fuzz
在这里插入图片描述
这里简单看下输出日志:

  • Seed:随机数种子,可以使用 -seed=xx 进行指定
  • -max_len:测试输入的最大长度,不指定的话其会自行进行推测
  • corpus:初始语料库,可以通过 demo ./corpus 提供
  • # 开头的信息表示在 fuzz 的过程中覆盖的路径信息
  • INITED:表示 fuzzer 已完成初始化,其中包括被测代码运行每个初始输入样本
  • READ:表示 fuzzer 已从语料库目录中读取了所有提供的输入样本
  • NEW: 表示 fuzzer 创建的一个测试输入涵盖了被测代码的新区域。此输入将保存到主要语料库目录
  • pulse:表示 fuzzer 已生成 2^n 个输入(定期生成以使用户确信 fuzzer 仍在工作)
  • REDUCE:表示 fuzzer 发现了一个更好(更小)的输入,可以触发先前发现的特征(设置 -reduce_inputs=0 以禁用)
  • cov: num:表示执行当前语料库所覆盖的代码块或边的总数
  • ft: num:表示 LibFuzzer 使用不同的信号来评估代码覆盖率:边缘覆盖率,边缘计数器,值配置文件,间接调用方/被调用方对等
  • corp: 当前内存中测试语料库中的条目数及其大小(以字节为单位)
  • exec/s: 每秒模糊器迭代的次数
  • rss: 当前的内存消耗

这里我们可以添加一些参数选项:

./target -option=val

心脏滴血漏洞测试

fuzzer-test-suite 是谷歌提供的一个用于练习 fuzz 的项目。心脏滴血(HeartBleed)是 OpenSSL 加密库的一个堆溢出漏洞,fuzzer-test-suite 项目提供了 openssl-1.0.1f 版本

拉取上述项目让进入 openssl-1.0.1f 目录可以看到如下目录树:
在这里插入图片描述
里面有两个重要的文件 build.shtarget.cc,其中 build.sh 就是用来搭建 openssl 环境的,并且会编译 target.cc

#!/bin/bash
# Copyright 2016 Google Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
. $(dirname $0)/../custom-build.sh $1 $2
. $(dirname $0)/../common.sh

build_lib() {
  rm -rf BUILD
  cp -rf SRC BUILD
  # This version of openssl has unstable parallel make => Don't use `make -j `.
  (cd BUILD && CC="$CC $CFLAGS" ./config && make clean && make)
}

get_git_tag https://github.com/openssl/openssl.git OpenSSL_1_0_1f SRC
build_lib
build_fuzzer

if [[ $FUZZING_ENGINE == "hooks" ]]; then
  # Link ASan runtime so we can hook memcmp et al.
  LIB_FUZZING_ENGINE="$LIB_FUZZING_ENGINE -fsanitize=address"
fi
$CXX $CXXFLAGS $SCRIPT_DIR/target.cc -DCERT_PATH=\"$SCRIPT_DIR/\"  BUILD/libssl.a BUILD/libcrypto.a $LIB_FUZZING_ENGINE -I BUILD/include -o $EXECUTABLE_NAME_BASE
rm -rf runtime
cp -rf $SCRIPT_DIR/runtime .

来看下 target.cc

#include <openssl/ssl.h>
#include <openssl/err.h>
#include <assert.h>
#include <stdint.h>
#include <stddef.h>

SSL_CTX *Init() {
  SSL_library_init();
  SSL_load_error_strings();
  ERR_load_BIO_strings();
  OpenSSL_add_all_algorithms();
  SSL_CTX *sctx;
  assert (sctx = SSL_CTX_new(TLSv1_method()));
  /* These two file were created with this command:
      openssl req -x509 -newkey rsa:512 -keyout server.key \
     -out server.pem -days 9999 -nodes -subj /CN=a/
  */
  assert(SSL_CTX_use_certificate_file(sctx, "runtime/server.pem",
                                      SSL_FILETYPE_PEM));
  assert(SSL_CTX_use_PrivateKey_file(sctx, "runtime/server.key",
                                     SSL_FILETYPE_PEM));
  return sctx;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
  static SSL_CTX *sctx = Init();
  SSL *server = SSL_new(sctx);
  BIO *sinbio = BIO_new(BIO_s_mem());
  BIO *soutbio = BIO_new(BIO_s_mem());
  SSL_set_bio(server, sinbio, soutbio);
  SSL_set_accept_state(server);
  BIO_write(sinbio, Data, Size);
  SSL_do_handshake(server);
  SSL_free(server);
  return 0;
}

target 中启动了 openssl,然后往里面喂 libfuzzer 提供的数据

利用 build.sh 搭建好环境后,其会生成一个 openssl-1.0.1f-fsanitize_fuzzer 文件,运行即可开始 fuzzASAN 很快就检测到了堆溢出
在这里插入图片描述

提高代码覆盖率和测试速度

其实影响 LibFuzzer 效果的因素有很多,种子语料库的选择、模糊测试接口函数的编写、编译选项等等,这里仅仅看下几个比较简单的提高效率的方法

指定种子语料库

语料库的使用比较简单:

./target corpus_dir1 corpus_dir2 corpus_dir3 

后面的文件夹都会被当作语料库目录,但是变异生成的输入都会保存在第一个文件夹中,所以一般情况下,我们会让 corpus_dir1 是一个空文件夹,这样就可以获取一个比较好的种子语料库

然后变异过程中可能产生很多重复的输入,这就使得保存的输入种子存在大量重复(这里重复指的是效果一样,而不是说内容完全一样),这时我们可以使用 -merge=1 标志去精简语料库

这里以 fuzzer-test-suite 项目的 woff2-2016-05-06 为例来看看设置语料库和不设置语料库代码的覆盖率,环境构建:

mkdir woff2
cd woff2
../woff2-2016-05-06/build.sh

环境构建成功后可以在目录下看到 seeds 文件夹,其是官方提供的 fuzz 语料库
在这里插入图片描述
不指定语料库 fuzz

这里我们直接执行 woff2-2016-05-06-fsanitize_fuzzer,效果如下:
在这里插入图片描述
笔者没有跑出 crash,共执行了大概 4 百万次,代码覆盖基本块也只有 800 多个,整体的效果比较差

指定语料库 fuzz

先对 seeds 进行下精简,然后在进行 fuzz

./woff2-2016-05-06-fsanitize_fuzzer -merge=1 corpus_min/ seeds/
./woff2-2016-05-06-fsanitize_fuzzer corpus/ corpus_min/

在这里插入图片描述
可以看到这里执行了 10 多次,代码覆盖基本块就到达了 900 多,所以提供一些种子语料库能够有效的加快 Fuzz 的速度

这里笔者继续跑着,看看能不能跑出 crash

最后差不多跑了 10 多分钟吧,跑了个堆溢出
在这里插入图片描述
但是可以看到这里总共执行了 4 百多万次,但是覆盖基本块有接近 1000,而且 corp628/40Mb
在这里插入图片描述

多核并行 Fuzz

这里跟 AFL 类似,不同 Fuzzer 之间可以共享语料库,在 LibFuzzer 中使用 -jobs=N 指定 fuzzing job 的数量。然后这些 fuzzing job 会由 worker 进行处理,worker 的数量默认为 CPU 数量的一半。如果 -job=20 运行在 16 核的机器上,则默认执行 8 个 worker 处理这 20 个 fuzzing lab

由于笔者虚拟机是 4 核的机器,所以只有两个 worker,所以其实跟直接运行没啥区别,但是如果的你 CPU 核心比较多的话,速度肯定是会大大提升的
在这里插入图片描述

使用字典

首先需要明确的是为什么要使用字典。其实这里跟 AFL 中类似,对于有些数据其是有特定格式的,比如 png 图片有特定的 png 头,xml 文件有特定格式,而变异过程是随机不确定的,这时就有极大的可能导致数据的特定格式被破坏,从而导致 target 一直拒绝。

LibFuzzer 中使用字典只需要设置 -dict=dict_dir 即可,例子后面做 libfuzzer lab 时就有了

参考

https://github.com/google/fuzzing/blob/master/tutorial/libFuzzerTutorial.md
libFuzzer使用总结教程
入门libFuzzer——编译链接
afl-fuzz: making up grammar with a dictionary in hand

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1540907.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Nacos部署(一)Linux部署Nacos2.3.x单机环境

&#x1f60a; 作者&#xff1a; 一恍过去 &#x1f496; 主页&#xff1a; https://blog.csdn.net/zhuocailing3390 &#x1f38a; 社区&#xff1a; Java技术栈交流 &#x1f389; 主题&#xff1a; Nacos部署&#xff08;一&#xff09;Linux部署Nacos2.3.x单机环境 ⏱️…

【NC20313】仪仗队

题目 仪仗队 欧拉函数&#xff0c;找规律 思路 这好像是一道非常简单的找规律问题&#xff0c;所以你从 1 1 1 开始枚举&#xff0c;计算出当 N i Ni Ni 时的结果 a n s i ans_i ansi​&#xff0c;所以你得出了以下结果&#xff1a; Nans10233549513621725837 令人失望…

招聘自媒体编辑岗位的人才测评方案

人才测评工具在招聘入职的方案&#xff0c;在线工具网根据自媒体岗位的特性和需求来分析&#xff0c;并制定自媒体主编的测评方案。 自媒体作为互联网时代的产物&#xff0c;自然也为我们带来了很多的福利&#xff0c;例如&#xff1a;海量的信息、快捷的传媒方式&#xff0c;那…

学习次模函数-第2章 定义

纵观本专著&#xff0c;我们认为及其幂集&#xff08;即&#xff0c; 所有子集的集合&#xff09;&#xff0c;其基数为。我们也考虑一个实值集函数&#xff0c;使得。 与凸函数的一般约定相反&#xff08;见附录A&#xff09;&#xff0c;我们不允许函数有无穷大的值。 次模分…

一文搞懂数据链路层

数据链路层 1. 简介2. MAC3. 以太网 1. 简介 &#xff08;1&#xff09;概念 链路(link)是一条无源的点到点的物理线路段&#xff0c;中间没有任何其他的交换结点。 数据链路(data link) 除了物理线路&#xff08;双绞线电缆、同轴电缆、光线等介质&#xff09;外&#xff0…

Java获取方法参数名称方案||SpringBoot配置顺序注解

一: Java获取方法参数名称的方法 普盲: getDeclaredMethods与getMethods的的区别 1、getMethods返回一个包含某些 Method 对象的数组&#xff0c;这些对象反映此 Class 对象所表示的类或接口的公共 member 方法。 2、getDeclaredMethods返回 Method 对象的一个数组&#xff0c…

STM32+ESP8266水墨屏天气时钟:简易多级菜单(数组查表法)

项目背景 本次的水墨屏幕项目需要做一个多级菜单的显示&#xff0c;所以写出来一起学习&#xff0c;本篇文章不单单适合于水墨屏&#xff0c;像0.96OLED屏幕也适用&#xff0c;区别就是修改显示函数。 设计思路 多级菜单的实现&#xff0c;一般有两种实现的方法 1.通过双向…

HTTPS协议的工作原理:保护网络通信的安全盾牌

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

力扣每日一题 2024/3/24 零钱兑换

题目描述 用例说明 思路讲解 动态规划五步法 第一步确定dp数组的含义&#xff1a;dp[i]为凑到金额为i所用最少的硬币数量 第二步确定动态规划方程&#xff1a;凑足金额为j-coins[i]所需最少的硬币个数为dp[j-coins[i]]&#xff0c;那凑足金额为j所用的最少硬币数为dp[j-coin…

【C语言】C语言实现扫雷三子棋

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;C语言_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1.C语言实现三子棋 1.1 整体思路 1.2 游戏菜单的创建 1.3 游戏主体的实现 1.3.1 棋盘的初始化 1.3.2 打印棋盘 1.3.3 玩家下…

3分钟搞懂示波器测原副边波形

大家好&#xff0c;我是砖一。 今天分享一下如何用示波器测试原副边的波形&#xff0c;验证电源设计规格准确性。 一&#xff0c;试验目的 假设我们现在拿到的样品是属于开关电源类型的。 1&#xff0c;我们对于电源工程师设计出的一个开关电源样品测试原副边波形&#xff…

手撕算法-爬楼梯

描述 分析 一维动态规划。dp[i] dp[i-1] dp[i-2]; 初始状态&#xff1a;d[0] 0, dp[1] 1, dp[2] 2; 返回值:dp[n]; 代码 class Solution {public int climbStairs(int n) {if (n < 2)return n;int[] dp new int[n 1];dp[1] 1;dp[2] 2;for (int i 3; i < …

matlab批量读取目录下的文件的方法

批量处理可以提高效率&#xff0c;这里提供一个可以批量读取nc文件的代码&#xff1a; address C:\Users\Hello World!!\DESKTOP\TerraClimate_ppt\; % Get the list of files udir address; form *.nc; % Get the list of station names files GetFiles(udir,form); [n,p…

MySQL--select count(*)、count(1)、count(列名) 的区别你知道吗?

MySQL select count(*)、count(1)、count(列名) 的区别&#xff1f; 这里我们先给出正确结论&#xff1a; count(*)&#xff0c;包含了所有的列&#xff0c;会计算所有的行数&#xff0c;在统计结果时候&#xff0c;不会忽略列值为空的情况。count(1)&#xff0c;忽略所有的列…

IIS7/iis8/iis10安装II6兼容模块 以windows2022为例

因安全狗的提示 安全狗防护引|擎安装失败 可能原因是: IIS7及以上版本末安装1IS6兼容模块! .所以操作解决 如下. 在开始菜单中,找到服务器管理器.找到下图的IIS,右键添加角色和功能,找到web服务器的管理工具选项,iis6管理兼容性 打钩并安装. 如下图

C++ 实现BSTree

目录 BSTree.h 框架 insert insertR find findR erase eraseR InOrder 拷贝构造 赋值重载 BSTree.h 框架 template<class K> struct BSTreeNode {BSTreeNode<K>* _left;BSTreeNode<K>* _right;K _key;BSTreeNode(const K& key):_left(null…

2024智能EDM邮件营销系统使用攻略

在数字化营销领域&#xff0c;智能EDM&#xff08;Electronic Direct Mail&#xff09;邮件营销作为一种高效、精准的推广方式&#xff0c;正日益受到企业的高度重视。而要实现这一策略的成功落地&#xff0c;一个高可靠性和高稳定性的专业邮件发送平台则是不可或缺的关键环节。…

Linux V4L2 应用编程

V4L2&#xff1a;Video4Linux2&#xff0c;是 Linux 内核中的一个框架&#xff0c;提供了一套用于视频设备驱动程序开发的 API。它是一个开放的、通用的、模块化的视频设备驱动程序框架&#xff0c;允许 Linux 操作系统和应用程序与各种视频设备&#xff08;如摄像头、视频采集…

Less-1(sqlmap手工注入攻击)--sqli

第一步&#xff1a;判断他是什么sql注入&#xff1f; 1 报错 1 and 12 -- 错误结果(--表示注释符) 1 and 11 -- 正确结果 第二步&#xff1a;判断返回字段数 ?id1 order by 3-- 正确显示结果 ?id1 order by 4--当列数为4时开始报错&#xff0c;所以只有三列 注&#xf…

aurora仿真使用等

IP设置 代码 aurora_8b10b aurora_8b10b_inst (/**********************************************************************************///axi_stream tx.s_axi_tx_tdata(s_axi_tx_tdata), // input wire [0 : 31] s_axi_tx_tdata.s_axi_tx_tkeep(s_axi_tx_…