属性表文件 ( Plist ) 是一种文件形式,通常用于储存用户设置,也可以用于存储捆绑的信息,该功能在旧式的 Mac OS 中是由资源分支提供的。由于 Plist 中存储的数据是抽象的,其采用的文件格式可以不止一种。
macOS 看起来非常有趣,尤其是属性列表 ( plists ) 是存储序列化对象的文件,在苹果操作系统中很常见,类似于微软 Windows 使用注册表存储配置数据。下面显示了 macOS 应用程序自动器的基于 xml 的属性列表示例。此属性列表存储应用程序的版本信息以及其他有用数据如下所示:
二进制 plist 格式
属性列表为模糊测试提供了一个有趣的目标,因为它们很容易被操作系统的许多部分(包括更高特权的进程)创建和使用。苹果公司的开源代码使我能够创建任意的 bplist,并开始模糊文件格式,同时使用内置的 macOS plutil 命令行工具确保正确的语法。
我花了几天时间来生成 bplist 来执行该格式,并很快发现某些对象类型在被常见的 macOS 二进制文件(例如 Finder)以及更高特权的二进制文件(包括 Launch Services 守护程序(LSD))进行解析时会导致异常。系统崩溃日志表明 Core Foundation 框架中存在漏洞,但是,正如我们稍后将看到的,此漏洞存在于多个位置。Core Foundation(简称 CF)是一套 Mac OS 和 iOS 中的 C 语言 API,由较低层的一些例程和封装函数组成。Apple 发布的最大的一个 CF 开源项目叫 CFLite,基于 CFLite 可以开发跨 Mac OS X,Linux 和 Windows 平台的应用。另外一个第三方的开源实现叫做 OpenCFLite 也有同样的功能。大多数的 Core Foundation 例程的对象通常遵循这样的命名规则,例如:CFDictionary 开头的函数中会出现 CFDictionaryRef,这些对象经常被会手动通过 CFRetain 和 CFRelease 来管理引用计数。在内部,Core Foundation 也会把一些基础类型转成 Objective-C 运行时中可用的格式。
大多数生成的崩溃似乎是由 Core Foundation 解析 bplist 并随后尝试使用创建的对象的方式引起的。任何 bplist 的 ObjectTable 中有非字符串类型的对象 ( Date、Data、Bool 等 ) 都会在调用不存在的字符串相关选择器时导致解析过程崩溃。其结果是,任何使用 Core Foundation 读取属性列表的进程都可能因无法识别的选择器异常而崩溃。下面是一个示例易受攻击的代码路径:
可以通过编程方式或通过将修改后的 bplist 放置在系统上自动对其进行分析的方式来为此漏洞生成崩溃,实际上,此漏洞的最初迹象之一是 LSD 在尝试使用修改后的属性列表注册应用程序时反复在我的系统上崩溃。
Objective-See 有一篇很棒的博客文章,详细介绍了通过 LSD 进行的应用程序注册以及属性列表的自动解析。
下图是控制台输出,显示了在我的桌面上以合法的 Info.plist 身份修改后的 bplist 导致崩溃发生的频率,还请注意用户级和系统级进程崩溃。
通过修改单个字节以将 ASCII 字符串对象(类型 0x5X)更改为另一种对象类型,如 DATE(类型 0x33),可以创建恶意的 bplist。修改后的 bplist 示例如下:
漏洞是如何发现的?
由于能够轻松地重新创建崩溃,因此我深入研究了该漏洞的实际发生位置。跟踪此漏洞的一种简单方法是查看崩溃进程的堆栈跟踪。以下是前面显示的测试应用程序的崩溃日志,该日志使用 Core Foundation 读取了恶意属性列表。
我逐步完成了 LLDB 中的 CFStringFind,直到调用 _CFStringGetLength 来检查寄存器之前。从苹果的 _CFStringGetLength 文档中,我们知道第一个参数应该是字符串,因此我们可以使用以下 LLDB 命令检查 RDI 寄存器。
可以看到,第一个参数的对象类型不是字符串,而是恶意 bplist 中的 _NSDate 对象。下面的 _CFStringGetLength 的反编译说明了可能会出错的地方:
我们可以看到,在该函数的第一个参数上调用了长度选择器,我们知道该函数对于 _NSDate 对象将失败,因为它没有此选择器,该理论也与崩溃日志相匹配。
如果我们继续执行这个函数,则会最终将在 Objective-C 异常处理的内部遇到一个异常,这表明我们已找到这些崩溃的根本原因。
其他选择器
继续生成带有非字符串对象的 bplists,并且能够从其他无法识别的选择器在 Core Foundation 中生成其他崩溃。下面的崩溃日志是 LSD 在使用了一个恶意 bplist 和一个剩余的 nscfdata 对象后的崩溃日志:
多个漏洞的发现
在此研究的早期,我使用 plutil 从恶意 bplist 生成崩溃,然后编写了自己的代码以到达必要的代码路径。以下命令通过使用 plutil 作为目标进程和 print plist 标志来设置一个 LLDB 会话,以开始调试此崩溃,该标志只会打印出人类可读版本的属性列表。
经过几次执行之后,很明显 plutil 实际上在其他位置崩溃,而不是在 Core Foundation 中崩溃。下面的输出说明了它试图在 __NSDate 类型上调用长度选择器,该类型会导致无法识别的选择器异常,但是此漏洞存在于 plutil 中,而不存在于 Core Foundation 中。
我们可以通过在 _LSPlistCompactString 上设置断点并使用以下断点命令打印第一个参数来验证这一点:
恶意影响
1. 根级进程可能会在普通用户帐户中崩溃,并且如果操作系统重新生成了根级进程,则它们会反复崩溃(例如,LSD 和 MDS)。
2. 系统不稳定和拒绝服务特别是在 Finder 或其他与 UI 相关的进程消耗了恶意 bplist 并使崩溃进程需要崩溃 0-click 时发生,因为应用程序捆绑包,程序包等在写入磁盘时会自动进行处理。
3. 有可能使普通用户帐户中与安全相关的进程崩溃,从而删除安全边界(XProtect 等),尽管本文中并未对其进行全面探讨。
4. 在自动分析 bplist 的情况下有可能被远程利用。
5. 受此漏洞影响的系统组件包括使用 Core Foundation 解析 bplist 的应用程序,占很大的比例,在 macOS 10.15.3 上快速搜索了 1000 多个已安装的二进制文件,这些文件导入了实现此漏洞的函数。
5. 许多应用程序通过 Core Foundation 解析 bplist 数据,但也会在自己的代码中错误地访问生成的对象,这意味着漏洞发生的概率可能更大。