专栏名称: Seebug漏洞平台
Seebug,原 Sebug 漏洞平台,洞悉漏洞,让你掌握第一手漏洞情报!
目录
相关文章推荐
51好读  ›  专栏  ›  Seebug漏洞平台

原创 Paper | 使用 Peach 进行基于变异和生成的 fuzzing

Seebug漏洞平台  · 公众号  ·  · 2024-09-14 16:14

正文

作者: 0x7F@ 知道创宇404实验室
时间: 2024年9月14日



1 前言

Peach 是一个于 2004 年开发的模糊测试框架(SmartFuzzer),能够执行基于生成和变异的模糊测试。其核心思路在于其内部的 PeachPit 变异引擎,安全研究员可通过 xml 描述目标文档的详细格式(pit文件),在随后的 fuzzing 过程中引导变异数据的生成;相比于使用 afl/afl++ 进行常规化的 fuzzing,使用 Peach 能够生成更规范的变异数据,从而在一定程度上提高覆盖率。

Peach 项目目前有接近 20 年的发展历史;Peach2.0 使用 Python 进行开发并于 2007 年夏开源发布,其功能包括进程监控和使用 XML 创建模糊测试器;Peach3.0 使用了 Microsoft .NET Framework(C#) 完整重写了项目,并使用 Mono 实现了跨平台支持,于 2013 年初发布;直到 2020 年,Peach Fuzzer Professional v4 被 GitLab 收购并成为 GitLab 生态圈的一部分。

本文将简单介绍 Peach 的功能,通过实验的方式理解 Peach 的核心思路和基本使用方法;由于 Peach4.0 部分功能被修改用于适配 GitLab,同时该项目目前处于停止维护的状态,所以本文将以 Peach3.0 作为实验环境。

本文实验环境:

Ubuntu 22.04 x64
Peach 3.1.124
Mono 4.8.1
GDB 12.1
Python 2.7


2 Peach环境配置

从 https://sourceforge.net/projects/peachfuzz/ 可以选择通过二进制包安装或者源码安装。

2.1 Peach二进制包安装

这里我们首先进行二进制包安装:

# download peach-3.1.124-linux-x86_64-release.zip
# 配置工作目录
$ mkdir peach-3.1.124-linux-x86_64-release
$ cd peach-3.1.124-linux-x86_64-release/
# 解压和安装 Peach
$ unzip peach-3.1.124-linux-x86_64-release.zip
执行如下:

图1:Peach二进制包安装

Peach3 是由 Microsoft .NET Framework 进行开发的,所以需要 Mono (即跨平台的.NET实现),我这里使用的较老的 Mono 4.8.1 版本,参考官方使用 apt 安装老版本如下:

# 配置 mono 仓库签名
$ sudo apt install ca-certificates gnupg
$ sudo gpg --homedir /tmp --no-default-keyring --keyring /usr/share/keyrings/mono-official-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF

# 添加 mono 源以及更新
$ echo "deb [signed-by=/usr/share/keyrings/mono-official-archive-keyring.gpg] https://download.mono-project.com/repo/ubuntu stable/snapshots/4.8.1 main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list
$ sudo apt update
$ sudo apt list -a mono-complete

# 使用 apt-preferences 解决依赖组件的版本问题
$ vim /etc/apt/preferences.d/50my
Package: *mono*
Pin: origin download.mono-project.com
Pin-Priority: 1001

Package: libnunit*
Pin: origin download.mono-project.com
Pin-Priority: 1001

# 安装 mono-complete=4.8.1.0-0xamarin1
sudo apt install mono-complete=4.8.1.0-0xamarin1

# 检查 mono 版本
$ mono -V

经测试,在 Ubuntu 22.04 通过 apt 默认安装的 mono 6.8.0.105 环境下, Peach3.exe 不能正常执行 fuzzing 功能,仅能够执行 help 等命令。

执行如下:

图2:安装mono支持

Peach3 的核心程序为 Peach.exe ,使用 mono Peach.exe -h ./Peach.exe -h 运行如下:

图3:Peach运行以及帮助

2.2 GDB-Python2.7适配

由于 Peach3 在 Linux 环境下依赖于 GDB 和 Python2.7 运行,同时还要求 GDB 使用 Python2.7 的插件,Ubuntu 通过 apt 安装的 GDB 默认使用 Python3.x,我们需要从源码指定 Python 插件版本来编译 GDB,如下:

# 下载以及解压 gdb-12.1
$ wget "https://ftp.gnu.org/gnu/gdb/gdb-12.1.tar.gz"
$ tar -zxvf gdb-12.1.tar.gz
# 进入源码目录,处理部分依赖问题
$ cd gdb-12.1/
$ sudo apt install python2.7-dev texinfo
# 指定 python2.7 版本和编译
$ ./configure --prefix=/home/ubuntu/peach/install --with-python='/usr/bin/python2.7'
$ make && make install
编译安装完成后,使用 ldd /home/ubuntu/peach/install/bin/gdb 可以看到源码编译的 GDB 依赖于 Python2.7 如下:

图4:GDB-Python2.7适配

2.3 Peach源码安装

由于 Peach3 版本已经很老了,在实际使用还会出现诸多问题;这里我们推荐通过源码安装以便解决这些使用问题,如下:

# download peach-3.1.124-source.zip
# 配置工作目录
$ mkdir peach-3.1.124-source
$ cd peach-3.1.124-source
# 解压 Peach 源码
$ unzip peach-3.1.124-source.zip

为了使用 Peach3 能够直接使用我们源码编译的 GDB(Python2.7),我们需要修改源码中的默认依赖,同时由于 GDB12.1 更新了 logging 的用法,我们还需要解决 Peach3 中不兼容的问题,我们整合后的补丁参考 peach-gdb.patch;

除此之外, peach-3.1.124 版本下 Peach.Core/Engine.cs#runTest() 好像还存在 bug?这里的逻辑为 目标程序运行 => 数据变异 => crash收集 ,导致 crash收集 环节时将错位保存新变异的数据,修复补丁参考 peach-runtest.patch;

最后由于 Peach3 源码依赖于 3rdParty/pin/pin-2.13-61206-gcc.4.4.7-linux/ 组件,这需要非常老的 Gcc4 ,我们这里采取手动升高依赖版本 pin-3.19-98425-gd666b2bee-gcc-linux ,如下:

# 下载和解压 
$ cd 3rdParty/pin/
$ wget "https://software.intel.com/sites/landingpage/pintool/downloads/pin-3.19-98425-gd666b2bee-gcc-linux.tar.gz"
$ tar -zxvf pin-3.19-98425-gd666b2bee-gcc-linux.tar.gz

# 修改编译依赖组件名称
$ vim ./build/config/linux.py
# env['PIN_VER'] = 'pin-3.19-98425-gd666b2bee-gcc-linux'

随后使用 waf 进行编译:

# 使用 configure 构建项目
$ python2.7 waf configure
# (由于修改了 pin 版本这里会提示错误,但不影响实际功能
# (Available - Missing Features: pin

# 使用 build 构建项目,编译的二进制存储于 [src]/slag/
$ python2.7 waf build
# (默认 pin 版本和默认 Gcc 版本的冲突错误
# (#error The C++ ABI of your compiler does not match the ABI of the pin kit.

# 使用 install 安装项目,编译的二进制存储于 [src]/output/
$ python2.7 waf install
编译安装执行如下:

图5:Peach二进制包安装

安装完毕后,Peach 程序位于 [src]/output/ 下,我们可以使用 export PATH= 添加路径,便于后续使用:

# Peach3 + mono 4.8.1 需要设置 TERM
$ export TERM=xterm
# 添加 PATH
$ export PATH=$PATH:/home/ubuntu/peach/peach-3.1.124-source/output/linux_x86_64_debug/bin/
$ Peach.exe -h
执行如下:

图6:Peach运行以及帮助


3 基于变异的fuzzing

配置好 Peach 环境后,我们可以按照官方文档学习 Dumb Fuzzing,其核心思路是通过对种子文件的各种变换(位翻转、裁剪、拼接等)产生变异数据,也就是基于变异的 fuzzing;这里我们将对 Linux 下的图片查看器 feh PNG 图片格式逻辑进行 fuzzing。

首先安装目标软件 feh 以及配置工作目录:

$ sudo apt install feh
$ mkdir test-feh && cd test-feh
使用 Peach 进行 fuzzing 的核心在于编写 Pit 文件,其模板文件 [src]/Peach/template.xml 如下:

图7:Peach模板Pit文件

根据模板我们很容理解和构造针对 PNG 的 Pit 文件;首先构建 字段,该字段用于描述数据格式,如下:



我们这里使用「基于变异的fuzzing」,所以使用 Blob 二进制数据进行占位表示,后续我们将使用种子文件内容填充该位置;

随后是 字段,该字段用于描述 Peach 的执行状态流,其中子字段 用于定义状态/阶段(通常只有一个),如下:










字段用于实际描述 Peach 的动作,最常用的有三个:

  1. output:链接至具体的 表示数据生成并在此输出/生成,我们这里额外使用了 字段,从 "samples_png/*.png" 文件填充上文的 数据;

  2. close:输出/生成完数据后,进行关闭文件的操作;

  3. call:调用下一个组件,通常为 Peach.Agent 字段;

接下来就是 字段,主要需要设置 子字段,其用于监控目标程序的异常状态(即crash),依照操作系统我们这里使用 LinuxDebugger (底层调用为 GDB),并按需设置好目标程序和命令行参数,如下:






最后我们来设置 字段,该字段相当于 Pit 文件的主函数,同时包含了一些全局定义等内容,如下:













这里定义并链接了上文的 ,而 表示生成的样本数据应该如何发布,这里保存为了 fuzzed.png 文件,该字段完成了「变异数据生成」和「样本传入目标程序」两个环节的对接:


===>

===>

另外还有 字段用于描述日志的存储;完整的 Pit 文件请参考 png.xml。

随后我们准备一个 test.png 样本图片,并构建工作目录如下:

.
├── png.xml
└── samples_png
└── test.png

使用如下命令启动 Peach 进行 fuzzing:

# feh 需要 GUI 支持,在 ssh 下需设置 DISPLAY
$ export DISPLAY=:0
# 启动 Peach 进行 fuzzing
$ Peach.exe png.xml
执行如下:

图8:Peach进行基于变异的fuzzing




4 基于生成的fuzzing


参考资料

接下来我们继续按照官方文档学习 File Fuzzing,其核心思路是根据用户编写的 xml 规范化的生成编译数据,即基于生成的 fuzzing,这是 Peach 最核心的功能,这里我们将对 Linux 下的图片查看器 mplayer wave 格式进行 fuzzing。

首先安装目标软件 mplayer 以及配置工作目录:

$ sudo apt install mplayer
$ mkdir test-mplayer && cd test-mplayer

相比于上文章节中的「基于变异的fuzzing」编写 Pit 文件,「基于生成的fuzzing」同样需要 4 大标签:

不同点在于 标签内我们将使用具体的数据结构来描述 wave 格式,这里我们主要针对 标签进行详细介绍和编写;

在编写 标签内容前,我们需要对 wave 格式有一个精确的认知,可以参考 WaveFormat 格式说明以及示例文件 sample.wav 如下:

图9:wave文件格式说明

首先我们编写一个名为 Wav ,用于整体描述 wave 文件格式,其中定义了 RIFF/WAVE 魔数,注释部分 wave data 将在下文进行填充:







我们提取了 wave 格式子块结构的通用部分( fmt/data sub-chunk ),定义了一个名为 Chunk 的通用 wave 块,这里我们使用了 标签,表示该 Number 的值和名为 Data 的数据相关联,如下:








接着我们来编写 ChunkFmt 块,用于表示包含有 fmt sub-chunk 的 wave 文件,我们这里使用了 ref 引用了通用 Chunk 块,其中同名的数据结构 Data 将会由新的进行覆盖,如下:













我们再来尝试编写 ChunkData 块,其表示包含有 data sub-chunk 的 wave 文件,该结构只有简单的 data 部分:



按照这个思路,我们继续编写完善 ChunkFact / ChunkSint / ChunkWav1 / ChunkCue / ChunkPlst / ChunkLab1 / ChunkNote / ChunkLtxt / ChunkList / ChunkSmpl / ChunkInst 等块;

最后我们填充 Wav 块,并使用 标签将这些 Chunk* 都添加进来,表示生成 wave 文件时将从中进行选取:



















至此我们就完成了 wave 格式的描述,完整的 Pit 文件请参考 wav.xml。

再补充一点,在 output 动作下,我们使用 链接到我们定义的 wave 格式,同时还指定了 ,即 Peach 会加载该文件用于验证 (格式不正确时会提示错误),如下:





随后构建工作目录如下:

.
├── sample.wav
└── wav.xml

使用如下命令启动 Peach 进行 fuzzing:

# 启动 Peach 进行 fuzzing
$ Peach.exe wav.xml
执行如下:

图10:Peach进行基于生成的fuzzing



5 Pit文件概要


参考资料 5.补丁修复
通过以上官方提供的两个示例,我们基本了解了 Peach 的核心思路和基本使用,通过以下流程图概括 Peach 的工作流程:

图11:Peach进行基于生成的fuzzing

可以看到 PeachPit(*.xml) 几乎定义了整个 Peach 的工作流程,当 Peach 启动后就从 PeachPit 加载 内容,其中定义了关键的 / / 数据,Peach 通过 可访问到 ,解析 生成变异数据并按照 保存为变异样本,随后调用 启动对应的监视器,由监视器传入变异样本并启动目标程序,收集异常完成一次 fuzzing。

对于 PeachPit 的语法最好的方式是 官方文档 ,我们这里仅简单列举下标签名称和关系:

DataModel

  • Blob:定义二进制数据

  • Block:定义块结构

  • Choice:定义可选择数据(类似switch)

  • Custom:未知

  • Flag:结合 Flags 父块,定义位域值

  • Flags:结合 Flag 子项,定义位域值

  • Number:定义数字数据

  • Padding:定义填充数据

  • String:定义字符串数据

  • XmlAttribute:定义 xml 属性数据

  • XmlElement:定义 xml 元素数据

  • Relation:定义关联数据

  • Fixup:定义修正数据(如校验值)

  • Transformer:定义转换数据(如base64编码)

  • Placement:定义偏移数据

StateModel

  • State:定义 Peach 的执行状态

    • output/DataModel:链接至 DataModel

    • Action:定义 Peach 的执行动作

Agent

  • Monitor:定义监视器

    • Param:定义监视器的参数

Test

  • StateModel:链接至 StateModel

  • Publisher:定义变异数据的输出方式

  • Agent:链接至 Agent

  • Strategy:定义全局的变异策略

  • Logger:定义日志存储



6 vuln实践


参考资料 5.补丁修复

现在我们来编写一个有 crash 的测试程序,来进一步掌握 Peach 的基本使用,编写 test.c 测试程序,关键代码如下:

// +------------------------------------------+
// | header | length | chunk1 | chunk2 | data |
// +------------------------------------------+
// 2bytes 4bytes 2bytes 4bytes nbytes
int vuln(char* str, int len) {
int offset = 0;

if (len <= 12) {
printf("data format error\n");
return 0;
}

char* header = str + offset;
offset = offset + 2;

int length = atoi(str);
offset = offset + 4;

char* chunk1 = str + offset;
offset = offset + 2;
char* chunk2 = str + offset;
offset = offset + 4;

char* data = str + offset;

if (length > 0xF0000000) {
printf("length = %x\n", length);
raise(SIGSEGV);
}
else if (data[0] == 'A') {
printf("data[0] %c\n", data[0]);
raise(SIGSEGV);
}
else {
printf("OK\n");
}

return 0;
}

测试程序实现了一个简单的格式解析,并将在两个条件下手动跑出异常:

  1. length > 0xF0000000 时,则异常退出;

  2. data[0] == 'A' 时,则异常退出。

根据格式解析我们编写 PeachPit 文件 test.xml ,其核心部分 如下:







随后启动 Peach 进行 fuzzing:

图12: 使用Peach对vuln程序进行fuzzing

发现的 crash 将保存于






请到「今天看啥」查看全文