苹果OTA更新的噩梦:看看攻击者如何绕过签名验证并篡改内核
2023-10-30 14:36:28 Author: www.4hou.com(查看原文) 阅读量:10 收藏

苹果的OTA更新

在大多数情况下,macOS更新是通过OTA更新过程完成的。

OTA是over-the-air的缩写。在“系统设置”中,我们可以通过点击“立即更新”按钮直接更新系统。

1.png

OTA更新是一种增量更新,因此比完整的操作系统升级更快,容量更小。

它用于小版本更新,通常每两个月更新一次。但是,如果苹果公司认为内核中存在紧急漏洞,并且已经被积极利用,并且无法通过RSR(快速安全响应)修复,则可能在几周内可用,OTA更新包从Apple CDN服务器下载。

2.png

从表中可以看到,OTA包是针对当前操作系统版本定制的。

例如,为了更新到12.6,12.5和12.4的软件包是不同的。更新过程是应用补丁码,所以不同的操作系统版本有不同的补丁码。在大多数情况下,系统越老,包就越大。

下载并解压缩OTA包后,我们可以看到包的内容如下:

3.png

引导目录包含与引导过程相关的内容,增量更新的真正补丁代码位于名为payloadv2的目录中。有一个名为payload.bom的关键文件,它列出了OTA包中的所有项目及其校验和值。文件payload.bom.signature用于验证payload.bom文件的完整性。文件pre.bom和post.bom列出了更新前后系统上的所有项目及其校验和值。Info.plist文件提供了有关当前OTA包的一些基本信息,例如预版本、目标版本等。

在payloadv2文件夹中,有一些需要注意的重要文件。新系统中的新数据文件被压缩为名为data_payload的文件。ecc_data文件夹包含一些与文件权限相关的文件。links.txt文件列出了新系统的所有硬链接。removed.txt文件列出了需要从当前系统中删除的所有项目。

更新阶段

一般的更新过程可以抽象为3个阶段。

4.png

第一步是下载并解压缩UpdateBrainService 捆绑包。

第二阶段是下载并解压缩OTA包。

第三个阶段是使用OTA包生成UpdateBrainService。

那么,系统从哪里下载UpdateBrainService呢?

经过一些研究,我发现下载网址位于XML文件/System/Library/AssetsV2/com_apple_MobileAsset_MobileSoftwareUpdate_MacUpdateBrain/com_apple_MobileAsset_MobileSoftwareUpdate_MacUpdateBrain. XML:

5.png

从这个文件中,我们可以看到基本URL和构建完整URL的相对路径。它还包含版本、发布日期、包大小、SHA1值和其他有用的信息。

第一阶段

6.png

获取下载URL后,进程nsurlessiond负责将UpdateBrainService下载到临时目录。同时,com.apple.StreamingUnzipService将其解压缩到相同的临时位置。接下来,mobileassetd进程将解压的内容移动到可信位置,/System/Library/AssetsV2/com_apple_MobileAsset_MobileSoftwareUpdate_MacUpdateBrain/。最后,在启动的进程生成UpdateBrainService之前,xpc服务包将被复制到它的暂存路径。

第二阶段

第二阶段与第一阶段相似:

7.png

不同之处在于xml路径/System/Library/AssetsV2/com_apple_MobileAsset_MacSoftwareUpdate/com_apple_MobileAsset_MacSoftwareUpdate.xml,

下载url和目标位置/System/Library/AssetsV2/com_apple_MobileAsset_MacSoftwareUpdate/。

第三阶段

第三个阶段是UpdateBrainService本身。

特定的xpc服务有许多有趣的权限:

8.png

例如,“com.apple.rootless.install.heritable”授予其自身及其所有子进程修改SIP保护位置的权限。此外,“com.apple.apfs.reverse to snapshot”和“com.apple.private.apfs.create sealed snapshot(创建密封快照)”权限可能允许服务在重新启动后更新受SSV保护的内容。

我们还应该注意的一点是,它是用标志库验证进行签名的。因此,我们不能通过将动态库注入到这项服务中来直接享受这些权限。

逆转UpdateBrainService

com.apple.MobileSoftwareUpdate.UpdateBrainService2

通过简单的逆向工程,我们发现它提供了一个名为com.apple.MobileSoftwareUpdate.UpdaterainService2的mach服务,该服务具有一个称为MSUBrainPrivateSXPCI接口的协议。

9.png

它通过在委托方法中返回YES直接接受来自任何客户端的所有xpc连接:

10.png

但在研究过程中,我意识到服务中的协议方法没有实现,所以我可能会在未来的版本中再次检查。

com.apple.MobileSoftwareUpdate.UpdateBrainService

还有一个名为com.apple.MobileSoftwareUpdate.UpdateBrainService的服务。它是通过C语言的低级XPC API实现的:

11.png

在xpc handler(Handler主要用于异步消息的处理:当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分在消息队列中逐一将消息取出,然后对消息进行处理,也就是发送消息和接收消息不是同步的处理。 )方法中,我们可以看到它通过一个全局数组来调度xpc请求:

12.png

如果xpc客户机具有相应请求所需的权限,则服务将相应地调用处理例程函数。

全局调度表有7个元素,每个元素有3个成员:操作的名称、所需的权限字符串和实际处理例程函数的地址。

13.png

绕过签名验证(CVE-2022-42791)

修改包

可以在应用补丁之前修改OTA包吗?

回顾之前讨论过更新的第二阶段,可以确认OTA包的最终路径(/System/Library/AssetsV2/com_apple_MobileAsset_MacSoftwareUpdate/)被SIP保护。

但是,提取的内容首先被放置在一个临时位置(/var/folders/zz/zyxvpxvq6csfxvn_n00000y800007k/T/

com.apple.nsurlsessiond/CFNetworkDownload_XXXXXX.tmp/[hash].asset),完全不受限制,它由nsurlsessiond所有,根用户可以直接修改它。

因此,在mobileasseted进程移动到最终可信位置之前,有一个修改内容的时间窗口。因此,可信位置中的OTA包的内容是不可信的,需要进行验证。

当我尝试替换有效负载时。由于权限问题,mobileassetd进程无法调用文件API,因此拒绝移动到最终路径:

14.png

但事实是,一旦通过检测,它将调用API rename来移动包内容。所以我很早就替换了目标文件。

但一个成功的日志是什么样的呢,如下图所示:

15.png

幸运的是,有一个关键字字符串(“Moving file in …”) 表明通过检查的时间窗口。因此,一旦从日志中监控到该关键字,我就可以替换目标文件。

接下来,我将进行第二次尝试:

监控日志,一旦检测到关键字“Moving file”,就立即从OTA包中替换目标文件。

然后,被篡改的内容成功地传输到最终的可信位置!

但是,UpdateBrainService停止准备操作系统更新。

OTA包验证

此服务的职责是从可信位置验证不可信的OTA包的内容。那么,它如何验证OTA包呢?

如上所述,payload.bom文件列出OTA包中的所有项目及其校验和值:

16.png

下面是验证包内容的函数:

17.png

它打开文件payload.bom并读取其内容。接下来,该函数将payload.bom文件中指定的文件摘要值与最终路径上的真实摘要值进行比较:

18.png

如果其中一个摘要值不等于期望值,则该函数返回false并且验证失败。

它如何验证payload.Bom文件本身?

另一个名为verify_package_signature的函数负责验证:

19.png

首先,它打开payload.bom文件并计算其SHA1值。然后打开payload. dom .signature文件并读取签名文件内容。

接下来,它从系统证书文件(/System/Library/SoftwareUpdateCertificates/iPhoneSoftwareUpdate.pem)中获取公钥,该文件受SIP和SSV保护:

20.png

最后,它通过调用来自Security.framework的API seckeyverifysignsignature,用公钥计算出的SHA1值和签名文件内容验证签名:

21.png

TOCTOU漏洞

这样,在验证中存在一个经典的TOCTOU (Time-Of-Check-Time-Of-Use)漏洞:

22.png

位于受信任位置的payload.bom文件不能直接修改,但我们可以在mobileassetd进程移动OTA包之前用符号链接替换它。因此,可以使用符号链接随时修改payload.bom文件。

接下来,在函数verify_package_signature中,它根据符号链接从受控位置读取BOM文件,因此我们使用原始payload.BOM来通过检查。

然后在函数verify_package_contents中,它也遵循符号链接并使用受控BOM文件验证OTA包中的所有其他项。所以现在,我们可以模拟payload.bom文件以替换OTA包中的所有其他项。

经过三次尝试,终于实现了:

将原始payload.bom复制到受控位置/tmp/ppayload.bcom。

监控日志,一旦检测到关键字“Moving file”,就用指向/tmp/playload.bom的符号链接替换payload.bom。

符号链接(/tmp/payload.bom)已成功移动到最终受信任的位置!

在传递函数verify_package_signature后,伪造BOM文件(/tmp/payload.bom) 。

现在,OTA包中的所有项目都可以被篡改!

漏洞利用1:SIP绕过

第一个漏洞是绕过SIP,这很容易做到。

函数ParallelPatchRemoveFiles读取OTA包中的deleted .txt文件,并删除txt文件中指定的所有项:

23.png

因此,我可以通过修改txt文件获得一个原语来删除任意受sip保护的路径。

该漏洞适用于所有Mac平台,包括英特尔Mac和Apple Silicon Mac。

漏洞利用2:攻击内核

那么,我可以劫持新的操作系统内核吗?

使用SSV

绕过SIP后直接劫持OS内核的挑战是SSV保护。

signed system volume (SSV)是macOS Big Sur中引入的新功能。在SIP被绕过的情况下,它仍然使用隔离的只读系统卷来保护系统文件。

24.png

最基本的事实是,苹果需要通过OTA更新来更新操作系统内核文件。因此,OTA更新过程必须具备突破SSV保护的能力。

但是这个过程是如何完成的呢?

macOS系统有一个隐藏的更新卷 (/System/Volumes/Update/mnt1),它是当前操作系统的快照。然后将所有补丁应用于该快照。如果更新过程成功,它将更新密封值并引导新的操作系统。如果更新失败,它将恢复到以前的快照。

在深入研究了UpdateBrainService之后,我总结了OTA更新过程中的关键工作流,如下所示:

25.png

首次尝试

首次尝试是通过有效负载提取函数释放一个精心制作的内核文件。

制作data_payload的步骤如下:

26.png

在OTA更新过程中,按照预期将精心制作的data_payload提取到快照中。

不过,重启后它就不起作用了,目前还不确定是什么原因。

第二次尝试

我的第二种方法是滥用应用修复的copy_patched_files函数。这与OTA更新过程更新内核的方式相同。

然而,困难在于我必须自己创建修复文件,它是BXDIFF 5格式的,并且没有文档记录。所以,我必须先研究一下文件格式。

BXDIFF 5文件格式

谷歌搜索这个文件格式后,我找到两个GitHub存储库,它们都用于将修复文件应用于旧文件,然后生成新文件。但是,我需要基于两个不同的文件生成一个修复文件。

但它们确实帮助我理解了文件格式,通过阅读代码,我知道BXDIFF 5文件由4部分组成:Header, Control, Diff和Extra。

Header部分的大小为88字节,前8个字节是硬编码的魔术数字:

27.png

红色的部分是未知的,似乎是无用的。绿色部分为修复前后的哈希值,用于验证修复是否成功。蓝色部分是以下部分的大小。

Control部分使用LZMA算法进行压缩,解压后的Control段数据为24字节,由3种控制命令组成:

28.png

第一个是混合长度,它指定从Diff部分混合多少字节。第二个是复制长度,第三个是查找长度。

Diff部分和Extra部分也是LZMA压缩的。解压缩后,数据是一个原始字节数组,以前由Control部分使用。

制作一个精心制作的修复程序文件

我的目标是替换内核中系统命令uname的输出字符串。所以我应该修改Diff部分。

首先,使用以下脚本计算Diff节中的新字节。

29.png

然后计算当它到达inputSeekPosition时的diffSeekPosition。

30.png

我发现我的目标字符串“Darwin Kernel Version”位于偏移量0x95058d处。因此,我使用这个值作为inputSeekPosition,然后得到相应的diffSeekPosition。

接下来,重写diffSeekPosition处的新字节,并使用LZMA算法压缩新创建的Diff 部分。我们可以直接重用Control部分和Extra部分。

最后,更新新Header 部分中的哈希值和大小在我为内核制作了一个修复文件之后,它就如预期的那样工作了!

虽然它只适用于没有T2芯片的英特尔Mac电脑,但我能够将恶意shellcode 注入内核,并在ring 0执行任意代码!Ring0是CPU工作在保护模式下的一种特权模式,也称为内核模式或系统模式。在这种模式下,CPU能够访问所有的系统资源,包括内存、设备、引导程序和所有进程的地址空间。

相对于其他环节,ring0级别具有最高权限。它拥有锁定内存、直接访问硬件、中断处理、定时器、缓存管理等很多特权。

ring0属于操作系统内核,只有操作系统内核可以运行在ring0级别,这使得操作系统可以对计算机硬件进行直接的管理和控制。

硬件缓解:安全启动

其他Mac平台(Intel Mac with T2 Chip & Apple Silicon Mac)面临的挑战是名为“安全启动”的硬件缓解措施。默认设置是“完全安全”,它提供了最高级别的安全。

在启动过程中,Mac会验证启动磁盘上操作系统的完整性,以确保它是合法的。如果操作系统是未知的或无法验证为合法的,你的Mac连接到苹果下载更新的完整性信息,它需要验证操作系统。这些信息是你的Mac所独有的,它可以确保你的Mac从一个受苹果信任的操作系统启动。

苹果的修复过程

苹果是这样修复这个漏洞的:

31.png

在第180行,它调用函数digest_file_nofollow来计算SHA1值。在内部,它打开带有NO_FOLLOW标志的payload.bom文件以阻止符号链接攻击。

SIP绕过漏洞(CVE-2022-46722)

我们可以从下面的屏幕截图中看到:

32.png

解压的文件夹本身是受限制的,但其中的文件和文件夹不受限制。

一个例子是名为AssetData的文件夹。因此,我们可以直接修改OTA包的内容,即使它们被移动到最终的可信位置!

所以验证OTA包的完整性是没有用的。我们可以在OTA包通过包验证后,通过修改包内容直接获得sip绕过原语。

根本原因

通过我的研究,我意识到问题的根源在于进程mobileassetd中名为moveTargetToDirectory的函数:

33.png

它通过调用API moveItemAtURL:toURL:来移动OTA包内容,它本身保留了源文件标志和扩展属性。

最终路径上的内容不受限制,因为源文件不受限制。

苹果的修复

现在,苹果公司已经解决了这个漏洞:

34.png

它首先使用API moveItemAtURL:toURL:将OTA包内容复制到中间路径,然后在复制后使用API moveItemAtURL:toURL: 。

现在,最终路径上的内容将受到限制,因为被移动的进程写入的文件受到限制。

绕过SIP后如何直接攻击内核?

如上所述,我们可以通过通过sip绕过原语替换内核修复文件来执行任意内核代码,就像上述利用2中所做的那样。

但这有点复杂,我可以直接从快照替换受限制的内核文件本身吗?

研究发现,如果更换得太晚,重启后它将无法工作。但是,如果我过早地替换它,新内核将被补丁覆盖。那么从快照替换内核文件的正确时间是什么时候呢?我怎样才能抓住这个时间呢?

通过监控/System/Volumes/Update/restore.log,我发现它在打补丁的过程中输出了一些有用的信息。当所有补丁都被应用后,它进入下一阶段并输出日志“Unarchiving files in parallel…”:

35.png

所以我写了一个名为spin_for_log的函数来等待所有补丁启动的正确时刻:

36.png

接下来,我可以直接从快照中将原始内核替换为被恶意攻击的内核。

如上所述,它只适用于没有T2芯片的英特尔mac电脑,而T2芯片没有安全启动缓解功能。

现在我们知道UpdateBrainService的职责是验证不受信任的OTA包的内容。验证依赖于文件payload.bom。

但是,我发现并不是OTA包中的所有文件都在BOM文件中列出:

37.png

例如:usr/standalone/update/ramdisk/, Restore/, boot/*

漏洞利用

这使得许多关键文件不受保护:

38.png

所以有很多方法可以利用这个问题。

首先,删除并触发文件AssetData/usr/standalone/update/ramdisk/x86_64SURamDisk.dmg 将导致DoS漏洞。

其次,通过修改Firmware/*文件夹的内容劫持固件。

第三,修改文件AssetData/boot/Ffirmware/System/Library/CoreServices/bootbase.efi,从第一条指令劫持引导过程,并将最早的shell代码注入系统。

bootbase.efi文件采用Windows PE格式。我没有开发自己的EFI程序来改变操作系统的引导过程。为了快速验证概念,我刚刚将入口点代码修复为“0xEB 0xFE”,这是一个用于死循环的短跳转指令:

39.png

更换bootbase.efi文件并重新启动后,系统将陷入死循环,为了验证这一点,我设置了一个远程GDB存根,并在IDAPro中远程连接到它。因此,你可以看到我的shell代码确实按预期在EFI shell中执行!

40.png

为了有效地验证OTA包的完整性,有必要从payload.bom文件中列出OTA包中的所有项目。

41.png

降级攻击(CVE-2023-35983)

UpdateBrainService的主要可执行文件是一个空调用程序:

42.png

所有函数都在UpdateBrainLibrary.dylib中实现。当然,新的补丁代码也包含在dylib中。

43.png

因此,攻击者可以通过替换旧版本的dylib来执行降级攻击,旧版本的dylib由apple签名并通过库验证。

与OTA更新包类似,UpdateBrainService bundle包也可以由root用户修改。因此,攻击者可以在将UpdateBrainService移动到受信任位置之前用旧版本替换它,然后再次利用旧漏洞。

现在,苹果重构了整个流程。它确保整个过程是可信的,并且包的内容不被篡改:

44.png

首先,进程nsurlessiond创建一个名为downloadDir的受限目录,然后将UpdateBrainService bundle包下载到受信任的位置。接下来,新引入的服务com.apple.StreamingUnzipService.privileged.xpc在受信任的位置并行执行解压缩。

下载和解压缩操作完成后,以后的过程类似。

请注意,进程nsurlsessionnd和com.apple.StreamingUnzipService.privileged.xpc现在有了新的特殊权限com.apple.rootless.storage.MobileAssetDownload来修改受限制的目录。

顺便说一句,OTA软件包的下载也遵循这一新流程。

文章翻译自:https://jhftss.github.io/The-Nightmare-of-Apple-OTA-Update/如若转载,请注明原文地址


文章来源: https://www.4hou.com/posts/nmW4
如有侵权请联系:admin#unsafe.sh