WinAFL

因为winafl需要的测试时间比较长,所以目前采取的策略:

本地的win10虚拟机环境配置,命令配置,样本收集,成功跑起环境的时候推送到服务器上进行长时间测试(操作相同配置、或者直接推送到服务器上)

环境

  • win10专业版,但是认证过期了
  • 安装WinAFL
  • 安装DynamoRIO(新+旧,注意选对仓库,右边的那个仓库)
  • cmake 3.190,高版本问题也不大,别太低就行
  • Visual Studio 2019(c++环境)

过程

需要提前安装好Visual Studio 2019(c++环境)和perl环境还有doxygen。最好提前安装好,配置好环境变量(或者在make里面当时添加)。

编译DynamoRIO

这个是插桩库,关于fuzz中在Windows上用来插桩的关键工具,如果没有这个东西,winAFL就不能编译出winafl.dll这个关键文件。两种方式,我建议直接安装编译好的版本,关键就是里面的cmake目录。

image-20230416173402004

编译AFL

不同的位数需要分开编译,用到cmake-gui,为什么呢,因为Windows下的命令行不会讲错误高亮出来,你也不知道对错,给自己埋地雷。

64

利用cmake的gui填入相关目录

image-20230416173702072

这里第二个输出目录随便,没有她会自己新建。然后Configure一下,这里需要注意的是在File里有一个清理缓存的选项

image-20230416183339869

Configure里面第一个就是要选择一下编译环境

image-20230416183421094

第二行可以选择是32还是64位,默认是64,一般就这样就直接finish,然后下面可能会报错,缺少message compiler或者是什么其他的就everything找一下,然后把目录添加进去就行,参考这里就行。

完成之后,还需要再点击Generate,完事就去目标目录里,用vs打开sln文件

image-20230416183903174

这个用vs打开,然后用vs编译就行,这里网上的教程基本都是让你用命令行,这种做法相对比较傻逼一些,给自己找麻烦。

image-20230416184042641

直接生成解决方案就行,这里注意会有一个报错,但是好像不影响文件的生成。

注意项目里有没有winafl这个文件,没有的话就说明cmake失败了。

32

注意在cmake里的Configure的时候选择一下,其他步骤一致。vs编译的时候不会报错

最后

image-20230416184433650

看到这样基本就成了。也可以看到目录下生成的文件。

image-20230427205355076

红框中的是关键文件,其他文件都是一些测试用例和我自己写的案例。

使用例子

为了验证程序是不是能跑,先找一个命令测试一下

下面的命令需要在cmd中使用,Windows terminal里面无法执行这个命令

afl-fuzz.exe 
-i in 
-o out 
-D "D:\dynamorio-master\dynamorio-master\Project\bin32" 
-t 20000 
-- 
-coverage_module gdiplus.dll 
-coverage_module WindowsCodecs.dll 
-fuzz_iterations 5000 
-target_module test_gdiplus.exe 
-target_offset 0x1680 
-nargs 1 
-- 
test_gdiplus.exe @@

需要注意的是里面的--是分隔符

-o、-i是输出输入目录

-t是超时时间,单位毫秒

coverage_module这是测试的模块,允许有多个

fuzz_iterations是循环的次数,这个会影响exec speed, 一般速度在几百上千是正常的,过慢就说明前处理过程不行

target_moduletarget_offset是测试的程序,就是要执行的程序,要和最后的相匹配,而且offset一定要是module里的offset,ida可以直接看

-nargs是参数的个数,不包括本身

@@代表参数是自动生成的in里的

最后的结果如下所示

image-20230416194408205

AFL++

afl已经很久没有更新了,但是推出的afl++还在更新,所以可以采用afl++来替代afl。

配置这个环境很简单,直接使用docker就行了,注意设置一个共享目录用来交流文件。

docker pull aflplusplus/aflplusplus

docker run -ti -v $HOME:/home aflplusplus/aflplusplus

export $HOME="/home"

IrfanView案例

首先在官网下载最新版本,然后下载一些他的插件

image-20230422141217671

在这里可以下载插件,可以看到这里还是有不少CVE版本的。

首先先找一下样本库,从github仓库中可以下载,下载之后需要对样本进行一个精简,让 afl自己去根据特征进行删减来提高效率。

寻找偏移

不论干啥都需要先找到处理文件的对应的偏移,可以使用ProcMon来查找

image-20230422151412447

找到处理偏移之后,就可以用winafl自带的工具进行样本筛选

python winafl-cmin.py 
--working-dir C:\Users\moshe\Desktop\fuzz\winafl\build32\bin\Release 
-w 3 
-i C:\Users\moshe\Desktop\fuzz\samples 
-o C:\Users\moshe\Desktop\fuzz\irfanview_cmin 
-t 4000 
-D C:\Users\moshe\Desktop\fuzz\dynamorio\build\bin32 
-covtype edge 
-target_module "i_view32.exe" 
-coverage_module "i_view32.exe" 
-target_offset 0xa54fd
-nargs 3
-- 
C:\Users\moshe\Desktop\fuzz\iview457\i_view32.exe @@ /convert="NUL" /silent
  • 第一个参数是winafl.dll所在的目录
  • -w是CPU的核心数,提高筛选速度
  • i/o就是输入和输出目录
  • -t就是超时时间
  • -D是DynamoRIO 目录。这与之前执行 drrun 的位置相同.exe
  • covtype:

默认覆盖跟踪器仅跟踪遍历了哪些基本块。Edge还跟踪基本块被命中的顺序。因此,如果您的程序从具有两个独立输入的基本块 A->B->C 和 A->C->B 开始,则基本块跟踪器只会看到一个感兴趣的输入,而边缘跟踪器将同时看到两个输入。

  • 后面就是目标模块和测试模块了
  • 程序参数的个数
  • 后面就是执行的时候的参数

经过筛选完之后就是这个结果

image-20230422151938867

使用drrun验证

这是关键的一步,需要注意的是:

测试程序和winafl一定要在同一个目录下,不然会出现定位不到偏移的问题。比如我测试的是aaa.exe但是主要目标是他的插件bbb.dll这里bbb的位置无所谓,但是exe一定要同目录

一定要注意一定要注意一定要注意一定要注意

可以使用如下命令进行测试,这个文档不是一天写的,所以测试的程序不一样,懒得改了。

D:\DynamoRIO9.92.19461\bin32\drrun.exe 
-c winafl.dll 
-debug 
-target_module calldll.exe 
- coverage_module test_call.dll
-fuzz_iterations 10 
-target_offset 0x1590 
-- 
calldll.exe "123123123456asdf"

查看覆盖率

利用命令,生成一个log文件,然后利用ida安装的lighthouse插件,在

image-20230423175552880

load生成的log file,就可以明显的看到覆盖率了,但是这里需要注意的是lighthouse已经很久没更新了,所以需要一个旧版本的drrun来测试,查看的结果其实应该基本一样的。如果报错了,就降低drrun的版本,但是编译afl的时候还是用新版本的drrun去编译。然后生成的对应的文件要load进对应文件的ida中,用错了也会报错。

D:\DynamoRIO-Windows-8.0.18712\bin32\drrun.exe  
-t drcov 
-- 
calldll.exe adsfadfadfdasfassdf 12345609876llllllllllllllllll

总结

利用procmon实际上就是进行一个大体的定位,后续用到的drrun就是对具体模块中的代码块进行的定位,可以看到覆盖百分率这些东西,看一下目标模块怎么样就可以了,或者有没有陷入奇怪的代码中。

开始模糊测试

使用经典的winafl命令就可以直接进行测试了,因为这是利用 DynamoRIO 进行的动态插桩,所以不需要编译啥的。

afl-fuzz.exe 
-i C:\Users\moshe\Desktop\fuzz\irfanview_cmin 
-o C:\Users\moshe\Desktop\fuzz\winafl_output 
-t 1000+ 
-D C:\Users\moshe\Desktop\fuzz\dynamorio\build\bin32 
-- 
-coverage_module "i_view32.exe" 
-target_module "i_view32.exe" 
-target_offset 0x082550 
-- 
C:\Users\moshe\Desktop\fuzz\iview457\i_view32.exe @@ /convert="NUL" /silent
  • iotD这些参数都是固定的含义
  • -- 作为分隔符
  • target_offset:目标的相对于文件头的偏移

开始测试:

image-20230422154314558

不难发现,全是一堆time out,这显然是不成功的。但是基本的流程清楚了。这是因为他找不到要测试的dll库了,需要手动patch一下目标程序让他可以在本目录中寻找插件dll,否则winafl会报错,winafl不能跨目录

image-20230424122030379

修改一下这里,然后将webp托到同目录就可以了就可以同目录测试了。

-f

这个测试可以指定输入文件的名称,如下所示:

afl-fuzz.exe -i in -o out -S s1 -D "D:\DynamoRIO9.92.19461\bin32" -f test.webp -t 7000 -- -c overage_module WebP.dll -target_module call_webp.exe -target_offset 0x0155c -fuzz_iterations 8000 -nargs 1 -- call_webp.exe @@

这个输入会根据in目录中的文件变异得到。

提高效率

直接通过原始程序测试会有许多其他路径,降低fuzz的速度,实测峰值在40次每s左右,这里我们可以手动写一个calldll程序来进行fuzz

首先就是要搞清楚dll里某个导出函数的参数,ida可能会出错,所以这里我们要x64dbg和ida联合调试,下面是我写的calldll文件,可以将速度提升到1000次每秒左右,速度非常快,仅供参考:利用下面的程序我们可以只fuzz下面的那个fuzz函数,防止fuzz程序陷入其他无用模块。

#include<stdio.h>
#include<windows.h>
// L"\x65E0\x0000\x215B\x54C4" 2
wchar_t iarg3[520] = { 0 };
wchar_t iarg4[520] = { 0 };
int iarg5[17] = {0};

// typedef void (WINAPI *Readwebp_W)(char *D, wchar_t a2[], wchar_t arg2[] , wchar_t *ini_path, wchar_t* file_path);
typedef void (WINAPI *Readwebp_W)(LPCWSTR file_path, LPCWSTR ini_path, wchar_t *arg3, wchar_t *arg4, int *arg5);

void fuzz(Readwebp_W readwebp_W, LPCWSTR file_path)
{
    LPCWSTR lpFileName = TEXT(L"C:\\Users\\Rootkit\\AppData\\Roaming\\IrfanView\\i_view32.ini");
    readwebp_W(file_path, lpFileName, iarg3, iarg4, iarg5);
}

int main(int argc, char **argv) 
{
    HMODULE PDFDLL = LoadLibraryA("C:\\Users\\Rootkit\\Desktop\\winafl-TEST\\IrfanView\\webp.dll");
    if(PDFDLL == NULL)
    {
        printf("call pdf.dll wrong , error code : %d\n", GetLastError());
        return 0;
    }

    Readwebp_W readWebp = (Readwebp_W)GetProcAddress(PDFDLL, "ReadWebP_W");
    if (readWebp == NULL) 
    {
        printf("GetProcAddress readWebp failed! error code: %d\n", GetLastError());
        FreeLibrary(PDFDLL);
        return 1;    
    }

    int len = strlen(argv[1]); // length of input string

    // calculate required length of wide byte string (add 1 for null terminator)
    int wideLen = MultiByteToWideChar(CP_UTF8, 0, argv[1], len, NULL, 0) + 1;
    printf("%d\n", wideLen);
    wideLen = len + 1; // UTF-8 to wide char conversion, assuming all chars are the same (no surrogates
    printf("%d\n", wideLen);

    // allocate memory for wide byte string
    LPWSTR wideStr = (LPWSTR) malloc(wideLen * sizeof(wchar_t));

    // convert narrow byte string to wide byte string
    MultiByteToWideChar(CP_UTF8, 0, argv[1], len, wideStr, wideLen);

    // null-terminate the wide byte string
    wideStr[wideLen - 1] = 0;
    printf("i will call the fuzz func\n");
    fuzz(readWebp, wideStr);

    FreeLibrary(PDFDLL);
    return 0;
}

然后通过-M maste和-S s1这些参数来多开进程来提高效率,影响到内存和cpu,每一个进程会独占一个cpu核心,内存也会成波浪式使用,所以要懂得取舍。

实际效果

afl-fuzz.exe -i in -o out -M master -D "D:\DynamoRIO9.92.19461\bin32" -t 7000 -- -c overage_module WebP.dll -target_module call_webp.exe -target_offset 0x0155c -fuzz_iterations 8000 -nargs 1 -- call_webp.exe @@

afl-fuzz.exe -i in -o out -S s1 -D "D:\DynamoRIO9.92.19461\bin32" -t 7000 -- -c overage_module WebP.dll -target_module call_webp.exe -target_offset 0x0155c -fuzz_iterations 8000 -nargs 1 -- call_webp.exe @@

afl-fuzz.exe -i in -o out -S s2 -D "D:\DynamoRIO9.92.19461\bin32" -t 7000 -- -c overage_module WebP.dll -target_module call_webp.exe -target_offset 0x0155c -fuzz_iterations 8000 -nargs 1 -- call_webp.exe @@

afl-fuzz.exe -i in -o out -S s3 -D "D:\DynamoRIO9.92.19461\bin32" -t 7000 -- -c overage_module WebP.dll -target_module call_webp.exe -target_offset 0x0155c -fuzz_iterations 8000 -nargs 1 -- call_webp.exe @@

IMG_6040

速度相当可观,大概是测试了33小时,当last new path的值长时间不变的时候,就差不多可以停了,我停止的时候他已经4小时没有新路径了,所以就停了,也没有fuzz出crash。

没有版权,随便复制,免费的知识应该共享 all right reserved,powered by Gitbook该文章修订时间: 2023-04-27 21:17:24

results matching ""

    No results matching ""