CVE-2025-31200:Apple CoreAudio 在野零日漏洞完整分析
背景2025 年 4 月 16 日,Apple 发布了一个针对 CoreAudio 中一个漏洞的补丁,他们表示这个漏洞“在野外被积极利用”。__text 部分的 20 字节增加正是你期望从这种内存损坏 2025-11-27 03:53:25 Author: www.freebuf.com(查看原文) 阅读量:8 收藏

背景

2025 年 4 月 16 日,Apple 发布了一个针对 CoreAudio 中一个漏洞的补丁,他们表示这个漏洞“在野外被积极利用”。
__text 部分的 20 字节增加正是你期望从这种内存损坏中看到的改变类型,所以我想我会用 Binary Ninja 打开新旧版本,变化会很明显。首先,我必须实际进行二进制差异分析。Ipsw-diff 对于像这样的基本信息很棒,但它不是反汇编器,所以它不显示任何代码级变化。为了进行实际差异分析,我使用了 Radare2 中的 radiff2。

我完全期望这个二进制文件是 Dyld 共享缓存的一部分。然而,如果你仔细注意 ipsw-diff 中的路径,你会看到它不是。它位于/System/Library/Components/AudioCodecs.component/中。那是 Components 而不是 Frameworks。我不知道它们之间的区别是什么。无论如何,macOS 上的 AudioCodecs 不是共享缓存的一部分。我收集了旧版和新版二进制文件,并用 radiff2 运行它们。

差异分析

Radiff2 为我吐出了几个候选函数。其中大多数只是编译器熵差异(想想寄存器围绕移动)或由于文本部分移位而导致的不同地址。幸运的是,有一个函数突出地作为明显候选:apac::hoa::CodecConfig::Deserialize(this, bitStreamReader),可能是 CodecConfig 类的成员方法(你能从 '::' 看出是 C++)。有许多逻辑变化、一个新的错误消息,以及一个新的检查,所有这些在方法底部清楚可见。我以为现在发现漏洞会很简单,因为变化似乎足够自包含,通常是后续修复——而不是实际的利用原语本身——需要最多的独创性。

我盯着旧版和新版 Deserialize 方法之间的差异看了一会儿。我甚至花时间手动复制粘贴到 VSCode 中,使用其内置的差异功能。我能看出很多是相同的。该方法由一系列从比特流读取的块组成,每个块都有自己的失败情况和控制流。它看起来像一个相对典型的解析方法。高层次流程是一系列看起来像这样的块:

  1. 从流中读取一些比特,使用 bitStreamReader

  2. 检查一些错误条件

  3. 如果失败,则记录失败并退出

  4. 如果成功,则推进指针并继续到下一个块

不同的部分是最后的从流中读取。在其中有重复的片段,像这样旨在读取数组长度的:

1// Access 
 2uint32_t *section = this->offset_0x78;
 3
 4// Read the opcode stored at section[0]
 5uint32_t opcode = section[0];
 6int32_t  elementCount;
 7
 8if (opcode == 0x10000) {
 9    // If opcode == 0x10000, count all set-bits in the next 8 bytes of data
10    // (i.e., the two uint32_t elements section[1] and section[2])
11    uint8x8_t raw_bytes     = vld1_u8((uint8_t *)&section[1]); // uint8x8_t is a NEON intrinsic vector type holding 8 unsigned 8-bit values
12    uint8x8_t popcounts     = vcnt_u8(raw_bytes);
13    uint64_t  total_bits    = vaddlv_u8(popcounts);
14    elementCount = (int32_t)total_bits;
15}
16else if (opcode != 0) {
17    // If opcode is nonzero, use its low 16 bits
18    elementCount = (int32_t)(opcode & 0xFFFF);
19}
20else {
21    // Otherwise, fall back to the third array element
22    elementCount = (int32_t)section[2];
23}
24//do stuff with elementCount

然后,在它们之间(在不同部分内)还有一个常见的浮点小舞步,来计算数组条目的比特宽度(bitWidth),稍后将从流中读取。

1v0_3 = (float)remappingEntries;
2int128_t v0_4;
3v0_4 = (double)_log2f(v0_3);
4v0_4 = v0_4 + -0.0001;
5v0_4 = (float)v0_4;
6uint32_t bitWidth = vcvtps_u32_f32(v0_4);

本质上是floor(log₂(elementCount))。在两个版本中,都有一些代码计算数组的大小。它使用所需元素数量和当前数组大小:

1if (elementCount > currentSize) {
2	resize(arr, elementCount - currentSize);
3}
4else if (currentSize > elementCount) {
5	current_arr_end_ptr -= (currentSize - elementCount)
6}

各自的最后读取其余部分不同。在旧版中,它使用上面计算的elementCount。在新版中,它使用this指针的某个字段:

1uint64_t elementCount = (uint64_t)*(uint32_t*)((char*)this + 0x58);

这是一个重要的线索。我想如果我只是追踪其余代码,那么会有一个基于this+0x58而非elementCount的大小检查的写入到elementCount大小的数组中。那将是原语。这里是从流中读取并写入数组的代码块:

1size_t     index       = 0;
 2uint8_t    lastSymbol;
 3int32_t    limit;
 4
 5do {
 6    // Same exact section as before: computing how many entries we need in our array
 7    uint32_t *section = this->off_0x78;
 8    uint32_t  opcode  = section[0];
 9    int32_t   elementCount;
10
11    if (opcode == 0x1000

文章来源: https://www.freebuf.com/articles/vuls/459346.html
如有侵权请联系:admin#unsafe.sh