看看iOS 14中的iMessage

2021-01-29 14:56:48

12月20日,Citizenlab发布了“ The Great iPwn”,详细介绍了“新闻记者如何被NSO怀疑的iMessage组“零点击”利用所黑客入侵。需要特别注意的是以下注意事项:“我们认为[漏洞利用]不适用于iOS 14及更高版本,其中包括新的安全保护。鉴于距我们发布远程iPhone漏洞博客博客系列已经差不多一年了,在该系列文章中,我们描述了iMessage 0-click漏洞如何在实践中发挥作用,并就如何防止类似攻击提供了一些建议在将来,现在似乎是个绝佳时机,更详细地研究iOS 14中的安全性改进,并探索Apple如何针对0次点击攻击强化其平台。

该博客文章的内容是大约一个星期的反向工程项目的结果,该项目主要在运行macOS 11.1的M1 Mac Mini上执行,其结果在可能的情况下也经过验证,也适用于在iPhone XS上运行的iOS 14.3。 。由于该项目的性质和时间限制,我可能错过了一些相关更改或在解释某些结果时犯了错误。在可能的情况下,我尝试描述了验证所显示结果所必需的步骤,并希望您进行任何更正或补充。

博客文章将首先概述Apple在iOS 14中实施的,影响iMessage安全性的重大变化。之后,对于大多数对技术细节感兴趣的读者,将详细介绍每项主要改进,同时还提供了反向工程的演练。至少就技术细节而言,建议简要回顾一下去年的博客文章系列,以基本了解iMessage及其用于攻击它的开发技术。

内存损坏漏洞,无需用户交互即可到达,理想情况下无需触发任何用户通知即可到达

(可能)一种突破任何沙箱的方法,通常是利用另一个操作系统组件(例如,用户空间服务或内核)中的单独漏洞

在iOS 14上,Apple进行了iMessage处理的重大重构,并使攻击的所有四个部分都更加困难。这主要是由于三个主要变化:

iOS 14的主要变化之一是引入了一个新的,紧密沙盒化的“ BlastDoor”服务,该服务现在负责iMessages中几乎所有的不可信数据解析(例如NSKeyedArchiver负载)。此外,此服务使用Swift(一种(主要)内存安全的语言)编写,这使得将经典内存损坏漏洞引入代码库的难度大大提高。

下图显示了粗略的新iMessage处理管道,各个服务进程的名称显示在每个框的顶部。

可以看出,大多数复杂,不受信任的数据处理已移至新的BlastDoor服务中。此外,此设计及其7种以上涉及的服务允许应用细粒度的沙盒规则,例如,仅需IMTransferAgent和apsd进程即可执行网络操作。因此,该管道中的所有服务现在都已正确地沙盒化(可以说BlastDoor服务被最强大的沙盒化)。

从历史上看,Apple平台上的ASLR具有一个体系结构上的弱点:共享缓存区域(在一个预链接的blob中包含大多数系统库)仅在每次引导时被随机分配,因此在所有进程中都将保持相同的地址。事实证明,这在0单击攻击的情况下尤为重要,因为它允许攻击者能够远程观察进程崩溃(例如,通过自动发送收据的时间),推断共享缓存的基址等。打破ASLR,这是后续利用步骤的先决条件。

但是,在iOS 14上,苹果添加了专门用于检测这种攻击的逻辑,在这种情况下,下次启动时针对目标服务将共享缓存重新随机化,从而使该技术无用。根据具体的漏洞,这将使在0单击攻击上下文中绕过ASLR变得更加困难甚至不可能(除了蛮力外)。

为了限制攻击者重试漏洞利用或蛮力ASLR的能力,BlastDoor和Imagent服务现在受新引入的指数限制机制的约束,该机制由launchd强制执行,从而导致崩溃后重新启动之间的间隔加倍,每次后续崩溃均会增加一倍。最多20分钟)。通过此更改,依赖于反复崩溃被攻击服务的漏洞利用现在可能需要几个小时到大约半天才能完成,而不是几分钟。

现在,本博客文章的其余部分将更深入地探讨这三个更改。

可以通过跟踪传入的iMessage的流程来研究新的BlastDoor服务及其在iMessage的处理中的作用。在线上,一个简单的文本iMessage看起来像这样,编码为二进制plist:

以前,所有这些工作都是在imagent中发生的。但是,在iOS 14中,所有功能都转移到了新的BlastDoor服务中。尽管主要处理流程仍从imagent开始,但imagent从-[IMDiMessageIDSDelegate service:account:incomingTopLevelMessage:fromID:messageContext:]中的identityservicesd(IDS框架的一部分)接收原始但未加密的有效载荷字节,然后立即或多或少立即发送消息通过+ [IMBlastdoor sendDictionary:withCompletionBlock:]转发到BlastDoor服务,该服务创建了回复处理程序块,然后调用-[IMMessagesBlastDoorInterfacediffuseTopLevelDictionary:resultHandler:]。那时,处理过程以Swift代码结束,该代码反序列化二进制有效负载,然后通过XPC将其发送到BlastDoor服务。

在BlastDoor内部,工作大部分发生在BlastDoor.framework和MessagesBlastDoorService中。由于大多数代码都是用Swift编写的,因此对它进行静态反向工程是相当不愉快的(没有符号,许多虚拟调用,遍布各处的swift运行时代码),但是幸运的是,这实际上并不是必需的博客文章。但是,值得注意的是,尽管高级控制流逻辑是用Swift编写的,但某些解析步骤仍涉及现有的ObjectiveC或C实现。例如,XML由libxml解析,而NSKeyedArchiver有效负载由NSKeyedUnarchiver的ObjectiveC实现解析。

可以通过中断imagent中的回复处理程序函数来查看BlastDoor的响应(该函数可以在+ [IMBlastdoor sendDictionary:withCompletionBlock:]中找到,或者通过搜索字符串“ Blastdoor响应%p收到XREFs(命令:%hhu ,guid:%@)”)。下面显示了一个简单文本消息的典型BlastDoor响应:

可以将这种数据结构中的每个字段大致与在线iMessage格式的一部分相关联。例如,plainTextBody字段包含t字段的内容,而content字段对应于x字段的内容。

除了简单的文本消息外,iMessages还可以包含附件(基本上是任意文件,这些文件已加密并临时上传到iCloud)以及相当复杂的序列化NSKeyedArchiver归档文件,这些文件过去是bug的来源。

例如,请考虑当用户通过iMe​​ssage将链接发送到网站时会发生什么。在这种情况下,发送设备将首先呈现网页的预览并收集有关该网页的一些元数据(例如标题和页面说明),然后将这些字段打包到NSKeyedArchiver档案中。然后,此存档使用临时密钥加密,并上​​传到iCloud服务器。最后,链接和解密密钥作为iMessage的一部分发送到接收器。为了创建有关传入iMessage的有用的用户通知,接收者必须在0单击代码路径上处理此数据。由于这又涉及相当多的复杂性,因此也需要在BlastDoor内部完成:从上方收到BlastDoor答复并意识到消息包含附件后,imagent首先指示IMTransferAgent下载和解密iCloud附件。之后,它将调用-[IMTranscodeController解码器iMessageAppPayload:bundleID:completionBlock:blockUntilReply:],然后将相关数据转发到IMTranscoderAgent,然后该代理进入+ [IMAttachmentBlastdoor sendBalloonPluginPayloadData:withBundleIndifier:lastpleallDollingDolledDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollingDollantDoll :withIdentifier:resultHandler:]。

在BlastDoor服务中,然后再次在Swift中执行插件数据解码,并分配给相应的插件类型,具体取决于插件ID。对于RichLinks(插件ID com.apple.messages.URLBalloonProvider),处理结束于LinkPresentation.MessagesPayload.init(dataRepresentation :),该过程反序列化NSKeyedArchiver有效负载并从中提取预览图像和URL元数据以生成预览。信息。

沙箱配置文件可以在System / Library / Sandbox / Profiles / blastdoor.sb中找到,并且在iOS和macOS上看起来是相同的。可以进行静态研究,为此可以在此处找到它,也可以动态地进行研究,例如使用sandbox-exec工具:

只能访问少数本地IPC服务,即Diagnosticd,logd,opendirectoryd,syslogd和notifyd。

此外,该配置文件利用系统调用过滤来限制与核心内核的交互。但是,到目前为止,系统调用筛选器似乎处于“允许”模式:

这样,仍然允许BlastDoor服务执行任何系统调用,但是可以预期,系统调用筛选将很快进入“强制模式”,这将进一步提高其有效性。

新处理管道的一个有趣的副作用是imagent现在可以检测传入消息何时导致BlastDoor崩溃(它将收到XPC错误)。更有趣的是,imagent似乎正在向Apple服务器通知此类事件,这可以通过在apsd中的-[APSConnectionServer handleSendOutgoingMessage:]上设置一个断点来看出,该守护程序负责实现Apple的push服务(在其上是iMessage建成)。显示外发消息将显示以下内容:

可以看出,imagent显然在通知iMessage服务器,其UUID为0x3a4912626c9645f98cb26c7c2d439520(fU键)的消息已导致BlastDoor崩溃。

尚不清楚访问服务器代码的目的是什么。尽管这些通知仅可用于统计目的,但它们也将为苹果提供有关iMessage涉及蛮力攻击的明确信号,而对于任何针对BlastDoor服务的漏洞利用失败的信号则较弱。

在我的实验中,观察到这些崩溃通知之一后,服务器将开始直接将发送收据发送给发送方,以接收尚未由接收方实际处理过的消息。可能这是另一种独立的工作,通过混淆发送方来打破崩溃Oracle技术,但是如果不访问服务器上运行的代码,很难验证这一点。在任何情况下,值得注意的是,服务器对交付收据进行“欺骗”通常是可能的,因为消息UUID(或多或少是交付收据的唯一内容)是非end2end加密有效负载的一部分,并且因此对服务器是已知的(断开-[APSConnectionServer handleSendOutgoingMessage:]并检查传出的iMessage以验证这一点,UUID将在U密钥中,而e2e加密的数据将在P密钥中)。这很可能是必需的,因此服务器可以跟踪哪些消息已经被传递,以及将来仍需要保留哪些消息。

以前,在利用iMessage内存损坏错误时,可以使用“崩溃预言”来显示共享缓存区域在内存中的位置:攻击者将以导致访问内存位置的方式触发内存损坏错误。区域0x180000000-0x280000000(可以映射共享缓存)中的某个位置。如果内存有效,则不会发生崩溃,然后将通过成像将发送收据发送给攻击者。但是,如果发生崩溃,则不会发送此类收据,通知攻击者该地址未映射。通过巧妙地选择查询的地址,共享缓存的位置可以以对数时间显示,只有大约20条消息。

但是,在iOS 14上,Apple添加了一种机制,可以对“攻击”过程重新分配共享缓存区域的位置,从而打破了对该技术的基本假设,并使其无效。这非常重要,因为崩溃oracle技术可能是可用于0单击iMessage攻击的极少数(即使不是唯一的)相当通用的ASLR旁路技术之一。

要了解共享缓存重新滑动的工作方式,可以先查看内核。在iOS 14中,内核现在可以具有两个活动的共享缓存区域:``常规''区域和``隐藏''区域。在攻击过程中,将发生以下情况:

当攻击者尝试使用基于崩溃的Oracle的技术时,被攻击的进程将很快最终访问范围为0x180000000-0x280000000(映射共享缓存)的未映射内存,并崩溃

内核处理由CPU产生的分段错误,并在崩溃信息中设置一个特定的标志,以表明崩溃发生在共享缓存区域内

同时,内核会将当前活动的重新滑动的共享缓存区域(如果存在)标记为陈旧,从而导致重新创建它,并在下次使用它时对其进行随机化。

启动(作为崩溃服务的父进程)接收崩溃信息,注意到OS_REASON_FLAG_SHAREDREGION_FAULT标志,并设置与崩溃进程相关的服务的ReslideSharedCache属性(参见`launchctl procinfo $ pid`并搜索`reslide shared cache = 1`)

下次重新启动服务时,启动该服务,然后由于ReslideSharedCache属性而添加posix_spawn的POSIX_SPAWN_RESLIDE属性

在内核中,此标志现在使新创建的进程获得重新滑动的共享缓存映像。但是,由于当前不存在活动的隐藏区域(在步骤3中将前一个区域标记为陈旧),因此将在新的随机地址处创建一个新区域。

其结果是,每当攻击者尝试使用崩溃Oracle破坏ASLR时,被攻击的服务每次启动时都会收到不同的共享缓存区域,从而阻止了攻击的成功。目前,尽管该功能似乎仅在iOS上有效,但也有望在macOS上使用。

虽然此机制原则上也可以保护第三方应用程序免受类似攻击,但目前对这些应用程序的保护程度较弱,这可能是为了首先评估此更改对现实世界的性能影响(共享缓存是操作系统的重要性能优化) )。特别是,当前的第3步仅在崩溃过程是平台二进制文件(基本上是操作系统附带的二进制文件并由Apple直接签名)(例如处理iMessages的服务)时执行。但是,对于第三方进程,只有在全局vm_shared_region_reslide_restrict设置为零的情况下才会发生:

由vm_shared_region_reslide_restrict bootarg控制。当前似乎设置为1。本质上,对于第三方应用程序,这意味着:

当被攻击的进程首次崩溃时,内核仍将设置OS_REASON_FLAG_SHAREDREGION_FAULT标志,并且启动后将添加ReslideSharedCache属性,但当前的滑动区域不会无效

重新启动的服务然后重新启动,现在使用“隐藏”的共享缓存区域

当服务下次崩溃时,并且如果该服务是当前唯一使用重新组合的共享缓存区域的服务(通常应该是这种情况,但是可能受到攻击者的影响),则该区域的refcount会降为零,并且共享缓存区域标记为要删除。

但是,删除实际上只会在两分钟后发生。这样,如果服务在两分钟内重新启动,它将在内存中的相同位置接收相同的共享缓存区域。

结果,如果第三方应用程序自动将某种形式的交付收据发送给发件人并在崩溃后足够快地重新启动,则它仍可能通过崩溃预兆技术受到攻击。但是,例如可以通过为这些服务启用ExponentialThrottling来防止这种情况。理想情况下,假设性能损失合理,Apple将在将来为所有应用程序重新随机化。

我们在2019年建议的另一件事是限制攻击者尝试利用漏洞的尝试次数。这对于抵御崩溃预言技术最为重要,但也有助于防止暴力攻击(例如,如果进行了足够的尝试,则可以简单地将共享缓存区域的位置暴力破解)。推出的新ExponentialThrottling功能似乎可以实现这一目标。

要使用它,系统守护程序或代理必须通过在其Info.plist(主要是服务元数据)中设置``" _ExponentialThrottling = 1”来选择加入,如下面的BlastDoor服务所示:

但似乎没有用于其他任何服务,例如可以通过查看launchctl dumpstate命令的输出来看到,该命令对于com.apple.imagent和com仅显示“ exponential throttling = 1”。 .apple.MessagesBlastDoorService。

据推测,_ExponentialThrottling属性指令已启动(macOS和iOS初始化过程),以延迟崩溃服务的后续重启。虽然由于缺少源代码或二进制符号而使静态逆向工程启动具有一定的挑战性,但幸运的是,通过实验确定_ExponentialThrottling属性的影响相当容易,例如通过安装将当前时间戳记写为文件崩溃。默认情况下,因此如果没有ExponentialThrottling,则会看到以下内容:

可以看出,默认情况下,服务最早是在先前启动后十秒重新启动的。但是,使用以下服务plist启用ExponentialThrottling:

在此,可以清楚地看到后续重新启动之间的时间呈指数增长,并且上升到最多20分钟。实际上,启动的函数中可能包含以下代码,该函数可能负责计算下一个重启延迟(搜索字符串"%s:服务受%llu seconds"限制的XREF):

进行此更改后,依靠暴力破解的漏洞现在每20分钟就会尝试一次,而不是每10秒一次。

尽管存在指针身份验证(PAC),但在iOS 12.4上针对iMessage的PoC攻击在很大程度上依赖于伪造ObjectiveC对象来获得某种形式的任意代码执行。这主要是因为ISA字段,

......