攻击者思维:再现 dispatch_async 在 Voice Control 崩溃问题

328次阅读  |  发布于3年以前

语音控制是 Apple 在 iOS 13 和 macOS Catalina 中引入的一项强大功能。它可以替代屏幕上的所有触摸手势,让您可以使用语音进行点击、滑动、键入等操作,从而与设备进行交互。

com.apple.SpeechRecognitionCore.speechrecognitiond 崩溃

com.apple.SpeechRecognitionCore.speechrecognitiond 是一个处理语音控制的系统 XPCService 进程。

在对 Mobile XDR / Mobile DFIR 的调查期间,我们发现了一系列看起来很有趣的崩溃:

Exception Type:  EXC_BAD_ACCESS (SIGBUS)
Exception Subtype: EXC_ARM_DA_ALIGN at 0x0074616f6c460003

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x2000002400000000 -> 0x0000002400000000 

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x00000000100c02d8

移动设备调查分析

并非所有崩溃都相同,但它们都有相似的模式。所有崩溃都发生在一些 libdispatch.dylib 调用之后。

有了这个线索,我们继续调查这次事故的原因。

我们将解释两个最典型的案例。当用户在不同的时间切换语音控制开关时,都会发生这两种情况。

我们将附上一个演示意外多线程问题的 POC,证明即使开发人员使用优化的线程管理库(如 Grand Central Dispatch (GCD) 调度队列,从多线程的角度来看,这已经被认为是安全的)竞争条件的可能性仍然存在,能够导致内存损坏并导致代码执行。

下面是两个最典型的案例:

案例1:当用户关闭 VoiceControl 时

当设备正在主动处理音频数据时,会创建调度队列“RDAudioBufferQueue”。一个 AVSoundInput 类实例已被传递给该线程,以通过上下文数据提供输入数据。由于线程安全考虑不够,当用户决定关闭语音控制功能时,上下文数据可能会在另一个队列“RDMainQueue”中提前释放,从而导致Use-After-Free(UAF)。

实际上有一个函数处理_dispatch_call_block_and_release<PC Corruption>之间执行的音频格式转换。它没有显示在回溯中,因为它使用了不将返回地址保存在堆栈中的“br”指令。

下面是该函数的伪代码:

(1) 在 _addRecordedSpeechSampleData:length: 方法内部,它试图调用存储在*( *(context_data + 48) + 16) 中的函数指针,通常它会执行EARCSpeechRecognitionAudioBufferAddAudioSamples。但是,如果用户决定关闭语音控制,context_data 将在另一个线程中释放,如下所示:

问题是缺少一个锁来确保 RDAudioBufferQueue 会在 context_data 被释放之前退出。 *(context_data + 48) 中的堆内存可能会提前释放并被其他数据重新占用,从而导致程序计数器 (PC) 损坏。

案例2:当用户打开语音控制时

RDMainQueue 可能会随机发生在不同对象上的内存损坏。上面的例子是XPC连接对象在使用前被释放,Use-After-Free导致线程崩溃。

RDMainQueue 用作多用途的通用队列。各种回调将任务扔到这个队列中,包括接受和处理xpc请求,上报音频数据反馈和采取行动,经常涉及嵌套调用..所有这些任务都是通过dispatch_async提交到RDMainQueue的,它们的顺序似乎是正确的 打电话。

如果 XPC 连接对象的使用和释放都被分配到同一个队列中以正确的顺序处理,Use-After-Free 如何发生?

答案是 dispatch_async 不保证块按照调用顺序执行!

以下 POC 演示了即使在同一队列上使用 dispatch_async 的潜在威胁:

dispatch_sync 更安全,就像将所有 dispatch_async 替换为 dispatch_sync 一样,上面的代码将完美运行。

dispatch_async 带来了支持嵌套调用的便利。但是,为了线程安全,开发人员最好实施额外的检查,以确保块按要求的顺序执行。

当用户频繁打开和关闭声控开关时,添加队列的繁忙操作会弄乱顺序,释放后可能仍然使用对象。

再现崩溃

您可以在锁定屏幕上触发此 Use-After-Free,执行以下步骤:

  1. 按住侧边按钮激活 Siri。
  2. 说“关闭语音控制”,会出现一个语音控制开关窗口。

  1. 反复开启/关闭语音控制。有一些技巧可以更可靠地触发崩溃。

除了使用 Siri,您还可以转到设置 -> 辅助功能 -> 语音控制

开启语音控制后,左上角会出现一个图标,先显示为灰色,然后变成蓝色。找到最佳时机的诀窍是在图标变为蓝色之前关闭语音控制。

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8