AFL++初探-手把手Fuzz一个PDF解析器
CVE-2019-13288
目前漏洞在正式版本已经被修复,本文章仅供学习Fuzz过程,不存在漏洞利用的内容
这是一个pdf查看器的漏洞,可能通过精心制作的文件导致无限递归,由于程序中每个被调用的函数都会在栈上分配一个栈帧,如果一个函数被递归调用太多次,就会导致栈内存耗尽和程序崩溃。因此,远程攻击者可以利用它进行 DoS 攻击。
练习目的
- 使用检测编译目标应用程序
- 运行Fuzzer (afl-fuzz)
- 使用调试器 (GDB) 对崩溃进行分类
环境构建
环境使用Ubuntu 20.04.2 LTS,尽量在物理机进行Fuzz而不是虚拟机来获取更多的性能(当然这只是性能方面,对Fuzz最终结果不会产生影响)
VMWare 镜像下载地址:
此 VM 的用户名/密码是fuzz/ fuzz
下载并构建
首先为Fuzz目标创建一个新目录:
cd $HOME
mkdir fuzzing_xpdf && cd fuzzing_xpdf/
可能需要安装一些额外的工具(即 make 和 gcc)
sudo apt install build-essential
下载 Xpdf 3.02:
wget https://dl.xpdfreader.com/old/xpdf-3.02.tar.gz
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/pdf.js-sample-files/raw/master/helloworld.pdf
wget http://www.africau.edu/images/default/sample.pdf
wget https://www.melbpc.org.au/wp-content/uploads/2017/10/small-example-pdf-file.pdf
可以使用以下命令测试 pdfinfo 二进制文件:
$HOME/fuzzing_xpdf/install/bin/pdfinfo -box -meta $HOME/fuzzing_xpdf/pdf_examples/helloworld.pdf
结果如下:

安装AFLplusplus
对于本实验,使用最新版本的AFL++,GitHub链接如下:
推荐本地安装
安装依赖:
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/AFLplusplus/AFLplusplus && cd AFLplusplus
export LLVM_CONFIG="llvm-config-11"
make distrib
sudo make install
可能会出现qemu_mode、frida和unicorn安装失败的情况,多数原因是因为网络,这几个功能是用于二进制文件黑盒Fuzz,可以暂时先不用管,从源码构建Fuzz不需要这几个功能
也可以使用docker构建,性能比较低:
安装docker:
sudo apt install docker
拉取镜像:
docker pull aflplusplus/aflplusplus
启动 AFLPlusPlus docker 容器:
docker run -ti -v $HOME:/home aflplusplus/aflplusplus
然后输入
export $HOME="/home"
安装成功后,应该可以使用afl-fuzz了,输入afl-fuzz命令可以看到如下内容:

使用AFL++开始Fuzz
当源代码可用时,AFL 可以使用检测,在每个基本块(函数、循环等)的开头插入函数调用。为了为目标应用程序启用检测,所以需要使用 AFL 的编译器编译代码。简单来说就是需要使用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
现在可以使用以下命令运行Fuzz:
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 使用非确定性测试算法,因此两个Fuzz会话永远不会相同。这就是为什么设置固定种子 -s 123的原因。用以保证Fuzz结果和示例相同。)
- @@:是占位符目标的命令行,AFL 将用每个输入文件名替换它
如果收到「Hmm, your system is configured to send core dump notifications to an external utility...」类似的命令,执行以下操作关闭核心转储:
sudo su
echo core >/proc/sys/kernel/core_pattern
exit
几分钟后,应该会看到如下内容:

可以看到红色的uniq crashes值,显示找到的唯一崩溃的数量。可以在$HOME/fuzzing_xpdf/out/目录中找到这些崩溃文件。一旦发现第一个崩溃,就可以使用control+c停止 fuzzer。
什么时候可以停止fuzzer?其中一个指标可以参考cycles done 的数字颜色,依次会出现洋葱红色,黄色,蓝色,绿色,变成绿色时就很难产生新的crash文件了。
复现崩溃和修复漏洞
复现崩溃
在$HOME/fuzzing_xpdf/out/目录中找到崩溃对应的文件。文件名类似于id:000000,sig:11,src:001504+000002,time:924544,op:splice,rep:16

将此文件作为输入传递给 pdftotext 二进制文件(如果提示无法打开文件,将文件名称改为xxx.pdf再尝试)
$HOME/fuzzing_xpdf/install/bin/pdftotext '$HOME/fuzzing_xpdf/out/default/crashes/<your_filename>' $HOME/fuzzing_xpdf/output
它会导致分段错误并导致程序崩溃:

使用 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:
gdb --args $HOME/fuzzing_xpdf/install/bin/pdftotext $HOME/fuzzing_xpdf/out/default/crashes/<your_filename> $HOME/fuzzing_xpdf/output
然后,在 GDB 中输入:
run
如果一切顺利,应该会看到以下输出:

然后输入bt以获取栈回溯:

滚动调用堆栈,将看到Parser::getObj方法的许多调用,这些调用似乎表示无限递归,如上图的红框所示。
修复问题
下载4.02版本代码,对比Parser.cc,可以看到此漏洞被修复:

总结
- 通过复现一个漏洞,学习AFL++的基本使用方法,并使用GDB查看crash原因
- 后续还有AFL++Fuzz学习的笔记会同步更新到博客
参考文章
AFL++初探-手把手Fuzz一个PDF解析器的更多相关文章
- 一起写一个JSON解析器
[本篇博文会介绍JSON解析的原理与实现,并一步一步写出来一个简单但实用的JSON解析器,项目地址:SimpleJSON.希望通过这篇博文,能让我们以后与JSON打交道时更加得心应手.由于个人水平有限 ...
- CozyRSS开发记录9-快速实现一个RSS解析器
CozyRSS开发记录9-快速实现一个RSS解析器 1.再读RSS标准 既然需要自己实现一个RSS解析器,那自然需要仔细的读一读RSS的标准文档.在网上随便找了两份,一份英文一份中文: http:// ...
- 如何编写一个JSON解析器
编写一个JSON解析器实际上就是一个函数,它的输入是一个表示JSON的字符串,输出是结构化的对应到语言本身的数据结构. 和XML相比,JSON本身结构非常简单,并且仅有几种数据类型,以Java为例,对 ...
- Python+Flask+Gunicorn 项目实战(一) 从零开始,写一个Markdown解析器 —— 初体验
(一)前言 在开始学习之前,你需要确保你对Python, JavaScript, HTML, Markdown语法有非常基础的了解.项目的源码你可以在 https://github.com/zhu-y ...
- Python 之父再发文:构建一个 PEG 解析器
花下猫语: Python 之父在 Medium 上开了博客,现在写了两篇文章,本文是第二篇的译文.前一篇的译文 在此 ,宣布了将要用 PEG 解析器来替换当前的 pgen 解析器. 本文主要介绍了构建 ...
- 几百行代码实现一个 JSON 解析器
前言 之前在写 gscript时我就在想有没有利用编译原理实现一个更实际工具?毕竟真写一个语言的难度不低,并且也很难真的应用起来. 一次无意间看到有人提起 JSON 解析器,这类工具充斥着我们的日常开 ...
- 如何实现一个SQL解析器
作者:vivo 互联网搜索团队- Deng Jie 一.背景 随着技术的不断的发展,在大数据领域出现了越来越多的技术框架.而为了降低大数据的学习成本和难度,越来越多的大数据技术和应用开始支持SQL进 ...
- 开源一个CSV解析器(附设计过程 )
在ExcelReport支持csv的开发过程中,需要一个NETStandard的csv解析器.在nuget上找了几个试用,但都不太适合. 于是,便有了:AxinLib.IO.CSV. 先看看怎么用: ...
- Python 之父的解析器系列之三:生成一个 PEG 解析器
原题 | Generating a PEG Parser 作者 | Guido van Rossum(Python之父) 译者 | 豌豆花下猫("Python猫"公众号作者) 声明 ...
随机推荐
- jvm源码解读--13 gc_root中的栈中oop的mark 和copy 过程分析
粘贴源码 package com.test; import java.util.Random; public class Test { static int number=12; private in ...
- Python实用案例,Python脚本,Python实现自动监测Github项目并打开网页
往期回顾 Python实现文件自动归类 前言: 今天我们就利用Python脚本实现Github项目的更新,提醒方式是邮箱.直接开整~ 项目地址: https://github.com/kenwoodj ...
- 使用C#winform编写渗透测试工具--暴力破解
使用C#winform编写渗透测试工具--暴力破解 这篇文章主要介绍使用C#winform编写渗透测试工具--暴力破解.暴力破解是指通过利用大量猜测和穷举的方式来尝试获取用户口令的攻击方式.简单来说就 ...
- Appium使用inspactor开始session报"Could not connect to server; are you sure it's running?"
appium在使用inspactor start session时提示:Could not connect to server; are you sure it's running?如下图 解决方案为 ...
- 【问题记录】- 谷歌浏览器 Html生成PDF
起因: 由于项目需要实现将网页静默打印效果,那么直接使用浏览器打印功能无法达到静默打印效果. 浏览器打印都会弹出预览界面(如下图),无法达到静默打印. 解决方案: 谷歌浏览器提供了将html直接打印成 ...
- Python 机器学习实战 —— 无监督学习(下)
前言 在上篇< Python 机器学习实战 -- 无监督学习(上)>介绍了数据集变换中最常见的 PCA 主成分分析.NMF 非负矩阵分解等无监督模型,举例说明使用使用非监督模型对多维度特征 ...
- 学废了系列 - WebGL与Node.js中的Buffer
WebGL 和 Node.js 中都有 Buffer 的使用,简单对比记录一下两个完全不相干的领域中 Buffer 异同,加强记忆. Buffer 是用来存储二进制数据的「缓冲区」,其本身的定义和用途 ...
- 双非本科字节跳动Android面试题分享(已拿offer)
基本情况 本人系非985非211普通本科生一枚,有实习有项目经历但成绩普通,在面试前刷了很多面经.面试题,这里也把自己的分享下,做个回报好了,顺便攒攒人品,一到这种时候人就迷信起来了. 面试是以视频面 ...
- Salesforce Integration 概览(六) UI Update Based on Data Changes(UI自动更新基于数据变更)
Salesforce用户界面必须由于Salesforce数据的更改而自动更新.这个场景其实在我所经历的项目中用到的不是特别多,因为客户可能直接点击刷新按钮就直接看到了最新的数据,而不是那种一直不刷新然 ...
- 跟我一起写 Makefile(十四)
使用make更新函数库文件 ----------- 函数库文件也就是对Object文件(程序编译的中间文件)的打包文件.在Unix下,一般是由命令"ar"来完成打包工作. 一.函数 ...