前言
因想了解自动化模糊测试的研究现状,读了些相关综述。这篇《Fuzzing: Challenges and Reflflections》主要描述了当前fuzzing技术存在的问题,指明研究的热点。
Fuzzing简述
Fuzzing指一种自动挖掘漏洞的技术,其可不断产生输入值并报告那些可导致程序崩溃的点。Fuzzing分三种:
- 黑盒fuzzing(如Peach),有两种变体mutational和generational。mutational有一或多个种子输入;generational则按给定的格式生成输入值。
- 灰盒fuzzing(如AFL,LibFuzzer,Honggfuzz),在编译程序时,在一些可控的位置插桩(instrument),并初始化种子。随后生成的输入值能覆盖代码,而覆盖反馈(coverage-feedback)让灰盒能逐渐到达代码深处。
- 白盒fuzzing(如KLEE,SAGE),基于符号执行(symbolic execution)技术(基于可满足性模块理论(SMT)),使用程序分析和约束求解方法来系统地枚举可能的代码路径。Fuzzer计算一个输入值i的路径条件。路径条件被表示为一个SMT公式。给定一个种子输入s,路径条件会被计算并变化(而不是程序的输入值变化)。
挑战
自动化漏洞搜索
- 如何fuzz更多类型的软件系统? 现有的研究更多是针对那些输入数据高度结构化的软件。有待探索的fuzz目标包括:信息物理融合系统(cyber-physical system),它们在运行过程中会和环境进行交互;机器学习系统,它们的行为则由训练数据决定。其他的诸如:协议程序,其对于同一输入数据的输出值可能不同;由多种编程语言编写的软件;GUI程序,其输入为一系列由用户操作界面产生的事件。对于符号执行技术,如果目标程序的输入由某种语法或协议规定,则如何制定约束条件?(?)
- Fuzzer如何识别更多类型的Bug? 当前工作主要只是找到注入点(会崩溃的地方(crash))。需要研究可高效检测其它非crash类型的漏洞。对于侧信道漏洞的研究是当前一个热门话题。需要研究能够自动检测特权提升、远程代码执行及其它重要安全漏洞的新技术,而且不能只检查C/C++程序,还要能检查其它编程语言写的程序。
- 如何探测复杂的、能躲避检测的bug(深层bug)? 有些bug的触发条件非常复杂,而有些检测方法又相当费时费力。结构感知(深层bug) 的fuzz方法,基于语法的(grammar-based) fuzz方法,以及集成了静态分析和符号执行的灰盒fuzz 方法可能是比较好的选择。软件也总是在改变,因此能锁定软件补丁的技术也很重要(techniques that can target software patches will prove essential for finding bugs as they are introduced)(?)需要能像AFLFast那样促进对程序错误的发现的策略,需要研究如何利用GPU等部件的并行化功能来提高执行的效率。
根据bug的重要性对它们进行排序也很重要
。 - 需要更多经验性的研究。为什么有的漏洞在长时间的fuzz中也没法被发现? 要研究这些漏洞无法被检测出的原因;要研究
如何理解源码的生态及安全漏洞在源码中的分布状况
。 - 如何发挥审计员的能力? (?)这里举了一个谷歌安全研究员Ned Williamson的工作方法。Ned会首先作代码审计,以确定存在安全隐患的单元;接着他会运行一会儿fuzzer,并确定fuzzer碰到的障碍,然后亲手为fuzzer排除这些障碍。如果fuzzer花了太多时间在无关代码上,Ned会调整测试驱动(?),并让fuzzer转移测试的重心。一旦找到潜在的漏洞,就将刚刚排除的障碍放回去,然后调整用于暴露漏洞的输入值。这其中就带出这几个问题:如何让审计员与fuzzer之间的交互更高效?审计员如何动态地引导fuzzer?(?)如何让fuzzer“理解”测试中遇到的阻碍?审计员如何指示fuzzer让其克服这些阻碍?
- 如何提高fuzz工具的可用性? 如何让开发人员更易于使用模糊测试?如何更简单地为fuzzer开发测试驱动?(?)如何将模糊测试集成到开发流程中?
如何让fuzzer生成详细的bug报告、甚至提供修复方案?
- 如果fuzz没有什么结果,如何评估潜在的风险? 为了可用性,白盒fuzzer可能牺牲一些正确性和完整性,这对于测试上的可靠性有什么影响?黑盒fuzzer通常无法找到所有漏洞,则如何评估残留的潜在风险?(
如果能将黑盒fuzzing建模,即视为在输入空间上的随机采样,则可能统计数据以评估残留的风险
)灰盒fuzzer使用的程序反馈信息可能引入一些差错,同样也需要在评估残留风险时有可用的统计差错的方法。 - 黑白灰盒fuzzer各有什么理论上的局限性呢? 黑盒和灰盒fuzzer效率(efficiency)高但效果(effectiveness)不太好。此二者生成输入值比较费劲。这就产生了一些问题——
若要在给定时间内找到更多的漏洞,该怎么选择fuzz工具?
程序的大小和复杂性会如何影响fuzzer?若攻击者有更充足的计算资源,则他们的效率如何?
评估和基准
怎样子才算是“合理的耗时”“需重点关照的软件”“重要的bug”?如果没有发现大bug,则怎么评估fuzz的效果?怎么防止过拟合?作为比较的基准模型是怎样的?
Benchmark
- 怎么评估各类fuzzer? Fuzzer测试的程序各种各样,有的以结构化数据为输入,有的以非结构化数据为输入,有的有状态,有的无状态,有的开放源码,有的只有二进制可执行文件,有的从文件中获取输入的内容,有的则是通过API或GUI界面。Fuzzer目的可能不同,有的只是要找到漏洞位置,有的则是要暴露指定类型的bug(比如性能bug)。已有的基准(benchmark)并不能用于这些特定的fuzzer。
- 如何防止过拟合于一个特定的benchmark? 如何防止使用仅由某些人提供的“单一源”类型的benchmark?(?)
fuzzing工具间的竞争
有助于应对以上所提两点问题。一种方法是分出不同的竞争领域,包括基于覆盖的fuzzing、直接fuzzing等,每个领域又可按bug类型和fuzzer适用的软件类型再度细分(TESTCOMP模型)。另一种方法则是通过挑战的形式,由开发工具的人用fuzzer来找出问题程序中潜藏的bug。好处是工具的开发者可以将他们的fuzzer调整至适用于各类任务,但又会增加各任务结果的复现的难度(RODE0DAY)。还有一种方法是持续性测试,即反复用fuzzer来fuzz多个程序。(谷歌的FUZZBENCH)
Fuzzer性能的度量
Fuzzer对某个软件系统的测试效果取决于其有能力找出的漏洞的总数,效率则取决于寻找漏洞的速度。
- 需要研究一些复合(synthetic)bug能不能代表重要漏洞。如果它们不具代表性,则它们与真实漏洞的区别是什么?如何让综合bug更像真bug?(?)
- 已有的由其他fuzzer发现的真实bug是否有代表性?
可以把先前发现的漏洞收集起来加入benchmark中
。新出的fuzzer。可以构建一个大型、共享的的漏洞数据库,收集过去已发现的漏洞。 - 如何预算时间开销(time budget)? 目前,预算时间普遍是1小时到1天。但有限实际很高效的fuzzer可能要费不少时间来生成测试用例。如果预算的时间少了,则一些速度快但实际并不高效的fuzzer反而显得比较高效。
- 为了表明自己提出的方法的优越性,研究人员会对比自己所提方法的实现和其它方法的实现。但是实现的工具是可以人为地微调的(make engineering decisions),这也会影响fuzzer的效率。(如AFL灰盒fuzzer和KLEE白盒fuzzer 的比较,作者说他持保留态度)
作者还调查了业界和学术圈的一些专家,从他们的回复中得到3个最受关注的话题:自动化方法上的改善(71%),建立fuzzing的理论(63%),fuzz性能的度量方法(63%)。
关于fuzz教学,作者推荐了一在线书籍fuzzingbook。