内容简介:我们在macOS中发现了一个双重释放(double free)漏洞(编码为这个CVE编号实际上覆盖了两个缺陷:
0x00 前言
我们在macOS中发现了一个双重释放(double free)漏洞(编码为 CVE-2019-8635 ),该漏洞由AMD组件的内存破坏缺陷所导致。如果成功利用该漏洞,攻击者可以实现权限提升,以 root
权限在系统上执行恶意代码。我们向Apple反馈了漏洞情况,厂商后面也发布了相应 补丁 。
这个CVE编号实际上覆盖了两个缺陷: discard_StretchTex2Tex 方法以及 AMDRadeonX400_AMDSIGLContext
这个AMD Radeon类中对边带令牌(sindeband token)的 处理逻辑 。 AMDRadeonX400_AMDSIGLContext
派生自 IOAccelGLContext2
类,而后者由 IOAccelContext2
类扩展而来。这些类用来在macOS主机上渲染图像。
漏洞位于 discard_StretchTex2Tex
以及 AMDSIGLContext::process_StretchTex2Tex
函数中,这两个函数是 AMDRadeonX4000_AMDSIGLContext
类的函数,我们可以使用 AMDRadeonX4000_AMDSIGLContext
userclient以及selector 2对应的函数 IOAccelContext2::submit_data_buffers
来访问这个类,使用connect type 1来打开 AMDRadeonX4000_AMDGraphicsAccelerator
客户端。
0x01 AMDRadeonX4000_AMDSIGLContext discard_StretchTex2Tex双重释放权限提升漏洞
攻击者可以利用该漏洞在用户空间上执行代码。为了利用该漏洞,攻击者首先必须具备在目标macOS系统上执行低权限代码的能力。
该缺陷是因为系统没有对用户提供的数据进行适当的验证,导致读取操作超出已分配数据结构的末尾地址。攻击者可以利用这一点,再与其他漏洞结合起来,将权限提升至内核级别。
0x02 AMDRadeonX4000_AMDSIGLContext双重释放权限提升漏洞
同一个AMD类对边带令牌的处理过程中也存在一个双重释放漏洞。本地攻击者可以利用该漏洞在受影响的macOS上执行任意命令。与上一个漏洞一样,攻击者首先必须具备在目标系统上执行低权限代码的能力,才能进一步利用该漏洞。
虽然前一个漏洞位于 AMDRadeonX4000_AMDSIGLContext:discard_StretchTex2Tex
函数中,但这个漏洞位于 AMDRadeonX4000_AMDSIGLContext::process_StretchTex2Tex
函数中。漏洞成因在于系统在对目标对象执行操作前,没有去验证该对象是否存在。攻击者可以利用该漏洞将权限提升至内核级别。
从本质上讲,这两个漏洞在可能的利用途径方面比较相似,但在于具体利用的函数方面有所区别。
0x03 漏洞分析
图1. AMDRadeonX4000_AMDSIGLContext: discard_StretchTex2Tex
函数伪代码片段(上图), AMDRadeonX4000_AMDSIGLContext::process_StretchTex2Tex
函数伪代码片段(下图)
如图1(上图)所示,如果 (cmdinfo+32)
等于 0x8c00
,那么 IOAccelResource
v10
以及 v11
都会从 IOAccelShared2
中取值,对应的索引分别为 *(shareMem_start_address_187_offset16+8)
以及 *(shareMem_start_address_187_offset16+12)
。该函数随后会使用 IOAccelResource2::clientRelease()
函数来释放两个加速器资源。然而攻击者可以从用户空间中,通过内存映射,使用 IOAccelContext2
userclient来直接控制这两个索引。如果用户空间为 lookupResource
函数映射相同的索引,那么 clientRelease
就会两次释放相同的资源客户端,此时就会出现双重释放漏洞。
如图1(下)所示,如果 v15
等于 0x8c00
,那么 accelResource_offset8
以及 accelResource_offset12
都会从 IOAccelShared2
中取值,以共享内存偏移24及28的值作为索引。最终,该函数会从 IOAccelShared2 _rst
释放 accelResource_offset12
,如果 accelResource_offset8->member2
不等于 10
,该函数也会从 IOAccelShared2
释放 accelResource_offset8
。然而将共享内存偏移24及28对应的值设成相同值,就会导致系统两次释放同一个 accelResource
。
在 process_StretchTex2Tex()
函数中,完成stretch操作时会使用 IOAccelResource2::clientRelease()
函数来释放两个资源客户端。然而,这两个 accelResource2
源自 AMDRadeonX4000_AMDSIGLContext
类中的 accelShare2
共享内存,使用对应的索引通过 IOAccelShared2::lookupResource
函数来获取。攻击者可以从用户空间中,通过 IOAccelContext2
用户客户端,利用内存映射来控制这些索引值。如果用户空间对 lookupResource
函数映射相同的索引,那么 clientRelease
就会两次释放相同的资源客户端,最终出现双重释放漏洞。
根据这两处代码执行流,共享内存地址指向的都是 commandStreamInfo + 24
。然而, commandStreamInfo
缓冲区实际上在 IOAccelContext2::processSidebandBuffe
函数中设置,如下图所示。在图2中, v5
指向的是 shareMem + 16
,而 this->member196
指向的是 commandStreamInfo + 24
。
图2. IOAccelContext2::processSidebandBuffer
伪代码片段
IOAccelContext2::clientMemoryForType
函数的伪代码片段如图3所示。该函数由已知的 IOConnectMapMemory64
API来调用,而后者会将一个用户缓冲区映射到内核空间中。在使用 IOConnectMapMemory64
函数时,我们需要设置连接对象、内存类型以及其他一些参数。此处连接对象为 IOAccelContext2
的实例,内存类型为 0
,如图3所示。当我们将内存类型设置为 0
时, clientMemoryForType
函数会创建一个缓冲区内存描述符,返回用户空间的起始地址。此外,该函数还会将缓冲区内存地址设置为 shareMem_start_vm_address_187
变量(我们设置的变量名,非原始代码使用的变量名),该变量实际上正是在 IOAccelContext2::processSidebandBuffer
函数中使用的值。
根据该代码流程,我们可以控制共享缓冲区,以类似的方式设置这两个资源索引,随后触发双重释放漏洞。
图3. IOAccelContext2::clientMemoryForType
函数伪代码片段
应用崩溃日志中的回溯信息如下所示,其中如果使用 discard_StretchTex2Tex
函数,那么只有 AMDRadeonX4000
AMDRadeonX4000_AMDSIGLContext::process_StretchTex2Tex 以及
process_StretchTex2Tex(IOAccelCommandStreamInfo&) + 2893`函数偏移有所区别。
* thread #1, stop reason = signal SIGSTOP frame #0: 0xffffff7f8d7adc37 IOAcceleratorFamily2`IOAccelResource2::clientRelease(IOAccelShared2*) + 13 frame #1: 0xffffff7f8d880dad AMDRadeonX4000`AMDRadeonX4000_AMDSIGLContext::process_StretchTex2Tex(IOAccelCommandStreamInfo&) + 2893 frame #2: 0xffffff7f8d79b5d5 IOAcceleratorFamily2`IOAccelContext2::processSidebandBuffer(IOAccelCommandDescriptor*, bool) + 273 frame #3: 0xffffff7f8d8885e4 AMDRadeonX4000`AMDRadeonX4000_AMDSIGLContext::processSidebandBuffer(IOAccelCommandDescriptor*, bool) + 182 frame #4: 0xffffff7f8d79bae7 IOAcceleratorFamily2`IOAccelContext2::processDataBuffers(unsigned int) + 85 frame #5: 0xffffff7f8d7a2380 IOAcceleratorFamily2`IOAccelGLContext2::processDataBuffers(unsigned int) + 804 frame #6: 0xffffff7f8d798c30 IOAcceleratorFamily2`IOAccelContext2::submit_data_buffers(IOAccelContextSubmitDataBuffersIn*, IOAccelContextSubmitDataBuffersOut*, unsigned long long, unsigned long long*) + 1208 frame #7: 0xffffff800b027a3c kernel.development`::shim_io_connect_method_structureI_structureO(method=, object=, input=, inputCount=, output=, outputCount=0xffffff8742023968) at IOUserClient.cpp:0 [opt] frame #8: 0xffffff800b025ca0 kernel.development`IOUserClient::externalMethod(this=, selector=, args=0xffffff87420239b8, dispatch=0x0000000000000000, target=0x0000000000000000, reference=) at IOUserClient.cpp:5459 [opt] *frame #9: 0xffffff800b02ebff kernel.development`::is_io_connect_method(connection=0xffffff80b094e000, selector=2, scalar_input=, scalar_inputCnt=, inband_input=, inband_inputCnt=136, ool_input=0, ool_input_size=0, inband_output=””, inband_outputCnt=0xffffff80b0d81e0c, scalar_output=0xffffff8742023ce0, scalar_outputCnt=0xffffff8742023cdc, ool_output=0, ool_output_size=0xffffff80ab5c7574) at IOUserClient.cpp:3994 [opt] frame #10: 0xffffff7f913044c2 frame #11: 0xffffff800a9bbd64 kernel.development`_Xio_connect_method(InHeadP=, OutHeadP=0xffffff8742023ce0) at device_server.c:8379 [opt] frame #12: 0xffffff800a88d27d kernel.development`ipc_kobject_server(request=0xffffff80ab5c7400, option=) at ipc_kobject.c:359 [opt] frame #13: 0xffffff800a859465 kernel.development`ipc_kmsg_send(kmsg=0xffffff80ab5c7400, option=3, send_timeout=0) at ipc_kmsg.c:1832 [opt] frame #14: 0xffffff800a878a75 kernel.development`mach_msg_overwrite_trap(args=) at mach_msg.c:549 [opt] frame #15: 0xffffff800a9f63a3 kernel.development`mach_call_munger64(state=0xffffff80af471bc0) at bsd_i386.c:573 [opt] frame #16: 0xffffff800a823486 kernel.development`hndl_mach_scall64 + 22
当Mac系统出现内核错误(kernel panic)时,panic文本会被添加到日志中。 kernel panic 指的是内核检测到的系统错误,根源在于内核代码中没有对处理器异常进行处理(比如引用了无效的内存地址,或者调用链中出现错误,可以对比用户空间代码检测到的错误)。panic日志如下所示:
panic(cpu 6 caller 0xffffff800aa1391c): Kernel trap at 0xffffff7f8d7adc37, type 14=page fault, registers: CR0: 0x0000000080010033, CR2: 0x0000000000000018, CR3: 0x0000000fea85f063, CR4: 0x00000000001626e0 RAX: 0x0000000000000000, RBX: 0xffffff800b473e28, RCX: 0x00000000ffffffff, RDX: 0x0000000000000000 RSP: 0xffffff8742023610, RBP: 0xffffff8742023610, RSI: 0xffffff80b0f8e470, RDI: 0xffffff80afa29300 R8: 0x0000000000000229, R9: 0xffffff800b2c4d00, R10: 0xffffff800b2c2c70, R11: 0x0000000000000058 R12: 0xffffff87299cb9b4, R13: 0x0000000000000001, R14: 0xffffff80b094e608, R15: 0xffffff80b094e000 RFL: 0x0000000000010282, RIP: 0xffffff7f8d7adc37, CS: 0x0000000000000008, SS: 0x0000000000000010 Fault CR2: 0x0000000000000018, Error code: 0x0000000000000002, Fault CPU: 0x6, PL: 0, VF: 0
与此同时,寄存器调试信息中关于kernel panic的信息如下所示。 $r12
寄存器指向的是共享内存地址 + 16
,资源索引值也为 0x42
。
(lldb) register read General Purpose Registers: rax = 0x0000000000000000 rbx = 0xffffff800b473e28 kernel.development`kdebug_enable rcx = 0x00000000ffffffff rdx = 0x0000000000000000 rdi = 0xffffff80afa29300 rsi = 0xffffff80b0f8e470 rbp = 0xffffff8742023610 rsp = 0xffffff8742023610 r8 = 0x0000000000000229 r9 = 0xffffff800b2c4d00 kernel.development`zone_array + 8336 r10 = 0xffffff800b2c2c70 kernel.development`zone_array r11 = 0x0000000000000058 r12 = 0xffffff87299cb9b4 r13 = 0x0000000000000001 r14 = 0xffffff80b094e608 r15 = 0xffffff80b094e000 rip = 0xffffff7f8d7adc37 IOAcceleratorFamily2`IOAccelResource2::clientRelease(IOAccelShared2*) + 13 rflags = 0x0000000000010282 cs = 0x0000000000000008 fs = 0x00000000ffff0000 gs = 0x00000000afa20000 (lldb) x/20g $r12 0xffffff87299cb9b4: 0x00000364001a8c00 0x0000004200000042 0xffffff87299cb9c4: 0x0000104000000101 0x0055550000900002 0xffffff87299cb9d4: 0x0004000800040008 0x1048000000010001 0xffffff87299cb9e4: 0x0055560000900002 0x0002000800020008 0xffffff87299cb9f4: 0x0000000000010001 0x0000000000000000 0xffffff87299cba04: 0x0000000400000004 0x0000000000000000 0xffffff87299cba14: 0x0000000200000002 0x00000364001a8c00 0xffffff87299cba24: 0x0000004200000042 0x0000104800000101 0xffffff87299cba34: 0x0055560000900002 0x0002000800020008 0xffffff87299cba44: 0x1050000000010001 0x0055570000900002
0x04 缓解措施
攻击者可以使用双重释放漏洞来攻击未打补丁的macOS系统,成功在目标主机上获取较高权限。Apple已推出安全补丁,改进内存处理机制来修复内存破坏问题。macOS Mojave 10.14.4已经可以使用最新补丁,大家应当尽快 更新 系统。此外,大家也可以安装类似 Trend Micro Antivirus for Mac 以及 Trend Micro Protection Suites 之类的解决方案来检测并阻止使用利用各种缺陷的攻击行为。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 漏洞分析:OpenSSH用户枚举漏洞(CVE-2018-15473)分析
- 【漏洞分析】CouchDB漏洞(CVE–2017–12635, CVE–2017–12636)分析
- 【漏洞分析】lighttpd域处理拒绝服务漏洞环境从复现到分析
- 漏洞分析:对CVE-2018-8587(Microsoft Outlook)漏洞的深入分析
- 路由器漏洞挖掘之 DIR-815 栈溢出漏洞分析
- Weblogic IIOP反序列化漏洞(CVE-2020-2551) 漏洞分析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。