头图

序言

 Fuzzing101系列包含针对10 个真实目标的10个练习,在练习中一步一步学习Fuzzing技术的知识。

 模糊测试(Fuzzing/Fuzz)是一种自动化软件测试技术,它基于为程序提供随机或变异的输入值并监视它的异常和崩溃。

 AFL、libFuzzer 和 HonggFuzz 是现实世界应用中最多的三个模糊器,这三个都是覆盖引导的进化模糊器(Coverage-guided evolutionary fuzzer)。其中

进化(evolutionary)是一种受进化算法启发的元启发式方法,它基本上包括通过使用选择标准(例如覆盖率)随时间推移初始子集(种子)的进化和变异。

覆盖引导(Coverage-guided)是指为了增加发现新崩溃的机会,覆盖引导的模糊器收集和比较不同输入之间的代码覆盖率数据,并选择那些导致新执行路径的输入。

 在这个练习中,我们将fuzz Xpdf PDF 查看器。目的是在 XPDF 3.02 中找到 CVE-2019-13288 的崩溃/PoC。

 CVE-2019-13288 是一个漏洞,它可能会通过精心制作的文件导致无限递归。由于程序中每个被调用的函数都会在栈上分配一个栈帧,如果一个函数被递归调用这么多次,就会导致栈内存耗尽和程序崩溃。因此,远程攻击者可以利用它进行 DoS 攻击。可以在以下链接中找到有关不受控制的递归漏洞的更多信息:https://cwe.mitre.org/data/de...

你会学到什么

 完成本练习后,你将了解使用 AFL 进行 fuzz 的基础,例如:

使用检测编译目标应用程序

运行模糊器(afl-fuzz)

使用调试器 (GDB) 对崩溃进行分类

环境

 所有练习都在 Ubuntu 20.04.2 LTS 上进行了测试。 我强烈建议您使用相同的操作系统版本以避免不同的模糊测试结果,并在裸机硬件而不是虚拟机上运行 AFL,以获得最佳性能。

 否则,您可以在此处找到 Ubuntu 20.04.2 LTS 镜像。用户名为 fuzz / fuzz。

 AFL 使用非确定性测试算法,因此两个模糊测试会话永远不会相同。我强烈建议设置一个固定的种子(-s 123),这样你的模糊测试结果将与本文的结果相似。

下载并构建目标

 首先为要进行模糊测试的项目创建一个新目录:

cd $HOME
mkdir fuzzing_xpdf && cd fuzzing_xpdf/
 为了完全准备好环境,需要安装一些额外的工具(make 和 gcc)

sudo apt install build-essential
 下载 Xpdf 3.02:

wget https://dl.xpdfreader.com/old...
tar -xvzf xpdf-3.02.tar.gz
 构建 Xpdf:

cd xpdf-3.02
sudo apt update && sudo apt install -y build-essential gcc
./configure --prefix="$HOME/fuzzing_xpdf/install/"
make
make install
 下面对 Xpdf 进行测试,首先下载一些 PDF 示例:

cd $HOME/fuzzing_xpdf
mkdir pdf_examples && cd pdf_examples
wget https://github.com/mozilla/pd...
wget http://www.africau.edu/images...
wget https://www.melbpc.org.au/wp-...
 使用以下命令测试 pdfinfo 二进制文件:

$HOME/fuzzing_xpdf/install/bin/pdfinfo -box -meta $HOME/fuzzing_xpdf/pdf_examples/helloworld.pdf

安装 AFL++

 我们将使用最新版本的 AFL++ fuzzer(https://github.com/AFLplusplu...)

 安装依赖项

sudo apt-get update
sudo apt-get install -y build-essential python3-dev automake git flex bison libglib2.0-dev libpixman-1-dev python3-setuptools
sudo apt-get install -y lld-11 llvm-11 llvm-11-dev clang-11 || sudo apt-get install -y lld llvm llvm-dev clang
sudo apt-get install -y gcc-$(gcc --version|head -n1|sed 's/.* //'|sed 's/\..*//')-plugin-dev libstdc++-$(gcc --version|head -n1|sed 's/. //'|sed 's/..//')-dev
 构建 AFL++

cd $HOME
git clone https://github.com/AFLplusplu... && cd AFLplusplus
export LLVM_CONFIG="llvm-config-11"
make distrib
sudo make install
 执行afl-fuzz,查看是否安装成功

认识 AFL++

 AFL 是一个覆盖引导的模糊器(coverage-guided fuzzer),这意味着它收集每个变异输入的覆盖信息,来发现新的执行路径和潜在的错误。当源代码可用时,AFL 可以使用插桩(instrumentation),在每个基本块(函数、循环等)的开头插入函数调用。

 要为我们的目标程序启用检测,我们需要使用 AFL 的编译器编译源代码。

 首先,我们要清理所有之前编译的目标文件和可执行文件:

rm -r $HOME/fuzzing_xpdf/install
cd $HOME/fuzzing_xpdf/xpdf-3.02/
make clean
 现在我们将使用 afl-clang-fast 编译器构建 xpdf:

export LLVM_CONFIG="llvm-config-11"
CC=$HOME/AFLplusplus/afl-clang-fast CXX=$HOME/AFLplusplus/afl-clang-fast++ ./configure --prefix="$HOME/fuzzing_xpdf/install/"
make
make install
 现在可以使用以下命令运行 fuzzer:

afl-fuzz -i $HOME/fuzzing_xpdf/pdf_examples/ -o $HOME/fuzzing_xpdf/out/ -s 123 -- $HOME/fuzzing_xpdf/install/bin/pdftotext @@ $HOME/fuzzing_xpdf/output
 每个选项的简要说明

-i 表示输入示例的目录

-o 表示 AFL + + 将存储的变异文件的目录

-s 表示要使用的静态随机种子

@@ 是占位符目标的命令行,AFL 将用每个输入文件名替换

 fuzzer将会对每个不同的输入文件运行 $HOME/fuzzing_xpdf/install/bin/pdftotext <input-file-name> $HOME/fuzzing_xpdf/output 命令

 出现错误,根据提示,执行以下操作:

sudo su
echo core >/proc/sys/kernel/core_pattern
exit
 成功运行,等待一段时间后,发现已经有了一个crash

 可以在$HOME/fuzzing_xpdf/out/ 目录中找到这些崩溃文件。一旦发现第一次崩溃,就可以停止fuzzer,上图中已经出现了一个独特的崩溃。根据您的机器性能,最多可能需要一到两个小时才能发生崩溃。

 为了完成这个练习,下面尝试使用指定的文件重现崩溃,调试崩溃发现问题,并且修复问题。

重现崩溃

 在$HOME/fuzzing_xpdf/out/目录下找到 crash 对应的文件。文件名类似于id:000000,sig:11,src:000390,time:103613,execs:71732,op:havoc,rep:16

68de27ea9a556cb90834a1a461ba960d.png

 将此文件作为输入传递给 pdftotext

$HOME/fuzzing_xpdf/install/bin/pdftotext '/home/fuzz/fuzzing_xpdf/out/default/crashes/<your_filename>' $HOME/fuzzing_xpdf/output
 它将导致段错误segmentation fault并导致程序崩溃。

调试

 使用 gdb 找出程序因该输入而崩溃的原因。

 首先使用调试信息重建 Xpdf 来获得符号堆栈跟踪:

rm -r $HOME/fuzzing_xpdf/install
cd $HOME/fuzzing_xpdf/xpdf-3.02/
make clean
CFLAGS="-g -O0" CXXFLAGS="-g -O0" ./configure --prefix="$HOME/fuzzing_xpdf/install/"
make
make install
 然后使用GDB,输入run

gdb --args $HOME/fuzzing_xpdf/install/bin/pdftotext $HOME/fuzzing_xpdf/out/default/crashes/<your_filename> $HOME/fuzzing_xpdf/output

 然后输入bt回溯查看栈帧

 发现有许多次Parser::getObj的调用,它们似乎表示一个无限递归。如果你去 https://www.cvedetails.com/cv... ,你可以看到描述符合我们从 GDB 得到的回溯。

实验推荐

 实验:Fuzz之AFL(合天网安实验室) 点击进入实操>>
 更多网安技能的在线实操练习,请点击这里>>
 更多网安工具及学习资料,扫码免费领:


蚁景网安实验室
53 声望45 粉丝

蚁景网安实验室(www.yijinglab.com)-领先的实操型网络安全在线教育平台 真实环境,在线实操学网络安全 ;内容涵盖:系统安全,软件安全,网络安全,Web安全,移动安全,CTF,取证分析,渗透测试,网安意识教育等。