0371-63319761
您的当前位置:主页 > 安全研究 > 安全研究 >

MediaTek 音频 DSP 中的漏洞分析

时间:2021-11-30


 
MediaTek系统级芯片(SoC) 已嵌入全球约 37% 的智能手机和物联网设备中,包括来自小米、Oppo、Realme、Vivo等高端手机。
 
MediaTek SoC,包括最新的 Dimensity 系列,包含一个特殊的 AI 处理单元 (APU) 和音频数字信号处理器 (DSP),以提高媒体性能并降低 CPU 使用率。APU 和音频 DSP 都具有定制的 Tensilica Xtensa 微处理器架构。Tensilica 处理器平台允许芯片制造商使用自定义指令扩展基本 Xtensa 指令集,以优化特定算法并防止它们被复制。
 
研究人员对MediaTek音频 DSP 固件进行了逆向工程,尽管其操作码和处理器寄存器具有独特的安全防护,但他们还是发现了几个可从 Android 用户空间访问的漏洞。
 
通过与原始设备制造商 (OEM) 合作伙伴库中的漏洞相关联,研究人员发现的MediaTek安全问题可能会导致 Android 应用程序的本地权限升级。成功利用 DSP 漏洞可能允许攻击者监听用户对话或隐藏恶意代码。
 
通过 Android 攻击音频 DSP 的方法
 
研究人员研究的目标是找到一种通过 Android 攻击音频 DSP 的方法。首先,研究人员需要了解运行在应用处理器 (AP) 上的 Android 如何与音频处理器进行通信。显然,必须有一个驱动程序等待来自 Android 用户空间的请求,然后使用某种处理器间通信 (IPC),将这些请求转发给 DSP 进行处理。
 
使用基于MT6853芯片组的小米红米Note 9 5G智能手机作为测试设备,操作系统为 MIUI Global 12.5.2.0 (Android 11 RP1A.200720.011)。
 
由于设备上呈现的媒体相关驱动程序很少,所以不难找到负责AP和DSP之间通信的驱动程序。
 
 
媒体驱动程序
 
研究人员对 /dev/audio_ipi 驱动程序感兴趣。
 
在供应商分区中对驱动程序名称进行简单搜索,即可找到MediaTek API库/vendor/lib/hw/audio.primary.mt6853.so。该库导出AudioMessengerIPI示例,其中包含sendIpiMsg方法,该方法可用于向音频DSP发送IPI消息。研究人员使用这个库来探索 Android 用户空间和内核之间的通信流程。在研究人员的 PoC 代码中,研究人员直接处理驱动程序 ioctls,无需额外包装。
 
/dev/audio_ipi 驱动程序中定义了以下 ioctls:
 
 
发送消息前,必须使用AUDIO_IPI_INIT_DSP ioctl对DSP固件进行初始化。
 
研究人员可以使用以下简单的函数来打开和初始化驱动程序:
 
 
在 DSP 端,有几个独立的消息处理程序,称为任务场景。每个任务场景都有自己独特的功能范围。例如,电话任务控制语音增强。AUDIO_IPI_LOAD_SCENE ioctl 用于在 DSP 上加载任务场景。任务场景ID是IPI消息的必填参数。
 
有三种不同的 ioctl 用于向音频 DSP 发送 IPI 消息。不同之处在于与消息关联的有效载荷数据的传输方式。可能的选择是:
 
将有效载荷作为消息的一部分传输 (AUDIO_IPI_SEND_PAYLOAD)。有效载荷大小限制为 0xE0 字节;
 
通过注册为在 AP 和 DSP 之间进行通信的共享内存 (AUDIO_IPI_SEND_DRAM) 传输载荷;有效载荷大小受共享区域大小的限制;
 
不要传输有效载荷 (AUDIO_IPI_SEND_MSG_ONLY);
 
IPI 消息具有以下结构:
 
 
重要的领域是:
 
task_scene——DSP任务场景ID;
 
data_type——有效载荷类型,如果有效载荷字段包含与消息关联的数据,则设置为 1。如果有效载荷字段包含有关共享区域的信息,则设置为 2;
 
msg_id ——消息 ID;
 
param1 和 param2——消息参数,通常 param1 包含有效载荷大小。
 
因此,研究人员可以完全控制从 Android 用户空间传输的消息。通过 task_scene 和 msg_id 字段定位 DSP 处理程序,并通过 param1、param2 和有效载荷字段为其提供研究人员的数据。
 
现在就可以处理共享内存了, AUDIO_IPI_REG_DMA ioctl 可用于请求 DSP 驱动程序在 AP 和 DSP 之间共享的专用直接访问存储器 (DMA) 中分配一个区域。实际上,分配了两个内存区域:一个用于从AP传输数据到DSP任务场景,另一个用于反向传输数据。DSP 驱动程序在调用 AUDIO_IPI_SEND_DRAM ioctl 时使用这些区域传输消息有效载荷并接收结果。
 
AUDIO_IPI_REG_DMA ioctl 需要具有以下结构的对象作为参数:
 
 
研究人员通过 a2d_size 和 d2a_size 字段控制分配区域的大小。
 
Android 内核日志为研究人员提供了有关预留 DMA 的信息:
 
基本虚拟地址- 0xffffff800b000000;
 
基本物理地址:0x7d940000;
 
大小- 0x200000;
 
当研究人员为任务场景分配共享区域时,也会记录 DMA 中的相应偏移量。
 
 
Android 内核日志
 
任务场景的共享区域的物理地址(计算为DMA的基本物理地址+共享区域的偏移量)在设备上是持久的。
 
以下函数可用于通过 DMA 发送带有数据传输的 IPI 消息:
 
 
/dev/audio_ipi 驱动程序不直接与音频 DSP 通信。相反,它通过将消息添加到 SCP 队列将 IPI 消息转发到系统控制处理器 (SCP)。音频DSP固件注册SCP调度器以接收来自SCP的音频IPI消息。
 
逆向过程
 
固件镜像
 
研究人员知道如何向音频 DSP 发送 IPI 消息,下一步是在 DSP 固件中找到此类消息的处理程序。
 
音频 DSP 在小米出厂更新中通过单独的 audio_dsp.img 镜像文件呈现。获取镜像的另一种方法是从根设备转储 /dev/block/platform/bootdevice/by-name/audio_dsp 分区。
 
镜像文件具有专有结构,但可以轻松重建。在研究人员的测试设备上,DSP 镜像包含九个分区。
 
 
audio_dsp.img 结构
 
cert1 和 cert2 分区是 DER 格式的证书,用于验证 hifi3 分区的完整性。hifi3_a_dram 分区是音频固件使用的动态内存。在初始状态下,它几乎是空的。hifi3_a_iram和hifi3_a_sram分区是自定义FreeRTOS的代码和数据。
 
每个分区都有一个标头,用于存储该分区的大小和名称。标头以神奇的 0x88168858 开头,可用于快速定位文件中分区的开头。图 4 显示了 hifi3_a_dram 标头。
 
 
hifi3_a_dram 标头
 
hifi3_a_dram 分区的标头和数据大小分别为 0x200 和 0x8000,研究人员可以轻松剪切hifi3内容。
 
仔细看看 hifi3_a_sram,分区以 0x400 零字节开始。所以这里没有特殊的文件格式。研究人员正在处理原始数据。接下来的 0x37F8 字节似乎是指向内存的指针,主要位于 0x56000000 地址之后,从字节 0x3BF8 开始是 Xtensa 代码。
 
IDA Pro 7.6 支持 Tensilica Xtensa 架构。研究人员在IDA中打开hifi3_a_sram分区,基地址为0x56000000。
 
研究人员使用这个简单的脚本将前导原始字节识别为指针(双字):
 
 
现在研究人员有成千上万个指向代码和数据的指针。但是研究人员如何处理代码呢?Xtensa 操作码的长度可变,IDA 不知道如何进行。
 
研究人员首先尝试编写一个脚本来查找函数的开头并尝试反汇编。这是可能的,因为大多数函数都从分配堆栈的入口操作码开始。但它在这里效果不佳,因为有太多 IDA 不知道的自定义操作码。反汇编遇到未知操作码时会卡住。研究人员得到的只是如下片段:
 
 
最终,研究人员找到了另一个很好的解决方案。研究人员使用 Xtensa SDK 来帮助 IDA。
 
HiFi DSP 软件开发工具链可以从 tensilicatools.com 网站免费下载。XtDevTools 是安装包的一部分。研究人员使用 ~/xtensa/XtDevTools/install/tools/RI-2020.5-linux/XtensaTools/bin/xt-objdump 工具来创建 hifi3 分区的对象转储。这样研究人员就转储了hifi3_a_sram:
 
 
对象转储包含反汇编的 Xtensa 代码,来看看IDA收到的指令:
 
 
如你所见,xt-objdump 工具的 hifi3_ss_spfpu_7 核心比 IDA 插件知道更多的 Xtensa 操作码。显然,MediaTek使用了Tensilica准备的标准音频DSP模板作为其处理器的基础。MediaTek添加了几个特定的指令,但与 Tensilica 为音频 DSP 提供的指令相比,它们的数量很少。
 
对象转储包含许多漏洞,不能作为研究的主要来源。但它可以帮助 IDA 更轻松地拆解 hifi3 分区。
 
Xtensa 插件在 IDA 中由 xtensa.so 库表示。因为要添加的指令太多,所以不容易打补丁。最好的解决方案是使用对象转储来查找所有基本的 Xtensa 指令,并将反汇编作为注释添加到任何无法识别的指令中。一个简单的 IDA 脚本就可以完成这项工作。在下图中,你可以看到应用转储后 IDA 导航栏的外观。几乎所有的代码块都被识别。
 
 
IDA 导航栏
 
反汇编后的代码如下所示:
 
 
大多数固件功能都包含用于记录调试信息的代码。日志消息包括当前函数的名称。MediaTek给了研究人员自描述的函数名和快速搜索代码中函数的能力。
 
研究人员以与 hifi3_a_sram 相同的方式拆解了 hifi3_a_iram 分区。hifi3_a_dram 和 hifi3_a_iram 的基地址分别为 0x4FFB0000 和 0x4FFE0000。
 
FreeRTOS
 
既然找到了研究音频DSP固件的方法,就来看看它的内容。
 
MediaTek 音频 DSP 操作系统是 FreeRTOS 的改编版本。MediaTek使用了第三方内核,并在其上实现了音频和消息逻辑。
 
操作系统在启动时会创建许多音频任务,并将它们与场景 ID 相关联。研究人员可以在 create_all_audio_task 函数中找到所有支持的任务和场景 ID。以下任务在研究人员的测试设备上运行:
 
 
每个音频任务由一个包含指向 recv_message 函数的指针的任务对象表示。当新的 IPI 消息到达时,SCP 消息调度程序调用此函数。IPI 消息作为第二个参数传递给函数。
 
recv_message 函数正是研究人员正在寻找的。这是音频任务开始处理从 Android 端发送的 IPI 消息的地方。快速查看代码后,研究人员看到除了电话呼叫、卸载、控制器和守护进程之外,大多数任务都使用相同的 task_common_recv_message 函数。毕竟,只有接下来的五个函数解析 IPI 消息,这是研究人员可以搜索漏洞的地方:
 
 
研究人员手动检查了这些功能,发现了几个可用于从 Android 攻击 DSP 的漏洞。
 
CVE-2021-0661、CVE-2021-0662 和 CVE-2021-0663
 
AUDIO_DSP_TASK_MSGA2DSHAREMEM 消息处理程序中的经典堆溢出
 
此漏洞与所有常见的音频 DSP 任务有关。在处理 ID 为 6 (AUDIO_DSP_TASK_MSGA2DSHAREMEM) 的 IPI 消息时,task_common_task_loop 函数会将消息载荷复制到公共任务对象的 atod_share 字段中。消息 param1 用作要复制的字节数。省略了 param1 不大于 atod_share 字段大小的检查。因此,当载荷大小大于0x20字节时,载荷会覆盖atod_share之后的内存。
 
 
下面的调用send_ipi_dma在Android端覆盖DSP内存垃圾和导致崩溃:
 
 
init_share_mem_core 函数中的经典堆溢出
 
当接收到ID为7的IPI消息时,守护任务的task_auddaemon_task_loop函数调用init_share_mem_core函数。init_share_mem_core 使用 param1 作为要复制的字节数将消息有效载荷复制到内部 audio_dsp_dram 缓冲区。该函数检查 param1 是否小于 0xE0 字节,但 audio_dsp_dram 大小为 0x20 字节, 0xC0 字节可以被覆盖。
 
要使用受控值修复 DSP 堆,研究人员可以发送携带有效载荷的 IPI 消息作为消息的一部分:
 
 
Android 内核日志确认了该漏洞:
 
 
audio_dsp_hw_open_op 函数中的数组索引验证不正确
 
在处理 ID 为 0x203 (AUDIO_DSP_TASK_PCM_PREPARE) 的 IPI 消息时,task_common_task_loop 函数调用 get_audiobuf_from_msg 从 param2 寻址的物理内存中提取音频缓冲区。接下来,将此缓冲区作为参数传递给作为 audio_dsp_hw_open_op 的包装器的 audio_dsp_hw_open 函数。audio_dsp_hw_open_op 函数将此音频缓冲区复制到静态数组。音频缓冲区中偏移量 0x54 处的字段用作数组索引。没有索引值的溢出检查。因此,研究人员可以提供任意索引,用受控值覆盖数组后面的一部分内存。
 
要拥有音频缓冲区,研究人员可以通过共享 DMA 区域将 IPI 消息发送到 DSP,并将 param2 指向有效载荷所在的内存。正如研究人员之前所展示的,共享 DMA 区域的物理地址永久存在于设备上。
 
以下 PoC 代码会重新启动研究人员的测试设备:
 
 
请注意,get_audiobuf_from_msg 函数也不验证 param2。在 param2 中使用任何不合适或空的地址都会使 memcpy 函数中的 DSP 崩溃:
 
 
寻找一种从非特权应用程序攻击 Android HAL 的方法
 
现在研究人员知道如何通过 /dev/audio_ipi 驱动程序从 Android 攻击音频 DSP。不幸的是,无特权的 Android 应用程序以及 adb shell 没有与此驱动程序通信的权限。SELinux 仅允许从 factory、meta_tst 和 mtk_hal_audio 上下文访问 audio_ipi_device 对象。攻击者需要找到一种方法来利用 MediaTek 硬件抽象层 (HAL) 从 mtk_hal_audio 上下文下访问 DSP 驱动程序。
 
在寻找攻击 Android HAL 的方法时,研究人员发现了 MediaTek 为调试目的实施的几个危险的音频设置。第三方 Android 应用程序可以滥用这些设置来攻击 MediaTek Aurisys HAL 库。
 
音频硬件参数
 
Android 文档指出 AudioManager 提供对音量和铃声模式控制的访问。Android 应用程序可以绑定音频服务,然后使用 AudioManager 的 setParameters 方法来配置硬件。
 
 
设备制造商可以添加他们自己的音频设置并跟踪他们的更改。MediaTek 提供专有参数来配置 Aurisys 库。在研究人员的测试设备上,/vendor/lib/hw/audio.primary.mt6853.so 库负责处理MediaTek添加的音频参数。在下图中,你可以看到 setParameters 字符串参数的可接受格式。
 
 
MediaTek音频参数
 
参数字符串包含以下信息:
 
处理命令的目标子系统,它可以是 HAL 或 DSP;
 
aurisys 场景;
 
标识受影响的 HAL 库的命令项;
 
命令字符串,研究人员找到了八个支持的命令;
 
该值实际上是命令的参数;
 
/vendor/etc/aurisys_config.xml 和 aurisys_config_hifi3.xml 文件定义了所有支持的 aurisys 场景和命令项。
 
例如,以下参数可用于启用语音处理信息的日志记录:
 
 
大多数支持的命令在信息泄漏方面对研究人员来说很有趣。但是研究人员只想关注 PARAM_FILE 命令,它允许研究人员设置与特定 Aurisys HAL 库相关的配置文件的位置。
 
例如,非特权 Android 应用程序可以通过设置以下参数来自定义 OEM 提供的 libfvaudio.so HAL 库:
 
 
Aurisys 库在提供时解析配置文件。
 
注意,设备制造商通常不关心正确地验证配置文件,因为非特权用户无法使用这些文件。但在研究人员的例子中,他们控制着配置文件。HAL 配置成为攻击媒介。格式错误的配置文件可用于使 Aurisys 库崩溃,从而导致 LPE。
 
研究人员已经准备了一个针对小米设备上的 libfvaudio.so HAL 库的攻击示例,但出于安全原因,研究人员无法分享详细信息。
 
为了缓解所描述的音频配置问题,MediaTek决定在 Android 的发布版本中删除通过 AudioManager 使用 PARAM_FILE 命令的功能,且漏洞被命名为 CVE-2021-0673。
 
参考及来源:http://research.checkpoint.com/2021/looking-for-vulnerabilities-in-mediatek-audio-dsp/  
来源:嘶吼专业版

 

Copyright © 2017-2020 冰球突破豪华版官网 版权所有 豫ICP备18011434号-1 豫公网安备 41019702002746号