AFL++ 漏洞挖掘实例

☆ 1. 模糊测试工具 AFL / AFL++介绍

模糊测试是一种黑盒测试方法,它通过向软件输入随机或异常的数据,来触发软件中的错误或异常。AFL 是 Google 开发的一款 fuzz 工具,它最初由 Michal Zalewski 开发,被设计为一种模糊测试工具,用于自动发现程序中的漏洞。

在 AFL 之前,模糊测试工具大多采用随机生成输入数据的方法。这种方法虽然简单,但效率低下,很难发现软件中的隐藏路径。AFL 引入了覆盖率导向的模糊测试方法,它会根据软件的执行路径来优化输入数据。这种方法可以有效地提高模糊测试的效率,并更容易发现软件中的隐藏路径。

AFL 的发布,标志着模糊测试工具进入了新的时代。AFL 的成功,激发了许多其他研究人员和开发人员对模糊测试工具的改进和研究。它对软件安全领域产生了深远的影响。AFL 的成功,证明了模糊测试是一种有效的软件安全测试方法。

AFL 在模糊测试工具发展史上的具体贡献主要有:
1)开创了覆盖率导向模糊测试的新时代
2)极大地提高了模糊测试的效率和效果
3)激发了其他研究人员和开发人员对模糊测试工具的改进和研究

AFL作为一款开创性的模糊测试工具,通过不断的发展和改进,已经在网络安全领域取得了显著的历史地位。其自动化特性和有效的漏洞发现能力使其成为安全专业人员首选的工具之一,对软件安全性的提升产生了积极影响。AFL 的成功,证明了模糊测试是一种有效的软件安全测试方法。AFL 的出现,为软件安全领域提供了新的工具和手段,帮助开发人员和安全研究人员发现软件中的安全漏洞。

AFLplusplus (简称 AFL++)是 AFL 的一个分支,它由 Marc “van Hauser 和其他开发者开发。AFLplusplus 在 AFL 的基础上进行了许多改进,包括:

  • 更快的执行速度:AFLplusplus 使用了新的编译器插件和其他技术来提高 fuzz 的速度。
  • 更强大的变异算法:AFLplusplus 提供了更强大的变异算法,可以生成更复杂的输入数据。
  • 更全面的覆盖率:AFLplusplus 提供了更全面的覆盖率分析,可以帮助开发人员发现软件中的隐藏路径。

目前 Google 的 oss-fuzz 项目已经使用 AFL++ 替换 AFL 作为其主要 fuzz 工具,AFL++ 在速度、变异算法和覆盖率分析方面都比 AFL 更优秀。

☆ 2. AFL++ 的安装

AFL++ 的源码有两个分支,stable 分支 [2] 为发布分支,dev 分支 [3] 为开发分支。dev 分支代码的更新频率很高,也不是很稳定可能出现 bug,推荐使用 stable 分支的源码。以 ubuntu 18.04 为例介绍如何安装 AFLplusplus

下载源码:

1
2
git clone https://github.com/AFLplusplus/AFLplusplus
git submodule update --init --recursive

git submodule 这条命令很重要,要不编译一些 AFL++ mode 的时候会因为缺少源码而报错。

要从源码编译 AFL++ 需要先安装系统的依赖库。

1
2
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 curl ninja-build

AFL++ 在文档中推荐使用 llvm 13+ , 所以先要升级 Ubuntu 系统的 llvm ,https://apt.llvm.org/ 提供了编译好的 llvm binary 可以下载,也提供了 llvm.sh 可以非常方面的安装新版本的 llvm,下面的例子演示安装 llvm 15

1
2
3
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 15 all

编译 AFL++

1
2
export LLVM_CONFIG="llvm-config-15"
make distrib

☆ 3. AFL++ Linux 系统环境配置

为了使 AFL++ 在 fuzz 时获得更好的性能,可以使用 root 用户执行下面的脚本, 调整 linux 内核的参数。

sudo bash afl-init.sh

1
2
3
4
5
6
7
8
9
echo 1 >/proc/sys/kernel/sched_child_runs_first
echo 1 >/proc/sys/kernel/sched_autogroup_enabled
echo 1 > /proc/sys/vm/overcommit_memory
echo core >/proc/sys/kernel/core_pattern
echo never > /sys/kernel/mm/transparent_hugepage/enabled
test -e /sys/devices/system/cpu/cpufreq/scaling_governor && echo performance | tee /sys/devices/system/cpu/cpufreq/scaling_governor
test -e /sys/devices/system/cpu/cpufreq/policy0/scaling_governor && echo performance | tee /sys/devices/system/cpu/cpufreq/policy*/scaling_governor
test -e /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor && echo performance | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
test -e /sys/devices/system/cpu/cpufreq/boost && echo 1 > /sys/devices/system/cpu/cpufreq/boost

☆ 4. AFL++ 的基本使用方法

4.1 编译插桩模糊测试目标软件

优先使用 llvm clang 对目标软件进行插桩,在最近几年的实战文章中已经很少看到使用 gcc 插桩目标软件的例子。AFL++ 提供了多种插桩方式,可以参考 https://github.com/AFLplusplus/AFLplusplus/tree/stable/instrumentation 中的 README*.txt

使用  afl-clang-fast/afl-clang-fast++ 或者 afl-clang-lto/afl-clang-lto++ 可以应对大多数情况,afl-clang-lto/afl-clang-lto++ 在编译大型程序的时候可能会失败,如果出现失败的情况,可以换用 afl-clang-fast/afl-clang-fast++ 。 下面的示例分别针对三种不同编译系统情况,[a] 使用 Makefile , [b] 使用 cmake,[c] 使用 meson , 需要根据具体情况选择。

1
2
3
CC=afl-cc CXX=afl-c++ ./configure --disable-shared # [a]
cmake -DCMAKE_C_COMPILERC=afl-cc -DCMAKE_CXX_COMPILER=afl-c++ . # [b]
CC=afl-cc CXX=afl-c++ meson # [c]

下载 Xpdf 3.02 作为 fuzz 目标。

1
2
wget https://dl.xpdfreader.com/old/xpdf-3.02.tar.gz
tar -xvzf xpdf-3.02.tar.gz

编译 Xpdf

1
2
3
4
cd xpdf
CC=afl-cc CXX=afl-c++ ./configure --disable-shared --prefix="$HOME/fuzz_xpdf/install/"
make
make install

将得到我们的 fuzz 目标 $HOME/fuzz_xpdf/install/bin/pdftotext

4.2 AFL++ 的命令行参数

1
afl-fuzz -i $HOME/AFLplusplus/testcases/others/pdf -o $HOME/fuzz_xpdf/out/ -s 123 -- $HOME/fuzz_xpdf/install/bin/pdftotext @@ $HOME/fuzz_xpdf/output
  • -i 输入目录,用于存放初始 testcase
  • -o 输出目录,用于存放 afl-fuzz 生成的 testcase
  • – 后面接 fuzz 目标的命令行 @@ 代表文件,不写 @@ 代表是从标准输入读取数据
  • -s RNG 的 seed

☆ 5. 实战 vim 漏洞挖掘

首先需要研究 vim 的命令行参数,默认的配置下 vim 启动速度慢,并且有可能执行外部shell 命令 (不安全),最终需要使用的命令行参数如下:

1
2
3
4
5
6
7
8
9
- -u NONE 跳过初始化
- -X 加快启动速度
- -s 安静模式
- -Z 受限模式 (All commands that make use of an external shell are disabled.)
- -e 以 Ex 模式运行 vim
- -S 加载第一个文件后执行文件 <session>
- -c <command> 加载第一个文件后执行 <command>
- -n 不使用交换文件,只使用内存
- -m Modifications not allowed to be written

5.0 vim 的 Ex 模式介绍

Vim 编译器有三种模式:普通模式、插入模式和 ex 模式。ex 模式是 Vim 的底层命令模式,用于执行各种文件操作,如打开、编辑、保存、搜索、替换、排序等。要进入 ex 模式,可以按下 : 键。在 ex 模式下,可以输入各种命令来执行操作。命令由行号和命令组成,以回车键结束。

最基本的 ex 命令是 p 命令,用于打印指定行号的行。例如,要打印第 10 行,可以输入 10p。

其他常用的 ex 命令包括:

1
2
3
4
5
6
e 命令:打开另一个文件
w 命令:保存当前文件
q 命令:退出 Vim
s 命令:将当前行替换为指定文本
g/pattern/s/old/new/ 命令:将所有匹配模式 pattern 的行替换为 old 替换为 new
:sort 命令:对当前文件进行排序。

ex 模式的使用方法如下:在普通模式下,按下 : 键进入 ex 模式,输入命令,按下回车键执行命令。

以下是一些 ex 模式的使用示例:

1
2
3
4
5
6
7
8
9
10
11
# 打开文件
:e test.txt

# 编辑文件
10iHello, world!

# 保存文件
:w

# 退出 Vim
:q

ex 模式提供了丰富的命令,可以用于执行各种文件操作。熟练掌握 ex 模式可以提高 Vim 的使用效率。

5.1 攻击面分析

研究历史漏洞发现,vim 主要有两个攻击面, vim 正则表达式引擎和vim 脚本执行引擎。

vim 的正则表达式引擎

1
./vim -u NONE -i NONE -X -Z -m -n -e -s -c 'call search(getline("."))' -c ':qa!' ./poc

vim 的脚本执行引擎

1
vim -u NONE -i NONE -X -Z -m -n -e -s -S ./poc -c ':qa!'

5.2 编译 vim

首先下载 vim 的源码

1
git clone https://github.com/vim/vim.git

编译 vim ,使用 afl-cc 插桩

1
2
3
4
5
export LLVM_CONFIG="llvm-config-15"
export CFLAGS="-O1 -g -fno-omit-frame-pointer"
cd vim
CC=afl-cc CXX=afl-c++ ./configure --with-features=huge --enable-gui=none --disable-shared
make

5.3 寻找 fuzz vim 的 testcases ( corpus)

vim 官方仓库里有历史上造成 vim crash 的 poc ,正好拿来用。https://github.com/vim/vim/tree/master/src/testdir/crash

5.4 fuzz vim

1
2
3
mkdir -p ~/fuzz/vim
mkdir -p /tmp/afl++
afl-fuzz -i testdir/crash/ -o ~/fuzz/vim -mnone -p fast -- ./vim -u NONE -i NONE -X -Z -m -n -e -s -S @@ -c ':qa!'
  • -x 指定字典文件
  • -mnone 不限制使用内存

5.5 fuzz 结果

经过 2天 16 小时的 fuzz, AFL++ 报告有 789 个 Crashes.

5.6 调整和改进

~/fuzz/vim/default 目录出现了大量的 _cur_inp.* 文件,这是 AFL++ 生成的的临时文件,为了取得更好的效果,需要把这些文件放到 /tmp 目录。

1
export AFL_TMPDIR=/tmp/afl++

使用 AFL++ 的 cmplog 特性,解决一些 fuzz 上的路径约束, 主要思路来自 https://github.com/RUB-SysSec/redqueen
https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/README.cmplog.md

要使用 cmplog,需要重新编译 vim,使用 AFL_LLVM_CMPLOG 环境变量,指定使用 cmplog 的编译。

1
2
3
4
5
6
7
8
9
10
cp src/vim src/vim-afl
make distclean

export LLVM_CONFIG="llvm-config-15"
export CFLAGS="-O1 -g -fno-omit-frame-pointer"
cd vim-cmplog
export AFL_LLVM_CMPLOG=1
CC=afl-cc CXX=afl-c++ ./configure --with-features=huge --enable-gui=none
make
mv src/vim src/vim-cmplog

编译完成后,将编译好的 vim 重命名成 vim-cmplog,可以使用下面的 AFL++ 命令行,启用 cmplog

1
2
export AFL_TMPDIR=/tmp/afl++-cmplog
afl-fuzz -i testdir/crash/ -o ~/fuzz/vim -mnone -p fast -S cmplog -c ./vim-cmplog -- ./vim-afl -u NONE -i NONE -X -Z -m -n -e -s -S @@ -c ':qa!'

同时运行两个 AFL++ 对 vim 进行 fuzz。

Trophy

历史上使用上述方法发现了许多 vim 漏洞,部分漏洞 vim 官方已经修复。

https://github.com/vim/vim/commit/0fb375aae608d7306b4baf9c1f906961f32e2abf
https://github.com/vim/vim/commit/b39b240c386a5a29241415541f1c99e2e6b8ce47
https://github.com/vim/vim/commit/eec0c2b3a4cfab93dd8d4adaa60638d47a2bbc8a
https://github.com/vim/vim/commit/abfa13ebe92d81aaf66669c428d767847b577453

☆ 参考资料

[1] https://github.com/AFLplusplus/AFLplusplus/blob/stable/docs/INSTALL.md
[2] AFL ++ stable branch
[3] AFL++ dev branch
[4] https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/README.llvm.md
[5] https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/README.cmplog.md


AFL++ 漏洞挖掘实例
http://usmacd.com/cn/AFL++ 漏洞挖掘实例/
Author
henices
Posted on
April 1, 2024
Licensed under