-
-
[翻译]针对Apple Safari的漏洞发现(评估复杂软件的漏洞利用)
-
发表于: 2018-6-30 16:16 3927
-
原文地址:http://http://blog.ret2.io/2018/06/13/pwn2own-2018-vulnerability-discovery/
漏洞发现是exploit开发生命周期的第一阶段。这个阶段的持续时间是无限制的,因为一个给定目标的搜索空间,代码质量和评估过程可能会大不相同。在手动和自动发现漏洞之间往往是选择先使用谁的问题,难以达到有效的平衡。
作为Pwn2Own 2018博客文章系列的第二部分,我们将讨论研究复杂软件目标(SafariWeb浏览器)的方法,缩小我们的评估范围,选择发现漏洞的策略,并为此开发独特的浏览器模糊测试工具(browser fuzzer)。
(上图是一个基于JS语法的Fuzzzer生成的测试用例片段)
有关此博客系列和我们整个Pwn2Own 2018 exploit-chain的顶级讨论,请查阅第一部分。
面对数百万行代码,挖掘大型软件的漏洞可能会有些困难。为了克服这个难题,建立目标的基础知识和一部分0day发展过程中最不引人注目的部分之一是至关重要的。这是基础侦察101。
软件有什么作用?用户如何与软件之间进行交互?项目如何构建?它的主要组分是什么?过去的漏洞是什么样的?
这一步实际上归结为汇总与目标相关的文档或安全文献,并花些时间对其进行研究。建立链接列表并做笔记,但不要试图理解其所有内容。
(上图表明:实际开发通常是通过从对现有资源的仔细研究开始的)
如果您没有与自己的目标相关的公开的研究或文档,也许查看与目标相关的材料(例如,Google的v8,Mozilla的SpiderMonkey,Microsoft的Chakra等)或设备(针对硬件攻击而言)可能会有帮助。
跳过这一步将意味着您会花费10倍的时间来完成这些资源为您提供的相同研究发现。
在处理中小型目标时,研究所有内容通常是可行的......但是,当研究规模和复杂度更大的代码库时,这会变得越来越困难。
使用从现有文献学习到的知识(recon 101),重要的是尽量缩小具有评估价值的范围,直到剩下的攻击面数目在你的掌控范围之内。
我们使用了同样的方法,将WebKit的评估范围从300万行C / C ++代码限制到不足70k。通过限制广度,我们希望能够在代码库里的一个指定子集上执行有深度、有质量的评估。
(上图是:由SciTools用Understand生成的WebKit源目录的可视化树形图)
对于Pwn2Own 2018,我们选择专注于研究Safari内的JavaScript引擎——JavaScriptCore。这是在分析了现有研究的基础上作出的明智决定。尽管JS引擎中的bug日益罕见,但他们高度的可利用性很难缓解。
选择JavaScriptCore大大缩小研究范围,即使如此,JSC仍有大约350k行C/C++代码。为了帮助我们熟悉代码库,我们选择将代码量进一步缩小到最易于研究的范围。
(上图是:JavaScriptCore源目录的可视化树形图,其中JavaScript运行时以红色突出显示)
组成JS运行时的C / C ++代码(以红色突出显示)几乎直接映射到编写JavaScript时使用的高级对象。运行时实际上是任何JS引擎的表层。
这个高层主要是在/Source/JavaScriptCore/runtime中:
(上图为:JavaScriptCore运行环境的目录,在前两张图片中都呈现为红色)
仅专注于运行时的文件夹,我们缩小到大约70k行代码。这个数字还是非常可观的,缩小的范围形成了更加平易近人的任务。什么是“合理的”取决于你计划使用的资源和发现策略。
选择目标后,是时候选择一种发现漏洞的方法了。
有效的软件(安全)评估有两种不同的方法。完全可以由个人喜好来选择其中之一,否则应当按照优先性和潜在的动机性来选择。
Fuzzing是许多安全爱好者的转战策略,因为它通常成本低且富有成效。Fuzzer可以在很短的时间内覆盖大量的攻击面,但是以人类能够提供的深度和“创造力”为代价的。使用Fuzzer快速查找bug会带来成就感,但也可能是“警告”,因其他人已在别的地方找到了相同内容。这些bug的保质期通常较短。
代码审计对于已经过严格审查的代码来说既枯燥又令人沮丧。但是,对于那些坚持审计的人来说,往往会导致更大的bug,这些bug是大多数Fuzzer以及一些审计人员检测不到的。这些bug往往更持久,并且经常用于贸易。
对于这次研究,我们使用繁重的fuzzing方法是合乎情理的。我们为JSC定位的这个层面是一个很高的目标,因为这些年来它被选择的次数非常多。它是浅薄的,也是WebKit中由Fuzzer或源代码审查者提供的审查次数最高的代码之一。在这个领域发现的bug在当今环境中不可能持续很长时间。
本文的其余部分详细介绍了在构建JS Fuzzer时我们应当虑的方法和注意事项。
经典的Fuzzing技术,如位翻转( bit-flipping),简单输入变异和测试用例剪接,通常不适用于高结构化的上下文输入,如解释型语言(Javascript)。为了Fuzz这些输入,最好的方法是使用语法来综合正确的语法和合理的语义测试用例。
实际上,语法通常是由分析师手写的,作为一套结构化数据的“规则”。举个例子,我们来写一些简单的语法以生成数学表达式:
使用这些'语言'模板,我们可以建立新的高级语法结构(数学表达式):
为了使这个概念具体化,我们要求测试用例生成器使用我们编写的语法规则,并输出任意数量的随机expr:
同样的想法可以推广到更复杂的任务中,例如生成语法正确的JavaScript代码。这可以归结为阅读规范并将其转换为语法定义。
fuzzer的JS测试用例是由Mozilla的dharma通用版本推出的语法提供的,扩展为包含一些简便方法,使我们能够生成更多有联系的代码。
我们为dharma提供了两类JS语法:库和驱动。
库语法要有三个目的:
驱动语法为我们提供了库语法之间的高级支架。通常,库语法将先使用已在库中定义的规则来初始化一组不同的JavaScript变量。在此之后,他们产生了随机化的结构,其中:
集合使我们能够在驱动语法中表达高级“准则” 。下面是一个例子,我们来看看我们为JSArray对象编写的一些集合:
如果我们希望一个数组随机修改自己,我们只能使用众多方法中的一种,可以在我们构建的任何更复杂的语法结构中包含单个语句+JSArray:InPlaceMethod+。没有集合的话,我们只能手动指定每个可能性。
集合中的每个条目都会生成该特定成员函数(或API)的随机选择的签名,可能会在必要时触发生成其他JavaScript构造:
这使我们可以很灵活地编写驱动语法。我们只需提供测试用例生成的粗略样式,然后指定它们之间应该完成的命令。
最后,我们使用一个'mega-driver'来做大部分fuzzing。这个语法会初始化我们编写的每个库语法中的变量,然后对它们进行无序的生成操作:
(上图是:一个无序的“mega-driver”语法生成的测试片段)
最初,这个语法旨在作为我们收集代码的覆盖范围并改进底层库语法的“管理”。只要我们对运行时的功能收集的代码覆盖范围广,稍后,这个计划就会建立更有趣的环境并阐述驱动语法。
这并没有结束,因为我们在构建库语法时发现了一个可利用的问题。
在构建用于更好地生成测试用例的JS语法的同时,我们还开发了另一种在Python中使用的分布式和可伸缩的Fuzzing线束。根据惯例,这个线束的基础架构是使用一个“主”节点和任意数量的“辅助”节点所构建的。
主节点负责:
因此,辅助节点的责任包括:
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!