fuzz——AFL基礎使用方法

狒猩橙發表於2022-02-13

最近打 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");
    }
    return 0;
}

int main(int argc, char *argv[])
{
    char buf[100]={0};
    gets(buf);//存在棧溢位漏洞
    printf(buf);//存在格式化字串漏洞
    vuln(buf);

    return 0;
}

對其插樁編譯

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> // https://lkml.org/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 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 https://patchwork.kernel.org/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 https://patchwork.kernel.org/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 https://patchwork.kernel.org/patch/10862231/
+                put_user_u32(sys_gettid(), child_tidptr);
             if (flags & CLONE_PARENT_SETTID)
-                put_user_u32(gettid(), parent_tidptr);
+                // taken from https://patchwork.kernel.org/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 https://patchwork.kernel.org/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漏洞看看。

 

參考文章:

https://blog.csdn.net/weixin_50919879/article/details/108916954

 https://xz.aliyun.com/t/4314

https://blog.csdn.net/geniusle201/article/details/111028697

https://www.codeleading.com/article/61745363753/

https://blog.csdn.net/qq_38239282/article/details/120975670

https://bbs.csdn.net/topics/392361391

https://blog.csdn.net/ChuMeng1999/article/details/121880731

相關文章