Fuzzing之后发现的crash分析(自我摸索的记录)
使用Fuzzing工具测试完成之后,如果发现了大量的crashes,我们需要分析crash是否为真的漏洞,同时需要在CVE平台上使用关键字查找这些漏洞是否已经被别人发现。
本次的整理是为跟我一样的初入此行时一片茫然的小伙伴们的,按照自己的理解,本次整理按照以下三个部分进行:POC去重、漏洞类型分析、CVE平台查新与提交。
一、POC去重
可能多个POC触发同一个crashes
去重方式我居然已经记不清,后面碰到时再补充吧~~~~~
二、漏洞类型分析
包括三种分析方法,分别是crashwalk、GDB、Address Sanitizer,我比较推荐Address Sanitizer。
-
crashwalk
(需要注意:在执行AFL时,需要添加--
参数)
具体做法为:
(1) 安装go:apt-get install gdb golang
(2) 安装crashwalk:# mkdir go # export GOPATH=~/go # go get -u github.com/bnagy/crashwalk/cmd/... # ~/go/bin/cwtriage -root . -afl ./path/to/target @@ 对于测试结束结果进行分析:~/go/bin/cwtriage -root fuzzer2/crashes/ -match id -seen ~/afl-experient/binutils-2.29/binutils/objdump -d @@ (同时输出到屏幕和一个名为crashwalk.db的数据库中,上面的-seen代表可以对数据库进行追加写入,通过~/go/bin/cwdump ./crashwalk.db > triage.txt,可以将漏洞进行分类到txt文件中) NOTES:需要AFL命令为afl-fuzz -i input -o output -- ./binutils/size @@ # cwdump ./crashwalk.db > triage.txt
-
GDB
需要在编译时添加-g
(gdb) file nasm Reading symbols from nasm...done. (gdb) run -felf ./input/seed1 Starting program: /home/lbb/afl-experient/Tests/ASAN/nasm-2.14.02/nasm -felf ./input/seed1 Program received signal SIGSEGV, Segmentation fault. expr2 (critical=critical@entry=0) at asm/eval.c:482 482 e = expr3(critical); (gdb) info stack #0 expr2 (critical=critical@entry=0) at asm/eval.c:482 #1 0x0000000000422941 in expr1 (critical=critical@entry=0) at asm/eval.c:456 #2 0x0000000000422cc1 in expr0 (critical=0) at asm/eval.c:430 #3 0x0000000000420233 in expr6 (critical=critical@entry=0) at asm/eval.c:857 #4 0x0000000000421139 in expr5 (critical=critical@entry=0) at asm/eval.c:567 #5 0x000000000042201c in expr4 (critical=critical@entry=0) at asm/eval.c:542 #6 0x0000000000422101 in expr3 (critical=critical@entry=0) at asm/eval.c:508 #7 0x00000000004225c1 in expr2 (critical=critical@entry=0) at asm/eval.c:482 #8 0x0000000000422941 in expr1 (critical=critical@entry=0) at asm/eval.c:456 #9 0x0000000000422cc1 in expr0 (critical=0) at asm/eval.c:430
-
Address Sanitizer,最新版gcc的内存检测工具,用户可以使用-fsanitize=address标签对二进制文件进行编译,这样如果发生了内存访问错误,用户可以获得一份十分详尽的事件信息。
-
编译源码时添加'-fsanitize=address'
想要在错误消息中添加更好的堆栈跟踪,启用-fno-omit-frame-pointer
,此外还可以使用-O1
进行一级优化的编译。具体做法为: (1) 对于单个程序编译,直接在编译时添加在命令行 如:`gcc -g -fsanitize=address -O1 -fno-omit-frame-pointer ./test.c` (2) 对于含有Makefile的项目,在'CFLAGS'后面添加 如:`CFLAGS = -g -fsanitize=address ......` (3) 对于含有configure的项目 ./configure CFLAGS='-g -fsanitize=address' 即可
-
使用一段python代码对Fuzzing的crash进行批量化分析:
(此处借鉴于安全客《从零开始学习fuzzing》,在此基础上做了一小部分修改)
运行方式为# python3 /path/xxx.py /path/crashes /path/program [param] 例如我将此python保存在 ~/mytest/crash_analyze.py,crashes存放在 ~/mytest/jhead-2.04/master/crashes,测试程序为 ~/mytest/jhead-2.04/jhead,因为jhead运行无参数,所以[param]缺省。 # python3 ~/mytest/crash_analyze.py ~/mytest/jhead-2.04/master/crashes ~/mytest/jhead-2.04/jhead 之后会在~/mytest/jhead-2.04/下创建analyze_output,所有分写结果全部保存于此
#!/usr/bin/env python3 import os from os import listdir from os import sys def get_files(): #files = os.listdir("/root/crashes/") files = os.listdir(sys.argv[1]) return files # argv[1]: crashes dir # argv[2]: program-name # argv[3]: param def triage_files(files): len_argv = len(sys.argv) # 漏洞类型的统计 cout_crashes = {"SEGV": 0, "HBO": 0, "UNKNOWN":0} folder = os.path.exists("analyze_output") if not folder: os.makedirs("analyze_output") for x in files: if len_argv == 4: original_output = os.popen(sys.argv[2] + " " + sys.argv[3] + " " + x + " 2>&1").read() else: original_output = os.popen(sys.argv[2] + " " + os.path.join(sys.argv[1] ,x) + " 2>&1").read() output = original_output # Getting crash reason crash = '' if "SEGV" in output: crash = "SEGV" cout_crashes["SEGV"] += 1 elif "heap-buffer-overflow" in output: crash = "HBO" cout_crashes["HBO"] += 1 else: crash = "UNKNOWN" cout_crashes["UNKNOWN"] += 1 address = '' operation = '' if crash == "HBO": output = output.split("\n") counter = 0 target_line = '' while counter < len(output): if output[counter] == "=================================================================": target_line = output[counter + 1] target_line2 = output[counter + 2] counter += 1 else: counter += 1 target_line = target_line.split(" ") address = target_line[5].replace("0x","") target_line2 = target_line2.split(" ") operation = target_line2[0] elif crash == "SEGV": output = output.split("\n") counter = 0 while counter < len(output): if output[counter] == "=================================================================": target_line = output[counter + 1] target_line2 = output[counter + 2] counter += 1 else: counter += 1 if "unknown address" in target_line: address = "00000000" else: address = None if "READ" in target_line2: operation = "READ" elif "WRITE" in target_line2: operation = "WRITE" else: operation = None log_name = (x + "." + crash + "." + address + "." + operation) fn = os.path.join("analyze_output", log_name) f = open(fn,"w+") f.write(original_output) f.close() print("Numbers of the crash:") for ele in cout_crashes.items(): print(ele) if __name__ == "__main__": if len(sys.argv) == 1: print("Please input \"crash_analyze.py --help \"") elif sys.argv[1] == "--help": print("argv[1]: crashes dir\n",\ "argv[2]: program-name\n",\ "argv[3]: param" ) else: files = get_files() triage_files(files)
三、CVE平台查新与提交
-
查新:确定了分析得到的crash为漏洞之后,需要利用发现漏洞的关键信息在CVE平台上查找相应的漏洞是否已经被提交,还可以在所测软件所属平台上进行查询。
例如我们在添加了'-fsanitize=address'编译得到的jhead-3.04上执行前面的crashes里包含的测试用例之后,得到了如下图信息,我们发现发生在jhead-3.04下的exif.c文件的Get32s函数出,通过定位到源码之后发现确实存在此漏洞。 -
我们利用关键字
jhead``Get32s
等在CVE网站进行搜索
CVE网站:https://cve.mitre.org/-
如果发现相关信息,则需要点进去看相应版本和具体发生位置等信息(此漏洞为我1月份提交的)
-
如果没有发现相关信息,并不意味着一定没有,我们需要扩大范围,例如前面是用两个关键词,可以直接用jhead关键词,还可以在其软件对应官网或其他平台
-
-
提交:我们可以将自己发现的漏洞的描述放在一个可以访问的网站,自己创建的、github上存放的、或其他平台上描述的(因为我也是摸着石头过河,参考一下CVE平台上的前辈们的链接),都可以,在CVE平台提交的时候提供相应链接即可。
如何提交,这篇文章写得非常棒CVE申请的那些事- 描述:
例如我提交的描述信息一般放在https://launchpad.net/ubuntu 因为此网站包含了一些ubuntu上的软件,直接定位的相应软件即可进行描述。
- 提交:前面都准备完成之后就是在CVE网站上的提交了,提交时可以同时提交好几个,我有一次连续提交了3个matio的,但后没有任何相应,具体我也不清楚了(我的理解是有些软件比较快,有些比较慢吧,我刚查询完matio相关的CVE,发现依然停留在2019)。
-
查找相应的CNA,如果无法确定自己所提交软件的CNA,可以按照前面漏洞查新部分的方法,查找相同软件的CVE编号里描述的对应的CNA即可。如jhead属于MITRE Corporation,点击后面的Web链接即可。
-
申请CVE-ID,对其进行描述,比较关键的一点就是邮箱、申请的个数、还有下面的Discoverer(s)/Credits(即谁发现的)
-
全部提交完之后会返回一份邮件,应该是提示提交成功的,如果快的话一两天就能收到CVE编号,慢的话一周或者更长时间吧。
- 描述:
这些全部是我的摸索与尝试,有错误或者有更好方案的欢迎评论区大家交流,我们共同进步