【技术分享】Pwn2Own专题:Safari沙箱逃逸第二部分

 http://p6.qhimg.com/t01c9b10b5193342cf3.jpg

翻译:myswsun

预估稿费:170RMB

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿


传送门:【Pwn2Own专题】Safari沙箱逃逸第一部分

0x00 前言

之前的文章是关于macOS上的本地提权。还缺少两个部分用于Safari沙箱利用:我们需要一个具有system.volume.internal.mount权限的授权的令牌,同时还要有能力在任意目录创建符号链接。根据CVE-2017-2535 / ZDI-17-356,在苹果安全框架中的一个逻辑问题允许绕过授权的沙箱,并且CVE-2017-2534中,Speech Synthesis服务允许我们在它的上下文执行任意代码。

最终的漏洞利用链不依赖内存问题,且能够提权为root权限。事实上,只有95%是的对的。我们也能使用CVE-2017-6977,是在未沙箱化的用户层服务中的一个不引人注意的空指针引用,在代码中有很多这种情况。它本身不能被利用,但是我们需要它使服务崩溃并重启。


0x01 概述

为了利用CVE-2017-2533,diskarbitrationd的TOCTOU问题,我们需要具有下面的能力,其中一些已经具备:

  • 访问diskarbitrationd的IPC终端

  • 写任意目录

  • 得到mount权限的授权令牌

  • 创建符号链接


0x02 授权令牌和权限

在macOS中的授权令牌使用API AuthorizationCreate创建。它由服务com.apple.authd提供,其管理了活动令牌列表,捕获用户和进程的令牌创建。令牌通过API AuthorizationMakeExternalForm / AuthorizationCreateFromExternalForm在序列化和反序列化时拷贝和共享他们。外部形式只是一个在authd服务中的关联一个令牌的随机的12字节的句柄。有趣的是,在导出一个令牌后,能通过不同的进程再次导入,初始创建的令牌的进程可以退出,而不使得令牌失效。Authd只要有连接的进程引用就简单的保证令牌存活。

一个令牌和一系列权限关联,在文件/System/Library/Security/authorization.plist中有定义,有特定的规则来约束谁能获取他们(例如 “is-admin”,任何管理员用户可以获得这个权限)。可以使用AuthorizationCopyRights来给令牌添加权限。很明显,被赋予权限的令牌能作为证据,调用者被authd允许获得那个权限。这是一些macOS服务和工具(如authopen工具)如何处理授权的。

下面的Shell片段是授权框架如何工作的一个例子。它运行一个小的swift程序来获得令牌并将它导出到文件中。在这个例子中authd将打开一个对话框询问用户权限(“swift项做出改变”)是否允许。其他的权限(尤其是system.volume.internal.mount)不需要用户的交互就能获得,只需要用户在管理员组。然后,authopen再次读取并内化令牌,检查令牌是否得到需要的权限(sys.openfile.readonly./tmp/cantread.txt),然后处理打开并读取文件。注意authorize.swift进程需要一直存活,至少直到authopen再次内化令牌,因此在authd内递增了它的引用计数。

 http://p6.qhimg.com/t01f1ae56e1c56f00d0.png


0x03 在错误的进程中执行权限检查

除了指定使用令牌能够获取哪些权限,关于沙箱化的令牌authd还有其他的限制:进程创建的令牌或者进程想要添加权限的都不能被沙箱化,或者如果他们要,沙箱规则必须包含明确的"authorization-right-obtain"规则:

 http://p1.qhimg.com/t0161f850969dc204fc.png

老版本的authd代码是可获得的,有下面的检查实现:

http://p6.qhimg.com/t0174697c6cbed66a16.png 

我们的场景如下:我们在Safari渲染进程(WebContent)中创建一个令牌,并且将它传给diskarbitrationd。当它尝试获得system.volume.internal.mount权限时,第一个检查(关于令牌的用户,这个例子是diskarbitrationd)将绕过,但是第二个(和它的创建者有关)将失败。

注意,在这两个沙箱检查中,相应的进程是通过PID标识的。然而,正如我们之前所见,创建者进程可以退出。而且在macOS中PID的范围是0-99999且可以重用。这样的沙箱检查能在错误的进程中执行!这就是为什么我们想使得未沙箱化的服务崩溃的原因:如果我们能得到与创建我们的令牌的沙箱化的进程相同的PID,那么两个检查都能绕过,且能添加权限。

巧合的是,这个bug和CVE-2017-7004非常类似,在Pwn2Own之后的几天由谷歌Project Zero团队的Lan Beer报告,并且它能使用相同的方式在IOS中利用。

当然,如果我们短时间创建10万个进程,这个bug将被利用(在Pwn2Own上有5分钟的时间限制)。我们原来的想法是使用空指针或其他bug使得一些系统服务崩溃,并且使用launchd服务重启他们。然而,他似乎有速度的限制,因为在两次崩溃后,需要花10秒才能重启。第二个选择是使用exec(),fork()或者vfork()。他们不是太理想的选择,因为他们不被应用沙箱允许,但是有些例外。


0x04 在speechsynthesisd中执行代码

事实上只有两个服务:

1. 可以从Safari沙箱中得到

2. 有沙箱配置因此可能审查少,但是

3. 需要支持fork和创建符号连接,因此足够实现我们的利用

他们两个是com.apple.fontd和com.apple.speechsynthesisd(实现了Apple’s Speech Synthesis API)。

SpeechSynthesisRegisterModuleURL使用一个用户控制的文件路径并将它作为CFBundle,以便加载一个动态链接库,使用它作为一个语音识别插件。没有签名校验,因此在库再加初始化时能执行任意代码。只有它自身还不是很糟糕,因为这个服务是沙箱化的,但是直到macOS 10.12.4才包含了沙箱:

 http://p5.qhimg.com/t012b2a3dfa719f7d4f.png

记住Safari渲染有目录的读写权限:

 http://p6.qhimg.com/t01974849c099baddf9.png

尤其是,进程能够读写:

 http://p0.qhimg.com/t011e9c75bf9c6df227.png

因此在目录中伪造一个CFBundle是可能的,且在渲染时将它加载到speechsynthesisd中。这里有个bug,因为speechsynthesisd的沙箱限制比渲染少。苹果在正则表达式中确定了这个bug,在macOS 10.12.5更新中修改了:

 http://p8.qhimg.com/t0129ad9e159a693669.png

0x05 总结

此时,我们有了所有的条件来完成沙箱逃逸:

  • CVE-2017-2553:创建符号链接的本地提权,和获得system.volume.internal.mount权限

  • CVE-2017-2535:获得上述权限,fork进程并启动未沙箱化的进程

  • CVE-2017-2534:创建符号链接和使用vfork()

  • CVE-2017-6977:nsurlstoraged(未沙箱化)中空指针引用,触发重启

完整的利用总结如下:

 http://p0.qhimg.com/t0135dc421db129691d.png

沙箱逃逸的完整代码在github中能找到。

(完)