winafl配置已经针对某开源软件的模糊测试

查看 34|回复 0
作者:Rootkit123   
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目录。

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

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

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

    第二行可以选择是32还是64位,默认是64,一般就这样就直接finish,然后下面可能会报错,缺少message compiler或者是什么其他的就everything找一下,然后把目录添加进去就行,参考这里就行。
    完成之后,还需要再点击Generate,完事就去目标目录里,用vs打开sln文件

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

    直接生成解决方案就行,这里注意会有一个报错,但是好像不影响文件的生成。
    注意项目里有没有winafl这个文件,没有的话就说明cmake失败了。
    32
    注意在cmake里的Configure的时候选择一下,其他步骤一致。vs编译的时候不会报错
    最后

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

    红框中的是关键文件,其他文件都是一些测试用例和我自己写的案例。
    使用例子
    为了验证程序是不是能跑,先找一个命令测试一下
    下面的命令需要在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_module和target_offset是测试的程序,就是要执行的程序,要和最后的相匹配,而且offset一定要是module里的offset,ida可以直接看
    -nargs是参数的个数,不包括本身
    @@代表参数是自动生成的in里的
    最后的结果如下所示

    AFL++
    afl已经很久没有更新了,但是推出的afl++还在更新,所以可以采用afl++来替代afl。
    配置这个环境很简单,直接使用docker就行了,注意设置一个共享目录用来交流文件。
    docker pull aflplusplus/aflplusplus
    docker run -ti -v $HOME:/home aflplusplus/aflplusplus
    export $HOME="/home"
    IrfanView案例
    首先在官网下载最新版本,然后下载一些他的插件

    在这里可以下载插件,可以看到这里还是有不少CVE版本的。
    首先先找一下样本库,从github仓库中可以下载,下载之后需要对样本进行一个精简,让 afl自己去根据特征进行删减来提高效率。
    寻找偏移
    不论干啥都需要先找到处理文件的对应的偏移,可以使用ProcMon来查找

    找到处理偏移之后,就可以用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 开始,则基本块跟踪器只会看到一个感兴趣的输入,而边缘跟踪器将同时看到两个输入。
  • 后面就是目标模块和测试模块了
  • 程序参数的个数
  • 后面就是执行的时候的参数

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

    使用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插件,在

    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:目标的相对于文件头的偏移

    开始测试:

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

    修改一下这里,然后将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
    #include
    // 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 @@

    速度相当可观,大概是测试了33小时,当last new path的值长时间不变的时候,就差不多可以停了,我停止的时候他已经4小时没有新路径了,所以就停了,也没有fuzz出crash。
    欢迎各位大佬一起交流学习关于fuzz的相关知识

    测试, 文件

  • 您需要登录后才可以回帖 登录 | 立即注册

    返回顶部