20199119 2019-2020-2 《网络攻防实践》综合实践

NAUTILUS: Fishing for Deep Bugs with Grammars

这是一篇FUZZ类文章,发表在NDSS 2019,第一作者是来自波鸿大学的Cornelius Aschermann。

论文主要内容 :Fuzzing那些需要有着复杂结构输入的程序,例如语言解释器,一直是学术界的一个难点。传统的基于变异的Fuzzer例如AFL,往往需要一个从互联网爬取的语料库,但这些语料库往往只包含了这些语言常用的语法,较难触发罕见的bug。而另一类基于生成的fuzzer则基于一定的模板或语法,往往能够生成结构性较强的输入,在fuzz语言解释器上取得了较好的效果。比较这两类fuzzer,作者认为前者使用了代码覆盖作为回馈,而后者能够生成高度结构化的输入,因此将这两种思路结合,提出了一个综合了语法和反馈的fuzzer——Nautilus。

贡献:

1.介绍并评估了第一个基于语法和反馈结合的Fuzzer-----NAUTILUS,对于高度结构化输入的目标,NAUTILUS可以显著提高效率和有效性,并且不需要任何语料库。

2.NAUTILUS一些方面优于传统Fuzzer,在多个软件项目中发现了一些新的安全漏洞,其他一些Fuzzer没有发现这些漏洞。

3.在ChakraCore、PHP、mruby和Lua中发现多个bug,并分配了6个CVE。

CVE-2018-10191、CVE-2018-10199、CVE-2018-11743、CVE-2018-12247、CVE-2018-12248、CVE-2018-12249。

研究工作:

安全漏洞对于软件的测试,需要找到它的输入口,然后构造大量的随机输入文件,企图使软件执行异常的程序执行路径,以找到崩溃点。在分类方面,可以把Fuzz大概分为两类,基于变异的Fuzz和基于生成的Fuzz,在基于变异的Fuzz,比较经典的例子是AFL,是通过一个语料库的提供,提供一些现有的输入,然后通过这个Fuzz对输入进行变异,再输入到软件中进行执行。基于生成是提供一些生存法则来不断地生成随机的一些测试用例,无论是哪一种,他们在执行之后都需要对一个程序的执行结果进行分析,以反馈到下一步的一个执行策略。

20199119 2019-2020-2 《网络攻防实践》综合实践_第1张图片

路径探索:

实用的一种方法就是覆盖率引导,覆盖率引导就是当程序在执行的时候通过插桩等方式得到程序的代码执行率,但覆盖率改变的话,说明程序进行了不一样的执行路径,在这个总的覆盖率统计中,还可以判断出这个程序所有的执行路径被测试了多少,相关的工具还有污点分析和符号执行,这些都是一些辅助把程序的执行路径映射到生成与变异上。

高度结构化输入问题:

测试对象对于测试用例的输入有着不同的结构性要求,对于像协议报文和媒体文件这些以内容为主的输入要求是较低的,而对于解释器编译器这样的输入内容是程序文件的,就需要一个高度结构化输入。

对于一般的Fuzz而言,如果是基于变异的,它的大部分测试用例在语法解析步骤就不能通过,因为是一个基于二进制文本的变异,如果是基于生成的Fuzz测试用例,大部分在语义检查部分也没有办法通过。

一般的Fuzz遇到的问题:

1.通过这个检查的时候,也许会构造一个非常复杂的策略,如何保证是有用的,只是完全通过的话,就是一条线走到底,直接通过,也没有办法达到目的,找到隐藏的崩溃点。

2.同时要保证代码覆盖率。

3.还有一个所有工程设计的通用指标,效率问题。
20199119 2019-2020-2 《网络攻防实践》综合实践_第2张图片

语法工具解决了这些问题,NAUTILUS在文法研究领域,使用了上下文无关文法(CFG),CFG非常适合指定高度结构化的输入语言。

CFG是编译原理里面一个重要概念,几乎所有程序设计语言都是通过CFG来定义的。基于生成的fuzzer在fuzz这些语言的解释器时,往往就会利用这种语法来生成一串合法的输入。相对应地,变异也是以语法树上的节点为基础进行的,与AFL等传统fuzzer在字符串上进行变异不同。

G是一个四原式,包括非终结符和终结符,还有一个产生式,即构造出一个出现语法树的规则,非终结符就是蓝色的节点,如果用汉语句子来比喻,这些蓝色的节点就是主谓宾等一些概念性的词语,最终推导出来的具体句子就是终结符。
直接推导并不需要通过一些上下文的这样一个关联性判断,所以说被称作上下文无关文法,CFG定义为一个四元式:G=(N,Σ,R,s)

20199119 2019-2020-2 《网络攻防实践》综合实践_第3张图片

例如:

N:{PROG,STMT,EXPR,VAR,NUMBER}

Σ:{a,1,2,=,return 1}

R:{ PROG —> STMT

​ PROG —> STMT;PROG

​ STMT —> return 1

​ STMT —> VAR=EXPR

​ VAR —> a

​ EXPR —> NUMBER

​ EXPR —> EXPR+EXPR

​ NUMBER —> 1

​ NUMBER —> 2

}

S:PROG

G1一个可能的派生是

最终派生的字符串是a=1。

CFG生成的字符串可以用派生树表示,根标记为开始符号,NAUTILUS主要操作派生树,派生树是对应用结构变异的输入内部表示,在NAUTILUS也允许使用额外的脚本来转换输入以扩展CFGs。

挑战:

1.生成语法和语义上有效的输入。生成的输入需要通过目标应用程序的语法和语义检查,以进入下一个计算阶段。

2.使用独立的语料。

3.对于目标需要有高覆盖率。

4.工具表现好。

因此设计NAUTILUS考虑到了这些挑战。

20199119 2019-2020-2 《网络攻防实践》综合实践_第4张图片

第一步是使用检测工具1编译目标应用程序的源代码,以便提供覆盖率信息反馈。之后,启动Fuzzer解析用户提供的语法2,再生成少量随机初始输入3,并传递给调度器,接着NAUTILUS通过执行检测的二进制文件4测试新生成的输入是否触发任何新的覆盖,如果触发,NAUTILUS使用语法将其最小化,添加到队列5中,调度器会触发现有输入的变异6或派生一个新的输入7。对于队列中的输入,应用基于语法的变异。变异之后,输入被添加到队列8,在分析运行9中使用。

过程详解:
基于CFG的初始种子生成

生成算法应产生使用语法不同方面的输入,以使覆盖范围最大化。

朴素生成:

对于同一个非终结符,等概率选取产生式运行生成,生成速度快。一个文本对于STMT非终结符有两个产生式,可以生成一段return1,也可以生成一个概念性的一个变量也就是等于一个数字的产生式,都是各自50%的概率,会产生一个问题,因为在变量和数字方面都会进行一个往下的派生,但是在总体的生成数字上有一半都会是到return1就停止,那会产生大量的重复输入,虽然重复输入可以通过添加一个过滤器过滤掉就可以解决,但是有个无法避免的问题就是它的生成效率很低,与做工程不符。

20199119 2019-2020-2 《网络攻防实践》综合实践_第5张图片

NAUTILUS利用了一种经典的算法

均匀生成:

对于生成的字符串,用长度去分类,生成的长度是n,就把它归到一类里面,在这一类里所有长度一样的字符串概率都相同,举例:对于1+1+1这个字符串,它可以通过在第一个分支节点,通过最右的这个节点进行派生,也可以通过左边节点进行派生,它具有两种派生形式,通过均匀生成算法,识别出同一个字符串,两种语法本质上是一样的,最终我们达到一个效果就是在长度相同的字符串里面,他们生成的概率相同。

20199119 2019-2020-2 《网络攻防实践》综合实践_第6张图片

最小化:

之后进行一个最小化的操作,是抽象语法树上一个变异策略的其中一部分,有两种,一种是子树最小化,子树有两种形式,把它替换成一个最小的子树,有一个递归结构,逐步自底向上不断去缩小递归的规模,在替换之后我们就生成了一个新的输入法,放到程序界面进行执行,检查的覆盖率,如果覆盖率不变,说明这两个结构是一样的,表明用这个小子树也是一样的效果,效率会高很多,就保留改变的新输入,但是如果覆盖率发生了变化,那说明对子树的这个操作破坏了文件结构与程序执行路径,现有的程序执行路径那么就不能改,就要保留改动之前的输入,在队列里面去找下一个进行最小化。
20199119 2019-2020-2 《网络攻防实践》综合实践_第7张图片

变异:

输入最小化操作之后,使用多种变异方法生成新的测试用例。大部分借用了AFL的编译策略。

随机变异:选择树中随即一个节点进行替换生成新的随机树。

规则变异:用其他可能的规则生成的子树顺序替换输入的节点。

随机递归变异:选择树的一个随机递归并重复该递归2n次。

剪接变异:将一个子树的部分输入提取并放置到另一个子树,找到不同的路径输入组合。

AFL变异:可以使用AFL的bitflips,也可以用感兴趣的值替换字符串部分,还可以在字符串中的数值添加或减去一些值。

修改后的字符串被添加到树中替换原来的子树。

通过位翻转将1+2翻转为1xf,之后替换子树。

20199119 2019-2020-2 《网络攻防实践》综合实践_第8张图片

当NAUTILUS得到候选派生树后,需要生成实际的输入,将派生树转换为二进制文件的步骤叫做unparsing。对于CFG,定义unparsing函数来连接所有未解析的子树。

实现:

使用了ANTLR开源语法分析器,通过输入一些自动生成的语法输入来自动生成语法树,并进行可视化显示,在ANTLR里面已经有200多种编程语言的语法公开可用,可以直接拿来用。

在生成一些初始输入之后,调度器决定接下来应该尝试哪个输入:(i)用某个特定的变异对现有输入进行变异,或者(ii)从头生成一个新的输入。

调度程序按顺序处理队列中的每一项。队列中的每个项都有一个状态,该状态指示从队列中取出它时将如何处理它。状态可以是以下值之一:

init:如果一个输入触发了一个新的转换,那么它将保存在具有init状态的队列中。当调度器选择处于init状态的项时,将最小化该项,完成之后状态被设置为det

det:det中的项使用规则变异、随机(递归)变异和剪接变异进行变异。当规则修改器对某项执行完操作时,该项将移至detafl状态。

detafl:det中的项使用AFL、随机(递归)变异器和剪接变异器器进行变异。当AFL 变异完成一个项目时,它会移动到随机状态。

random:只有随机变异、随机递归变异和剪接变异应用于此条目。与AFL相反,在继续下一个输入之前,不会完成每一个阶段。相反,在继续处理下一个输入之前,NAUTILUS只在每个输入上花费很短的时间。因此,可以快速地探索那些可能产生新覆盖率的输入,这允许实现类似AFLFast的效果。

20199119 2019-2020-2 《网络攻防实践》综合实践_第9张图片

选择要执行的输入树后,将其解析为输入字符串。然后,使用一个类似于AFL使用的fork服务器来运行目标程序,它可以高效地启动目标应用程序。有三种可能的状态可以遵循:(i)在执行目标应用程序崩溃,然后,输入的二进制表示导致崩溃保存在一个单独的文件夹,(ii)输入引起的目标应用程序新路径,然后,输入的树表示法被添加到队列,(3)输入没有触发任何新的改变和被丢弃。

复现工作:

自己选的坑,哭着也得跳。

把整体论文都差不多了开始复现工作,第一步从github上下载文件,第一步就遇到障碍,linux上首先先安装git,之后git需要登录github,按照网上教程,设置了user和pwd,出错,之后经过百转千回找到一篇教程,总算是从命令行登录了github。教程链接:git命令行登陆github进行操作这一步未截图。

第二步使用AFL编译开源文件中给的测试程序,将test.c编译为test文件

20199119 2019-2020-2 《网络攻防实践》综合实践_第10张图片

第三步原论文使用的是rust环境,不知为何配置rust环境一直失败,于是换用了AFL对测试程序进行fuzzing,并使用了网上的程序sam2p,一同fuzzing,测试程序跑出了crash,sam2p为检测出crash,参考了博客

github上开源工具介绍使用方法应该是简化了,没有fuzz经验的很难入门,需要AFL、llvm等支持。坑了我好久......

20199119 2019-2020-2 《网络攻防实践》综合实践_第11张图片

20199119 2019-2020-2 《网络攻防实践》综合实践_第12张图片

测评:

在四个应用程序上测试NAUTILUS,均发现漏洞,也与其他先进的Fuzzer进行对比,回答了六个问题:

1.NAUTILUS能否识别现实应用中的新bug

通过模糊四个目标应用程序评估了NAUTILUS,能在所有四个系统中发现新的bug,其他fuzzer在评估期间没有发现,所有的bug都得到了供应商的报告和承认。

2.NAUTILUS是否比其他先进的fuzzers更有效率

使用AFL和IFuzzer和NAUTILUS进行对比,如下表可以可出,NAUTILUS能够发现更多的额外覆盖率,更有效率。

20199119 2019-2020-2 《网络攻防实践》综合实践_第13张图片

3.对于具有高度结构化输入的目标应用程序,文法的使用是否提高了模糊处理效率?

文法模糊改进了由AFL执行的变异模糊。

4.当使用语法时,反馈的使用在多大程度上提高了模糊性能?

首先禁用覆盖率反馈机制,从下图和表1的nofeedback看出,使用朴素生成语法fuzzer可以显著提高AFL的覆盖率。添加反馈后,新分支是原来的两倍多。

20199119 2019-2020-2 《网络攻防实践》综合实践_第14张图片

5.复杂的生成方法比简单的方法需要更多的计算能力,它真的提高了模糊处理的性能吗?

由上图使用朴素和均匀生成分析了NAUTILUS的性能,对于ChakraCore和mruby,朴素生成结果相似,在PHP上性能稍好,在Lua上性能明显较差。表明当朴素生成与过滤器结合使用时,执行情况与复杂的均匀生成相似,并少了均匀生成的复杂性。

6.每种变异方法对找到新路径有多大贡献?

对使用的变异方式对覆盖率的贡献度进行了评估,可以看到其中剪接变异对于提高代码覆盖率的效果较好。

20199119 2019-2020-2 《网络攻防实践》综合实践_第15张图片

不足:

与一些如AFL工具相比,NAUTILUS需要源级别访问来添加覆盖率反馈所需要的工具,然而,这些方法本身可以很容易地在动态二进制工具或基于Intel PT的反馈机制之上实现。NAUTILUS仍然需要语法,且语法需要包含重要符号的列表,比如标识符或类名。尽管脚本支持是一种非常强大的原语,可以生成大量与上下文无关的结构,但有些常见特性有时需要对语法进行重大的重新构造。

未来值得研究相关工作:

为了进一步简化Fuzzer的使用,可以使用AFL-QEMU模式后端来切换基于工具的后端。这样,即使使用高度结构化输入语言的二进制目标也可以有效地模糊化。

在通过重要符号扩展语法时,将实用程序的输出添加到语法中的这个步骤可以自动化,进一步减少手工工作量。

研究通过使用机器学习技术以减少对语法的依赖。

开发其他性能更优的生成方法。

总结:

这项工作证实了语法的使用提高了解释复杂输入的模糊程序的有效性。

将语法模糊与反馈结合起来可以进一步改善模糊过程。通常,将反馈添加到基于语法的模糊测试中,对于我们的四个目标(mruby,PHP,Lua和ChakraCore),代码覆盖率至少增加两倍。与不基于语法的工具(例如AFL)仅使用反馈驱动的模糊测试进行比较时,对种子语料库的改进更为明显:在许多情况下,发现的新覆盖率是其十倍以上。结果表明,正是语法和工具的结合才导致性能的显著提高。这种组合使模糊测试器可以自动识别并重组语义上有效的代码片段,从而大大提高性能。

总结见Word

参考资料:

1.git命令行登陆github进行操作
2.博客
3.Fuzz_黑客技术
4.开源地址

你可能感兴趣的:(20199119 2019-2020-2 《网络攻防实践》综合实践)