Fuzzing: Challenges and Reflections

0x00 自动化

  1. 需要更多的fuzz工具,针对不同类型的对象,the questions:

    • 如何fuzzing有状态交互的对象?e.g. 协议
    • 如何fuzzing用同时使用多种语言编写的程序?
    • 如何fuzzing图形化程序?(输入可能是用户接口的事件队列)
    • 如何将符号执行应用到输入是高度结构化的对象上?(通常来说符号执行约束对象是数值或者字符串序列)
  2. 如何去发现更多类型的bug?

    • 不应该局限于crash带来的影响,需要更多不同的类型类似AddressSanitizer的东西,在合适的时间和合适的位置,添加合适的assertion。
    • bug的类型通常比较局限于fuzzing对象所实现的语言,大部分可能集中在c/c++,我们更应该关注其他语言可能带来的特性,从更上层的观念来看bug的形成,而不是又把它们翻译成c/c++。
  3. 如何去发现更深层次的bug?(这样的bugs通常满足复杂的触发条件),下面是一些有趣的解决方案。

    • 基于结构感知和语法模版的fuzzer
    • 基于静态分析和符号执行的灰盒fuzzer
    • 基于静态patch的fuzzer
    • 更有效的错误发现策略
    • 基于bugs优先级的策略(种子优先级?)
  4. 需要更多的对bug的认知经验

  • 经过长时间的fuzzing,为什么这些bugs没有检测出来?究竟是什么让它们无法触发?需要自身去实际分析这些bugs的本质,并将它们区分出来。
  1. fuzzer从来都不是一个黑盒子!

    • 许多人认为我们只需要人为对fuzzing初始阶段做一些准备,然后安静的等待结果,把fuzzer想象成一个黑盒子,但是实际上它并不是一个这样的黑盒子,更重要的是我们应该能在某个时刻去干预它,让它在我们预期的道路上,越走越远!
    • 如何让fuzzer和安全人员之间以一种更有效的方式交互呢?
    • 如何让安全人员在fuzzer运行过程中动态的去干预它呢?
    • 如何让fuzzer以更直观方式告诉安全人员,它们遇到的一些难以继续进行的阻力?或者说安全人员如何帮助fuzzer去解决这些阻力?
  2. 如何增强fuzzer的易用性?

    • 各种版本的fuzzer层出不穷,在尝试使用这些fuzzer的时候,我们需要对它有一个完整的认识,这个过程是有学习成本的,如何快速的认知到底这个fuzzer适不适合我去花时间和成本去了解它的内在原理?(减少学习成本)

    • 如何让开发者和软件工程师去更便捷的使用它?(面向人群可能不仅仅是安全人员)

    • 如何快速的利用一些test driver去尝试体验fuzzer?

    • 尝试把fuzzer放到CI或者IDE?

    • 如何打印出更详细的bug reports,让使用者能快速的知道哪里出问题了,出现的是什么类型的bug?

0x01 Fuzzing 论

It is important for any discipline to stand on a firm scientific foundation

有许多前沿的fuzzing技术,但是如何证明某种方法就一定比其他的方法要好呢?这些方法的局限性是什么?我们需要去解释这些方法所产生的结果,并能从结果中做出推断。为了实现这些,我们迫切需要有一个完整针对fuzzing的理论模型。

  1. 当fuzzing过程失败的时候,如何去评估剩余的威胁等级? (失败可以理解为fuzzing结束没有得到相关的bugs结果)

    如果从黑盒测试和白盒测试两个角度来看这个问题,当你使用的是黑盒测试,如果这个时候没有bugs report产生,相对于白盒测试来说,说明剩余威胁等级低的可信度一定是比较小的。进一步我们想象在最理想的环境下,在白盒的基础上用符号执行,把所有路径都能枚举到,并且在每一条路径上都添加assertion,那么它的结果就一定可以作为我们评估的标记,可实际上并不存在。(如果有这样一个god存在,它不需要告诉我哪里有漏洞,只需要告诉我有多少个,那该多好啊!)

    如果把不同类型fuzzer说明问题的能力,用一条光谱来表示,那么黑盒测试和白盒测试一定是位于这条光谱的两端。前面说了一下如何通过白盒方法去理想的验证剩余威胁度,当然黑盒情况下,我们也能通过一些方法说明问题,把黑盒测试想象成对程序输入空间的一个随机抽样,通过统计学的方法建立威胁评估。

    同样介于黑盒测试和白盒测试之间,还存在灰盒测试,灰盒测试通过程序反馈来构建比黑盒更有效的输入,但是如何通过程序反馈来区别于黑盒测试的评估过程?(我想程序反馈的引入,相对于提供了一种映射存在,映射的存在直接导致是等价关系的产生,使我们能缩小程序的输入空间,这种映射是很自然的满射,如果用代数的思想去看代它,类似同态,但是这个特殊的代数结构它的二元关系怎么定义呢?即输入和输入如何建立运算呢?非常值得探索!很多fuzzer输入和输入之间都是独立的!)。

    说了这么多,其实最后我们希望能有一个完整的理论框架来帮助我们去回答这样的种种问题,让其中的方法更加的general。

  2. 黑盒fuzzing,灰盒fuzzing,白盒fuzzing它们的理论限制是什么呢?

    在一般情况下,fuzzing的速度上黑盒 > 灰盒 > 白盒,但是在效果上 白盒 > 灰盒 > 黑盒。这将会导致一些问题的解决方式:

    1. 如果给定时间限制,我们应该如何选择不同的fuzzing技术?或者说通过组合不同的技术? 目的是让其在指定的时间内,找到更多的漏洞!
    2. 被测程序的文件大小和复杂度是否会影响不同fuzzing技术的适应性和性能?
    3. 如果拥有的计算资源提升,对fuzzing效率是一种怎样的增长?

    通过理解不同现存fuzzing的理论限制,我们就能去尝试回答上面的一些问题,并且开发出更有效的fuzzing技术!

0x02 评估和基准测试

当一个新的fuzzing技术横空出世的时候,我们需要用完整的方法去评估它。一般来说,我们常常关注它是否能在合理的时间中找到更多有趣的bugs (the better fuzzer)。 那么问题就来了,“合理的时间”,“有趣的bugs”,如何去用更本质方法的描述呢?如何防止过度拟合呢? 合适的基线对照组应该是什么呢? 如何比较它们的effectiveness和efficency

  1. 如何评估一些特殊的fuzzers?

    现存的基准测试方法可能无法覆盖所有的不同类型的被测系统,在这种没有情况下,我们就代表研究者去选择合适的程序和基线对照组。

  2. 如何防止benchmark过度拟合?

    将不同类型的fuzzer的benchmark分类,让所有的研究者来维护它,种做法主要是为了防止单一的组织对benchmark的过度控制(透明且多样)。其实1,2两个问题都可以归纳到如何解决Fuzzing工具相互竞争?其中一种方法是将不同类型的fuzzer分类,同一范畴下相互比较,例如基于覆盖率的fuzzing,指向型fuzzing等等。未来也许可以进一步在范畴下根据检测bugs类型和程序类型再划分。另一种方法是让fuzzer去处理一些challenge problems,其中里面是藏着一些经过精心设计的bugs,fuzzer的设计者只需要负责将自己的工具调整到最佳状态。

  3. 人造的bugs是否说明问题?

    synthetic =?real world

  4. fuzzer 发现的real world bugs是否之前已经被发现了?是否有代表性?

    建立被fuzzer发现的漏洞数据库

  5. 覆盖率是否真的是一个用于测量fuzzer effectiveness好的度量?

    覆盖率的增长对发现漏洞可能性增长 是怎样一种关系?

  6. 什么是合理的时间预算?

    时间的增加是否影响漏洞被发现的可能性?相同时间下,发现的漏洞数量,是一种测量effetiveness的方法,但是这个时间应该如何去做限制呢?

  7. How do we evaluate techniques instead of implementations?

自己的观点

最大的感触就是“Human in the loop”,我把它翻译成了Fuzzing从来都不是一个黑盒子,这一点我自己深有感触,在做实际做东西当产品用的时候,却偏偏让你做成一个黑盒子,这一类的安全产品实际上并不是给完全不会安全的人用的,这一点要特别明确。但是让安全产品快速的让普通的程序员,就马上使用,在未来依然是一个很大的挑战。关于evaluate的东西我记录的很少,因为我总觉得没有一个规范的东西在那里,也许小领域也就这样,没有完整而坚实的理论基础,Fuzzing未来的路真的还有很远要走。