原文:https://blog.ret2.io/2018/06/05/pwn2own-2018-exploit-development/
Pwn2Own是趋势科技旗下的Zero Day Initiative主办的一项行业级年度黑客大赛。Pwn2Own每年都会邀请顶级安全研究人员展示针对高价值软件目标的零日攻击,例如刚推出的Web浏览器、操作系统和虚拟化解决方案。
在今年的大赛上,我们第一次参与其中,选择的目标为macOS平台上的Safari浏览器,之所以选择它们,是因为之前我们尚未跟该软件和平台交过手。
为了完成比赛,我们挖掘并利用了Apple软件中以前不为人知的两个漏洞,最终,只要受害者点击了Safari Web浏览器中的链接,我们就能够以root用户身份远程执行代码。
来自ZDI的Joshua Smith正在评估我们在Pwn2Own 2018上提交的零日漏洞
对于我们来说,参加Pwn2Own大赛,就是以一种非常公开的方式来挑战我们对于这些“令人垂涎三尺”的目标的审计能力。作为参与该赛事的延伸,我们将分享一系列文章,以详细说明我们在突破不熟悉的目标时所使用的系统化方法。
在每篇文章中,我们都会“坦白”一些漏洞利用开发生命周期的关键点:
突破口:Safari
Web浏览器是大多数用户网络冲浪的必备工具。随着网络的飞速发展,浏览器也变得异常复杂。对于这种规模的软件来说,肯定会存在许多安全漏洞,并且其中一些漏洞的危害还将非常严重。
在现代浏览器的漏洞利用的世界中,由于DOM和JavaScript引擎的复杂性首当其冲,自然就会成为攻击者的首要攻击目标。通过研究Safari近期的安全漏洞史,发现它也概莫能外(1,2,3,4,5 ......)。
特别是,在此过程中,Safari的JavaScript引擎,即JavaScriptCore引起了我们的特别关注:
我们知道,Safari浏览器是基于WebKit的,而WebKit是一个开源浏览器引擎
JavaScriptCore(JSC)是一个很有吸引力的目标,因为JS脚本可以在引擎的执行环境中完成错综复杂的控制。此外,从某种程度上说,网站可以通过JS在最终用户的Web浏览器的上下文中执行任意计算。一般说来,JS还可以为静态HTML页面添加动态客户端行为。
对于浏览器供应商而言,很难对这种模式进行约束,除非不惜让当今网络的可用性开倒车。作为攻击者,我们的目标是突破执行环境的各种限制。
JSC:漏洞的发掘和利用
为了在Pwn2Own大赛中获胜,我们最感兴趣的漏洞,就是那些能够相对快速地挖掘出来的漏洞,换句话说,这些漏洞应该具有更短的生命周期。为此,我们构建了一个分布式模糊测试工具,并添加了一些开源项目来增强其火力,最终得到了一个简单的、覆盖率导向的、基于语法的JS fuzzer。
经过大约两周的模糊测试,评估覆盖率,改进JS语法,以及对不那么有趣的崩溃进行分类,我们的fuzzer生成了一个具有迷人的回溯的测试用例:
调用堆栈中的WTFCrashWithSecurityImplication(...),意味着我们可能找到了某些好东西
对这个测试用例的最小化版本进行根源分析后,得到的结论是:我们在array.reverse()和Riptide(JSC的新并发垃圾收集器)之间发现了竞争条件漏洞。
在适当的情况下,对array.reverse()进行恰到时机地调用会生成一个JSArray,其中包含多个已经释放的、分散在各处的对象。如果能够可靠地利用该漏洞的话,就能获得一个独特而强大的原语:对任意JS对象进行任意UAF。
Apple已经修复了该漏洞,并将其编号为CVE-2018-4192:
我们在Pwn2Own 2018大赛中利用的JSC竞争条件的CVE细节
在本系列的第二部分中,我们将详细介绍如何构造用以挖掘安全漏洞的JS fuzzer,第三部分将通过记录重放调试技术对该竞争条件漏洞进行根源分析。
第四部分将为读者提供用于赢得数据竞争的PoC代码,同时,还将介绍如何利用该漏洞在Safari环境中实现远程代码执行(RCE)。
沙箱逃逸:WindowServer
为了更好地保护用户,许多Web浏览器会采用沙箱技术 "沙箱技术"),以将自身与系统的其他部分隔离开来。当攻击者攻陷整个应用程序后,沙箱可用于限制攻击者针对系统的破坏范围。
回顾Safari沙箱的各种文献之后,我们将注意力转向了macOS WindowServer。WindowServer是一个用户空间系统服务,负责绘制和管理macOS的各种图形组件。
在macOS上的ps aux
输出中的WindowServer
从根本上说,WindowServer是通过处理来源于系统中的所有应用程序的mach_messages来运作的。在WindowServer中,大约有600个端点充当这些消息的处理程序。这些处理程序,就是我们在研究沙箱逃逸时的重点关注对象。
WindowServer mach消息处理程序
WindowServer看起来的确像一个理想的目标:它以root身份运行,但位于用户空间(更易于调试/审计),具有大量的攻击面,同时,之前就出现过许多可以利用的安全漏洞(1,2,3,...)。
WindowServer:漏洞的挖掘和利用
WindowServer是一个没有公开说明文档的私有框架。同时,我们很难直接与它打交道,因为它是通过封装各种更高级别的公共图形库构建而成的。如果要审计600多个没有公开文档的端点的话,时间肯定是不允许的,所以,我们换了一个方向——构建一个简单的fuzzer。
在WindowServer中,我们发现了三个不同的调度例程,其中所有传入的mach_messages必须经由它们才能传送到相应的显式处理函数。
使用中的fuzzer快照
在Frida的帮助下,我们hooked了这些调度例程,这样就可以在消息通过WindowServer时进行相应的检查、记录、比特翻转和重放操作了。通过对正常应用程序生成的消息进行比特翻转,只需对底层子系统有初步的理解,我们就能快速检测(例如,模糊测试)各种攻击面。
在为Pwn2Own购买的MacBook上,我们的fuzzer运行了不到24小时就找到了一个导致系统崩溃的越界读漏洞。更重要的是,崩溃时的callstack只有一个来自WindowServer端点的调用。找到这样一个“浅层”崩溃是非常理想的,因为这意味着,我们可以相对容易地触发和控制该漏洞。
通过重复比特翻转操作,我们能够可靠地重现该崩溃过程,进而对其进行根源分析:
WindowServer中的有符号整数比较漏洞
崩溃可归因于经典的有符号整数比较问题。
函数 _CGXRegisterForKey(...) 使用了受攻击者控制的数组索引,它正常的取值范围是从0到6的整数。但是,该检查是通过有符号整数操作实现的。如果传入负索引(例如,-10000),攻击者就能成功绕过该检查,并实现数组索引的越界。
这个漏洞已经得到了修复,其编号为CVE-2018-4193:
我们在Pwn2Own 2018中使用的WindowServer有符号整数比较漏洞的CVE细节
有趣的是,我们与Pwn2Own的竞争对手Richard Zhu发生了“撞车”。最终,Richard和我们团队都未能在Pwn2Own的三次尝试中独立搞定这个漏洞。主要是因为某些限制和一些非常不幸的巧合使得这个漏洞很难被利用。
在本系列的第六篇也是最后一篇文章中,我们将为读者阐释CVE-2018-4193的复杂性,同时,还会说明围绕这个漏洞实现沙箱逃逸的代码的复杂程度。
结束语
Pwn2Own大会是广大公众可以一窥zero-day漏洞的发现和利用过程的、为数不多的机会之一。通过这一系列文章,我们希望能够通过昂贵的安全漏洞和纯粹的奉献精神为大家揭开这一神秘过程的面纱。
下周,我们将进一步扩展我们的JSC模糊测试工作,并通过高级的调试技术以及其他技术,在复杂的竞争条件下完成相关的根源分析。