下载和安装
wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz
tar -xvf ./afl-latest.tgz
cd afl-2.52b/
sudo make && sudo make install
检测是否安装成功
root@b855926e8d87:~# afl-fuzz
afl-fuzz 2.52b by <lcamtuf@google.com>
afl-fuzz [ options ] -- /path/to/fuzzed_app [ ... ]
Required parameters:
-i dir - input directory with test cases
-o dir - output directory for fuzzer findings
有源码fuzz
由于我们用afl来fuzz upx项目,这个有源码的要用afl-gcc来编译,所以编辑Makefile文件
git clone https://github.com/upx/upx.git
cd upx/
vim Makefile
添加:CC = /usr/local/bin/afl-gcc
(我直接在第一行加)
查看位置
which afl-g++
/usr/local/bin/afl-g++
在./src/Makefile
文件中修改: CXX ?= /usr/local/bin/afl-g++
(应该在31行)
cd ./src
vim Makefile
对于upx项目,我们还要安装这个
apt install zlib1g zlib1g-dev
对于upx项目,安装lzma-sdk
root@b855926e8d87:~/upx# git submodule update --init --recursive
Submodule 'src/lzma-sdk' (https://github.com/upx/upx-lzma-sdk.git) registered for path 'src/lzma-sdk'
Cloning into 'src/lzma-sdk'...
remote: Enumerating objects: 440, done.
remote: Total 440 (delta 0), reused 0 (delta 0), pack-reused 440
Receiving objects: 100% (440/440), 334.59 KiB | 289.00 KiB/s, done.
Resolving deltas: 100% (151/151), done.
Checking connectivity... done.
Submodule path 'src/lzma-sdk': checked out '426fe82d122e2cf140a86751055ee523378fe2ef'
对于upx项目,安装ucl
wget http://www.oberhumer.com/opensource/ucl/download/ucl-1.03.tar.gz
tar -xvf ./ucl-1.03.tar.gz
cd ucl-1.03/
./configure && sudo make && sudo make install
export UPX_UCLDIR=/path/to/ucl-1.03 # !!!!!你自己的路径啊
最后到我们编译upx了,可以看到很多afl-cc alf-as的字样
root@b855926e8d87:~/upx# make all
make -C src all
make[1]: Entering directory '/root/upx/src'
Updating .depend
afl-cc 2.52b by <lcamtuf@google.com>
/usr/local/bin/afl-g++ '-DUPX_VERSION_GITREV="7a3637ff5a80+"' -I/root/ucl-1.03/include -O2 -fno-delete-null-pointer-checks -fno-strict-aliasing -fwrapv -funsigned-char -Wall -W -Wcast-align -Wcast-qual -Wmissing-declarations -Wpointer-arith -Wshadow -Wvla -Wwrite-strings -Werror -o c_file.o -c c_file.cpp
afl-cc 2.52b by <lcamtuf@google.com>
afl-as 2.52b by <lcamtuf@google.com>
最后编译生成的文件在src目录下的upx.out
在ida中打开,发现文件变得不同了
.text:00000000004030E0 lea rsp, [rsp-98h]
.text:00000000004030E8 mov [rsp+0], rdx
.text:00000000004030EC mov [rsp+arg_0], rcx
.text:00000000004030F1 mov [rsp+arg_8], rax
.text:00000000004030F6 mov rcx, 5010h
.text:00000000004030FD call __afl_maybe_log_10 #看这里
.text:0000000000403102 mov rax, [rsp+arg_8]
.text:0000000000403107 mov rcx, [rsp+arg_0]
.text:000000000040310C mov rdx, [rsp+0]
.text:0000000000403110 lea rsp, [rsp+98h]
.text:0000000000403118 push r15
.text:000000000040311A push r14
.text:000000000040311C push r13
.text:000000000040311E push r12
.text:0000000000403120 push rbp
.text:0000000000403121 push rbx
.text:0000000000403122 sub rsp, 28h
.text:0000000000403126 mov [rsp+58h+argc], edi
.text:000000000040312A mov [rsp+58h+argv], argv
.text:000000000040312F lea argc, [rsp+58h+argc] ; argc
.text:0000000000403134 lea argv, [rsp+58h+argv] ; argv
.text:0000000000403139 call _Z12acc_wildargvPiPPPc ; acc_wildargv(int *,char ***)
.text:000000000040313E call _ZL16upx_sanity_checkv ; upx_sanity_check(void)
.text:0000000000403143 mov rdi, cs:opt ; this
.text:000000000040314A call _ZN9options_t5resetEv ; options_t::reset(void)
.text:000000000040314F mov rax, [rsp+58h+argv]
.text:0000000000403154 mov rdx, [rax]
.text:0000000000403157 test rdx, rdx
.text:000000000040315A jz loc_403A8A
.text:0000000000403160 lea rsp, [rsp-98h]
.text:0000000000403168 mov [rsp+58h+var_58], rdx
.text:000000000040316C mov [rsp+58h+c2], rcx
.text:0000000000403171 mov [rsp+58h+argv], rax
.text:0000000000403176 mov rcx, 3887h
.text:000000000040317D call __afl_maybe_log_10 #看这里
开启core dump
echo core >/proc/sys/kernel/core_pattern
开始fuzz(下面用file文件作为样本)
root@giant:~/aflfuzz/fuzztarget/upx# mkdir afl_in afl_out
root@giant:~/aflfuzz/fuzztarget/upx# cp /usr/bin/file afl_in
root@giant:~/aflfuzz/fuzztarget/upx# afl-fuzz -i afl_in -o afl_out ./src/upx.out @@ #其中./src/upx.out为运行的文件
运行界面
american fuzzy lop 2.52b (upx.out)
lq process timing qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqwq overall results qqqqqk
x run time : 0 days, 0 hrs, 3 min, 38 sec x cycles done : 0 x
x last new path : 0 days, 0 hrs, 1 min, 0 sec x total paths : 50 x
x last uniq crash : none seen yet x uniq crashes : 0 x
x last uniq hang : none seen yet x uniq hangs : 0 x
tq cycle progress qqqqqqqqqqqqqqqqqqqqwq map coverage qvqqqqqqqqqqqqqqqqqqqqqqqu
x now processing : 0 (0.00%) x map density : 2.13% / 3.27% x
x paths timed out : 0 (0.00%) x count coverage : 1.20 bits/tuple x
tq stage progress qqqqqqqqqqqqqqqqqqqqnq findings in depth qqqqqqqqqqqqqqqqqqqqu
x now trying : bitflip 2/1 x favored paths : 1 (2.00%) x
x stage execs : 60.7k/152k (39.77%) x new edges on : 40 (80.00%) x
x total execs : 215k x total crashes : 0 (0 unique) x
x exec speed : 992.1/sec x total tmouts : 0 (0 unique) x
tq fuzzing strategy yields qqqqqqqqqqqvqqqqqqqqqqqqqqqwq path geometry qqqqqqqqu
x bit flips : 45/152k, 0/0, 0/0 x levels : 2 x
x byte flips : 0/0, 0/0, 0/0 x pending : 50 x
x arithmetics : 0/0, 0/0, 0/0 x pend fav : 1 x
x known ints : 0/0, 0/0, 0/0 x own finds : 49 x
x dictionary : 0/0, 0/0, 0/0 x imported : n/a x
x havoc : 0/0, 0/0 x stability : 100.00% x
x trim : 0.00%/1181, n/a tqqqqqqqqqqqqqqqqqqqqqqqqj
mqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqj [cpu000: 50%]
无源码fuzz
对无源码的程序进行fuzz一般有两种方法:
- 对二进制文件进行插桩
- 使用-n选项进行传统的fuzz测试
第一种由afl-qemu实现,如果使用第二种方法,把-Q改成-n就行
编译一个AFL版的qemu
root@giant:~/aflfuzz/afl-2.52b# cd qemu_mode/
root@giant:~/aflfuzz/afl-2.52b/qemu_mode# ls
build_qemu_support.sh patches README.qemu
root@giant:~/aflfuzz/afl-2.52b/qemu_mode# ./build_qemu_support.sh
root@giant:~/aflfuzz/afl-2.52b/qemu_mode#cp ../afl-qemu-trace /usr/local/bin/
如果缺少libtool
apt install libtool-bin
Error: devel version of ‘glib2’ not found, please install first.
apt-get install libgtk2.0-dev
最终显示
[+] Build process successful!
[*] Copying binary...
-rwxr-xr-x 1 root root 10351056 Dec 6 08:14 ../afl-qemu-trace
[+] Successfully created '../afl-qemu-trace'.
[*] Testing the build...
[+] Instrumentation tests passed.
[+] All set, you can now use the -Q mode in afl-fuzz!
下面使用无源码方式fuzz readelf 同样也是创建文件夹,放入原始样本(test你自己准备吧)
mkdir afl_in afl_out
mv test ./afl_in/ #这个是readelf需要读取的文件
cp /usr/bin/readelf .
afl-fuzz -i afl_in -o afl_out -Q ./readelf -a @@ #-Q就是无源码进行二进制插装fuzz,如果选传统fuzz,把-Q改成-n就行
运行界面
american fuzzy lop 2.52b (readelf)
lq process timing qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqwq overall results qqqqqk
x run time : 0 days, 0 hrs, 0 min, 18 sec x cycles done : 0 x
x last new path : 0 days, 0 hrs, 0 min, 0 sec x total paths : 77 x
x last uniq crash : none seen yet x uniq crashes : 0 x
x last uniq hang : none seen yet x uniq hangs : 0 x
tq cycle progress qqqqqqqqqqqqqqqqqqqqwq map coverage qvqqqqqqqqqqqqqqqqqqqqqqqu
x now processing : 0 (0.00%) x map density : 2.89% / 3.98% x
x paths timed out : 0 (0.00%) x count coverage : 1.55 bits/tuple x
tq stage progress qqqqqqqqqqqqqqqqqqqqnq findings in depth qqqqqqqqqqqqqqqqqqqqu
x now trying : bitflip 1/1 x favored paths : 1 (1.30%) x
x stage execs : 1006/50.4k (2.00%) x new edges on : 52 (67.53%) x
x total execs : 3181 x total crashes : 0 (0 unique) x
x exec speed : 122.2/sec x total tmouts : 0 (0 unique) x
tq fuzzing strategy yields qqqqqqqqqqqvqqqqqqqqqqqqqqqwq path geometry qqqqqqqqu
x bit flips : 0/0, 0/0, 0/0 x levels : 2 x
x byte flips : 0/0, 0/0, 0/0 x pending : 77 x
x arithmetics : 0/0, 0/0, 0/0 x pend fav : 1 x
x known ints : 0/0, 0/0, 0/0 x own finds : 76 x
x dictionary : 0/0, 0/0, 0/0 x imported : n/a x
x havoc : 0/0, 0/0 x stability : 100.00% x
x trim : 0.00%/1558, n/a tqqqqqqqqqqqqqqqqqqqqqqqqj
mqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqj [cpu000: 50%]
输出的结果
tree -L 3
.
|-- crashes #导致目标接收致命signal而崩溃的独特测试用例。
| |-- README.txt #保存了目标执行这些crash文件的命令行参数。
| |-- id:000000,sig:11,src:000007,op:flip1,pos:63
| |-- id:000001,sig:11,src:000007,op:flip1,pos:63
| |-- id:000002,sig:11,src:000007,op:flip1,pos:63
| |-- id:000003,sig:11,src:000007,op:flip1,pos:63
| |-- id:000004,sig:11,src:000007,op:flip1,pos:192
| |-- id:000005,sig:11,src:000007,op:flip1,pos:192
| |-- id:000006,sig:11,src:000007,op:flip1,pos:770
| |-- id:000007,sig:11,src:000007,op:flip1,pos:914
| |-- id:000008,sig:11,src:000007,op:flip1,pos:1035
| |-- id:000009,sig:11,src:000007,op:flip1,pos:1154
| |-- id:000010,sig:11,src:000007,op:flip1,pos:1154
| `-- id:000011,sig:11,src:000007,op:flip1,pos:1178
|-- fuzz_bitmap
|-- fuzzer_stats #afl-fuzz的运行状态。
|-- hangs #导致目标超时的独特测试用例。
|-- plot_data #用于afl-plot绘图。
`-- queue #存放所有具有独特执行路径的测试用例。
|-- id:000000,orig:file
|-- id:000001,src:000000,op:flip1,pos:0,+cov
|-- id:000002,src:000000,op:flip1,pos:4,+cov
|-- id:000003,src:000000,op:flip1,pos:5,+cov
|-- id:000004,src:000000,op:flip1,pos:6,+cov
|-- id:000005,src:000000,op:flip1,pos:7,+cov
漏洞是否能够利用的验证
afl-collect:alf-utils套件中的一个工具,项目地址:https://gitlab.com/rc0r/afl-utils.git
或者将crash文件用作输入可以使程序崩溃 ./afl_test fuzz_out/crashes/id:000002,sig:06,src:000002,op:havoc,rep:8
gdb调试
r id\:000010\,sig\:11\,src\:000007\,op\:flip1\,pos\:1154
bt
gdb-peda$ bt
#0 0x00000000005306da in PackLinuxElf64::calls_crt1 (this=this@entry=0x9ca030, rela=0x9cb3b8, sz=0x258, sz@entry=0x3c0) at p_lx_elf.cpp:1677
#1 0x000000000053e46a in PackLinuxElf64::canPack (this=0x9ca030) at p_lx_elf.cpp:2108
#2 0x00000000005f88f0 in try_pack (p=0x9ca030, user=0x7fffffffda40) at packmast.cpp:91
#3 0x00000000005fa879 in PackMaster::visitAllPackers (func=0x5f8840 <try_pack(Packer*, void*)>, f=0x7fffffffda40, o=0x7fffffffdbf8,
user=0x7fffffffda40) at packmast.cpp:194
#4 0x00000000005fd0da in PackMaster::getPacker (f=<optimized out>) at packmast.cpp:240
#5 PackMaster::pack (this=0x7fffffffdbe0, fo=0x7fffffffdb10) at packmast.cpp:260
#6 0x00000000006669a5 in do_one_file (iname=iname@entry=0x7fffffffe8ff "id:000010,sig:11,src:000007,op:flip1,pos:1154",
oname=oname@entry=0x7fffffffe180 "id:000010,sig:11,src:000007,op:flip1,pos:1154.001") at work.cpp:158
#7 0x000000000066717c in do_files (i=i@entry=0x1, argc=0x2, argv=0x7fffffffe6f8) at work.cpp:271
#8 0x00000000004054b1 in main (argc=argc@entry=0x2, argv=argv@entry=0x7fffffffe6f8) at main.cpp:1539
#9 0x00007ffff727b830 in __libc_start_main (main=0x4030e0 <main(int, char**)>, argc=0x2, argv=0x7fffffffe6f8, init=<optimized out>,
fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe6e8) at ../csu/libc-start.c:291
#10 0x0000000000406349 in _start ()
代码覆盖率及其相关概念
基本块:缩写为BB,指一组顺序执行的指令,BB中第一条指令被执行后,后续的指令也会被全部执行,每个BB中所有指令的执行次数是相同的,也就是说一个BB必须满足以下特征:
- 只有一个入口点,BB中的指令不是任何跳转指令的目标。
- 只有一个退出点,只有最后一条指令使执行流程转移到另一个BB
边(edge):边就是基本块的跳转
元组:具体到AFL的实现中,使用二元组(branch_src, branch_dst)来记录当前基本块 + 前一基本块 的信息,从而获取目标的执行流程和代码覆盖情况
另一个有源码例子
vuln.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
int vuln(char *str)
{
int len = strlen(str);
if(str[0] == 'A' && len == 66)
{
raise(SIGSEGV);
//如果输入的字符串的首字符为A并且长度为66,则异常退出
}
else if(str[0] == 'F' && len == 6)
{
raise(SIGSEGV);
//如果输入的字符串的首字符为F并且长度为6,则异常退出
}
else
{
printf("it is good!\n");
}
return 0;
}
int main(int argc, char *argv[])
{
char buf[100]={0};
gets(buf);//存在栈溢出漏洞
printf(buf);//存在格式化字符串漏洞
vuln(buf);
return 0;
}
插桩编译
afl-gcc -g -o vuln vuln.c
创建输入输出,并填入输入数据
mkdir afl_in afl_out
echo "hello" > afl_in/test.txt
开始fuzz
afl-fuzz -i afl_in/ -o afl_out/ ./vuln
其crash中文件的内容,就是导致程序崩溃的输出,我们可以用xxd
命令来查看
xxd id\:000000\,sig\:11\,src\:000001\,op\:havoc\,rep\:16
0000000: 4646 1306 5712 002c 136c 5702 f42c FF..W..,.lW..,
另一个无源码例子
还是上边的程序普通编译
gcc vuln.c -o vuln
开始进行binary的fuzz
afl-fuzz -i afl_in/ -o afl_out/ -Q ./vuln
同样crash中文件的内容,就是导致程序崩溃的输出
xxd id\:000001\,sig\:11\,src\:000001\,op\:havoc\,rep\:64
0000000: 46c6 c601 cb8b 0000 e200 cbfa ff7f 00cb F...............
0000010: 0100 cb00 64cb cbcb cbcb cbcb cbed cb00 ....d...........
0000020: 0200 0100 ecc4 c5c5 c5a5 c5c5 c9c5 c5c5 ................
0000030: c5c5 c5c5 c5c5 dec5 c504 00a1 61db adab ............a...
0000040: c67b .{
xxd id\:000002\,sig\:11\,src\:000002\,op\:havoc\,rep\:16
0000000: 4141 4141 4141 4141 5f3f 4141 4141 4141 AAAAAAAA_?AAAAAA
0000010: 4141 4141 4141 3141 4141 41c7 c7c7 c741 AAAAAA1AAAA....A
0000020: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000030: 1041 4141 4141 4141 41ff ff7f ff41 4141 .AAAAAAAA....AAA
0000040: 415c A\
验证
more ./afl_out/crashes/id\:000001\,sig\:11\,src\:000001\,op\:havoc\,rep\:64 | ./vuln
段错误
afl-tmin
精简payload
afl-tmin -i out/crashes/idxxxxxx -o min -Q -- ./a.out
安装afl-clang-fast
一.安装llvm(版本是3.5)
1.需要的文件
LLVM source code
Clang source code
Clang Tools Extra source code
Compiler RT source code
LibC++ source code
上面这些文件在这个链接:http://llvm.org/releases/download.html#3.5.0
下载好了以后,四个压缩包都解压
tar xvf XXXX.xz
得到四个目录:
llvm-3.5.src
cfe-3.5.src
clang-tools-extra-3.5.src
compiler-rt-3.5.src
libcxx-3.5.src
然后
mv cfe-3.5.src clang
mv clang/ llvm-3.5.src/tools/
mv clang-tools-extra-3.5.src extra
mv extra/ llvm-3.5.src/tools/clang/
mv compiler-rt-3.5.src compiler-rt
mv compiler-rt llvm-3.5.src/projects/
这样以后clang,clang-tools-extra和compiler-rt就可以和llvm一起编译了。
apt install clang
apt install meson
apt install cmake
在llvm-3.5.src同一层目录上新建个目录build-3.5并进入:
mkdir build-3.5
cd build-3.5
然后
../llvm-3.5.2.src/configure --enable-optimized --enable-targets=host-only
make -j 4
make install
现在用clang++ -v查看下是否安装成功:
然后
cd /home/afl-2.52b/llvm_mode
make
cd ../
make install
这样就装完了
masker/slave 模式
master:
afl-fuzz -i testcases/ -o syncdir/ -M fuzzer1 tcpdump-4.6.2/tcpdump -nr @@
slave
afl-fuzz -i testcases/ -o syncdir/ -S fuzzer2 tcpdump-4.6.2/tcpdump -nr @@
然后输出文件夹就可以看到4个
ls syncdir/
fuzzer1 fuzzer2 fuzzer3 fuzzer4
使用afl-whatsup查看每一个进程
afl-whatsup syncdir/
configure
CC=afl-clang-fast CXX=afl-clang-fast++ LDFLAGS="-static" ./configure
配合preeny使用的一些sample命令
LD_PRELOAD=/root/preeny/src/desock.so afl-showmap -m2048 -o/dev/null /root/redis/src/redis-server /root/redis/conf < <(echo "PING");
LD_PRELOAD=/root/preeny/src/desock.so ./wget1 --debug localhost:6666 < <(echo "SHUTDOWN");
LD_PRELOAD=/root/preeny/src/desock.so ./wget1 localhost:6666 -O /dev/null < <(echo "SHUTDOWN"); #这个是字符串的形式
LD_PRELOAD=/root/preeny/src/desock.so ./wget1 localhost:6666 < index.html; #这个是文件的形式
# compile and link our test harness using capstone's static library #测试library使用的编译
afl-clang-fast -static harness.c capstone/libcapstone.a -o harness
LD_PRELOAD=/root/preeny/src/desock.so afl-fuzz -i afl_in -o afl_out ./wget3 localhost:6666 -O /dev/null @@
LD_PRELOAD=/root/preeny/src/desock.so afl-showmap -m2048 -o/dev/null ./wget2 localhost:6666 -O /dev/null < <(echo "PING");
ASAN结合使用
./configure CC=afl-gcc CXX=afl-g++ CFLAGS="-m32" LD=afl-gcc--disable-shared
AFL_USE_ASAN=1 make
编译
./configure CC="afl-gcc" CXX="afl-g++" --disable-shared; make
` –disable-shared`这个会让 LD_PRELOAD失效!! «««< HEAD
精简样本
使用afl-cmin进行精简样本,把执行相同路径的给去重,留下 具有相同覆盖范围的最小子集
afl-cmin -i less1k/ -o cmin-out/ -m none ./harness1 @@
=======
84da95c3c45235a33239e4945eafdaed52dad6c6