fuzz——AFL基础使⽤⽅法
最近打 ctf 的时候感觉有点遇到瓶颈,就来 fuzz 这块看看。
AFL 全称为 American huzzy loop,是 Fuzzing 最⾼级的测试⼯具之⼀。这个⼯具对有源码和⽆源码的⼆进制程序均可以进⾏ fuzz 测试。alf 各位⾃⾏安装即可,值得注意的是,在我本机 glibc2.31 的环境下,编译 alf 前要对 AFL/llvm_mode/afl-clang-fast.c修改⼀下,否则会出现报错,只需把部分内容注释掉即可
输⼊ afl-fuzz 出现下图即安装成功
我们先看对有源码的⼆进制程序是怎样进⾏测试:
在 AFL ⽂件夹⾥会有很多⽬录,我们进⼊ test。⾸先先把⾃⼰要测试的源码放进去,再建两个⽂件夹分别放测试输⼊的内容和测试输出的内容,我这⾥就建了 fuzz_in , fuzz_out。在fuzz_in ⾥⾯还要建⼀个⽂件,⾥⾯随便放⼀些字母就⾏(这⾥笔者也不是很清楚为什么)。在有源码的情况下我们⽤ afl ⾃带的编译器,对其进⾏编译这会使测试更加⾼效。原因是 afl ⾃带的编译器在编译时会对⽬标程序插桩,故此过程叫插桩编译。我从其他师傅的博客⾥了⼀个简单的⼆进制程序来进⾏测试。
#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");
}
return0;
}
int main(int argc, char *argv[])
{
char buf[100]={0};
gets(buf);//存在栈溢出漏洞
printf(buf);//存在格式化字符串漏洞
vuln(buf);
return0;
}
对其插桩编译
afl-gcc -g -o afl_test afl_test.c
我们接下来就可以对其进⾏测试了,此外在 fuzz 前要关闭系统的核⼼转储,确保在 fuzz 的过程中即使出现 crash 也不会停⽌,不然就会出现如下报错
我们关闭系统核⼼转储开始测试
sudo su
echo core >/proc/sys/kernel/core_pattern
exit
afl-fuzz -i fuzz_in -o fuzz_out ./afl_test
这上⾯的数字是彩⾊的,注意 overall results ⾥的 cycles results 的颜⾊会随着 fuzz 次数的增加从红⾊到黄⾊到蓝⾊到绿⾊,当他到绿⾊的时候也就说明基本上该有的 crash 都出来了,继续跑下去,发现的东西也很少了,此时我们就可以 crtl+c 结束测试。
此时我们就可以从我们刚刚建⽴的 fuzz_out ⽂件夹⾥看到测试的结果了。
我们再进⾏查看
之前⼀次测试的时候格式化字符串也测试出来了,不知道为什么这次没出来。好吧,那插桩测试就写到这⾥。
下⾯我们来看⽆源码的测试过程。
进⾏⽆源码测试之前我们要进⼊到 AFL/qemu_mode 在终端中执⾏⼀下 ./build_qemu_support.sh
遇见如下报错:
make[1]: *** [/xxxxxxxx/AFL/qemu_mode/qemu-2.10.0/rules.mak:66: linux-user/syscall.o] Error 1
make: *** [Makefile:326: subdir-x86_64-linux-user] Error 2
查阅资料后得知:给 /AFL/qemu_mode/patches/syscall.diff 打个补丁即可
--- qemu-2.10.0-clean/linux-user/syscall.c 2020-03-12 18:47:47.898592169 +0100
+++ qemu-2.10.0/linux-user/syscall.c 2020-03-12 19:16:41.563074307 +0100
@@ -34,6 +34,7 @@
#include <sys/resource.h>
#include <sys/swap.h>
#include <linux/capability.h>
+#include <linux/sockios.h> // /lkml/2019/6/3/988
#include <sched.h>
#include <sys/timex.h>
#ifdef __ia64__
@@ -116,6 +117,8 @@ int __clone2(int (*fn)(void *), void *ch
#include "qemu.h"
+extern unsigned int afl_forksrv_pid;
+
#ifndef CLONE_IO
define的基本用法#define CLONE_IO 0x80000000 /* Clone io context */
#endif
@@ -256,7 +259,9 @@ static type name (type1 arg1,type2 arg2,
#endif
#ifdef __NR_gettid
-_syscall0(int, gettid)
+// taken from /patch/10862231/
+#define __NR_sys_gettid __NR_gettid
+_syscall0(int, sys_gettid)
#else
/* This is a replacement for the host gettid() and must return a host
errno. */
@@ -6219,7 +6224,8 @@ static void *clone_func(void *arg)
cpu = ENV_GET_CPU(env);
thread_cpu = cpu;
ts = (TaskState *)cpu->opaque;
- info->tid = gettid();
+ // taken from /patch/10862231/
+ info->tid = sys_gettid();
task_settid(ts);
if (info->child_tidptr)
put_user_u32(info->tid, info->child_tidptr);
@@ -6363,9 +6369,11 @@ static int do_fork(CPUArchState *env, un
mapping. We can't repeat the spinlock hack used above because
the child process gets its own copy of the lock. */
if (flags & CLONE_CHILD_SETTID)
- put_user_u32(gettid(), child_tidptr);
+ // taken from /patch/10862231/
+ put_user_u32(sys_gettid(), child_tidptr);
if (flags & CLONE_PARENT_SETTID)
- put_user_u32(gettid(), parent_tidptr);
+ // taken from /patch/10862231/
+ put_user_u32(sys_gettid(), parent_tidptr);
ts = (TaskState *)cpu->opaque;
if (flags & CLONE_SETTLS)
cpu_set_tls (env, newtls);
@@ -11402,7 +11410,8 @@ abi_long do_syscall(void *cpu_env, int n
break;
#endif
case TARGET_NR_gettid:
- ret = get_errno(gettid());
+ // taken from /patch/10862231/
+ ret = get_errno(sys_gettid());
break;
#ifdef TARGET_NR_readahead
case TARGET_NR_readahead:
成功!
⼜出现如下报错:
这时我们返回 AFL ⽬录重新 make install 即可
此后和插桩测试的过程就⼤体⼀致了,只是在 test ⾥直接放进⼆进制程序即可,并且加上参数 -Q
afl-fuzz -i fuzz_in -o fuzz_out -Q ./afl_test2
好了 afl 的⼀些基础⽤法掌握了,以后就在 ctf 打不动的时候,跟着其他师傅的博客去复现CVE漏洞看看。参考⽂章:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论