macOS看起来非常有趣,尤其是属性列表(plists)是存储序列化对象的文件,在苹果操作系统中很常见,类似于微软Windows使用注册表存储配置数据。下面显示了macOS应用程序自动器的基于xml的属性列表示例。此属性列表存储应用程序的版本信息以及其他有用数据如下所示:
属性列表也可以采用二进制形式,否则称为bplist。顾名思义,这些是二进制格式的属性列表,这些属性列表启用了一些附加的对象类型和关系,包括字典。以下是一个示例bplist,尽管可以使用其他标签来支持该格式的其他版本,但是可以通过bplist00标记来标识该示例:
通过查看苹果的开源代码,可以更好地理解bplist格式,我也发现这个参考非常有用。苹果开源代码中定义的bplist格式如下:
二进制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读取属性列表的进程都可能因无法识别的选择器异常而崩溃。下面是一个示例易受攻击的代码路径:
使用以下C代码以及与测试应用程序位于同一目录中的名为Info.plist的恶意属性列表,可以轻松到达此位置:
崩溃分析
可以通过编程方式或通过将修改后的bplist放置在系统上自动对其进行分析的方式来为此漏洞生成崩溃,实际上,此漏洞的最初迹象之一是LSD在尝试使用修改后的属性列表注册应用程序时反复在我的系统上崩溃。
Objective-See有一篇很棒的博客文章,详细介绍了通过LSD进行的应用程序注册以及属性列表的自动解析。
下图是控制台输出,显示了在我的桌面上以合法的Info.plist身份修改后的bplist导致崩溃发生的频率,还请注意用户级和系统级进程崩溃。
LSD崩溃
通过修改单个字节以将ASCII字符串对象(类型0x5X)更改为另一种对象类型,如DATE(类型0x33),可以创建恶意的bplist。修改后的bplist示例如下:
这个很小的字节更改现在可以用于在macOS系统和iOS上造成严重破坏,尽管在此研究中未对该平台进行过测试。这种方法还会影响包括Spotlight在内的多个数据库,这些数据库已被该恶意Info.plist攻击,甚至在重新启动后也反复导致崩溃。
漏洞是如何发现的?
由于能够轻松地重新创建崩溃,因此我深入研究了该漏洞的实际发生位置。跟踪此漏洞的一种简单方法是查看崩溃进程的堆栈跟踪。以下是前面显示的测试应用程序的崩溃日志,该日志使用Core Foundation读取了恶意属性列表。
阅读这篇文章以了解Core Foundation如何处理无法识别的选择器异常并阐明堆栈跟踪中的_CF_forwarding_prep_0会很有帮助。有了这些信息,我将之前的返回地址视为CFStringFind中的异常的可能来源,尤其是在调用_CFStringGetLength之后。下面的反汇编说明了此调用:
CFString查找反汇编
我逐步完成了LLDB中的CFStringFind,直到调用_CFStringGetLength来检查寄存器之前。从苹果的_CFStringGetLength文档中,我们知道第一个参数应该是字符串,因此我们可以使用以下LLDB命令检查RDI寄存器。
可以看到,第一个参数的对象类型不是字符串,而是恶意bplist中的_NSDate对象。下面的_CFStringGetLength的反编译说明了可能会出错的地方:
_CFStringGetLength反编译
我们可以看到,在该函数的第一个参数上调用了长度选择器,我们知道该函数对于_NSDate对象将失败,因为它没有此选择器,该理论也与崩溃日志相匹配。
如果我们继续执行这个函数,则会最终将在Objective-C异常处理的内部遇到一个异常,这表明我们已找到这些崩溃的根本原因。
其他选择器
继续生成带有非字符串对象的bplists,并且能够从其他无法识别的选择器在Core Foundation中生成其他崩溃。下面的崩溃日志是LSD在使用了一个恶意bplist和一个剩余的nscfdata对象后的崩溃日志:
此LSD崩溃的堆栈跟踪如下:
注意,在Objective-C异常处理之前,崩溃的位置不是来自CFStringFind,而是实际上调用_CFStringGetCStringPtrInternal的CFStringFindWithOptionsAndLocale,最终由于调用错误的选择器_fastCStringContents而崩溃。这样做的原因是,剩余的nscfdata类型实际上有一个长度选择器,所以它成功地通过了我们之前看到的第一个崩溃位置,并进一步进入Core Foundation,直到它调用另一个未识别的选择器。
多个漏洞的发现
在此研究的早期,我使用plutil从恶意bplist生成崩溃,然后编写了自己的代码以到达必要的代码路径。以下命令通过使用plutil作为目标进程和print plist标志来设置一个LLDB会话,以开始调试此崩溃,该标志只会打印出人类可读版本的属性列表。
经过几次执行之后,很明显plutil实际上在其他位置崩溃,而不是在Core Foundation中崩溃。下面的输出说明了它试图在__NSDate类型上调用长度选择器,该类型会导致无法识别的选择器异常,但是此漏洞存在于plutil中,而不存在于Core Foundation中。
似乎在许多macOS应用程序中存在类似的漏洞,这些漏洞假定bplists仅包含字符串对象类型。 LSD进程崩溃的堆栈跟踪如下所示:
如果我们使用GHIDRA来反汇编_LSPlistCompactString函数,则可以看到偏移量45或0x2D导致我们又对漏洞对象类型进行了另一个长度调用,大概是从我们现在在LSD数据库中的恶意bplist调用的:
我们可以通过在_LSPlistCompactString上设置断点并使用以下断点命令打印第一个参数来验证这一点:
下面的输出说明LSD正在从恶意bplist获取__NSDate对象:
这证实了我最初认为的一个漏洞实际上是多个macOS二进制文件中的许多漏洞,并且所有漏洞均源于bplist仅包含字符串对象的假设。
恶意影响
- 根级进程可能会在普通用户帐户中崩溃,并且如果操作系统重新生成了根级进程,则它们会反复崩溃(例如,LSD和MDS)。
- 系统不稳定和拒绝服务特别是在Finder或其他与UI相关的进程消耗了恶意bplist并使崩溃进程需要崩溃0-click时发生,因为应用程序捆绑包,程序包等在写入磁盘时会自动进行处理。
- 有可能使普通用户帐户中与安全相关的进程崩溃,从而删除安全边界(XProtect等),尽管本文中并未对其进行全面探讨。
- 在自动分析bplist的情况下有可能被远程利用。
- 受此漏洞影响的系统组件包括使用Core Foundation解析bplist的应用程序,占很大的比例,在macOS 10.15.3上快速搜索了1000多个已安装的二进制文件,这些文件导入了实现此漏洞的函数。
- 许多应用程序通过Core Foundation解析bplist数据,但也会在自己的代码中错误地访问生成的对象,这意味着漏洞发生的概率可能更大。
本文翻译自:https://objective-see.com/blog/blog_0x5A.html
【责任编辑:赵宁宁 TEL:(010)68476606】