CVE-2019-8635:macOS double free

在macOS存在一个double free漏洞,这个漏洞由AMD组件的内存损坏所引起。如果成功被利用,攻击者可以完成权限提升并且可以再root权限下执行恶意代码。

漏洞简单介绍

其实这个cve包含2个相似的漏洞:

  1. discard_StretchTex2Tex方法。
  2. 还有一个存在一个类中AMD Radeon class(AMDRadeonX400_AMDSIGLContext)对sideband tokens的处理过程。它是IOAccelGLContext2的派生类,并从IOAccelGLContext2扩展。 这些类用于在macOS机器上渲染图形。具体是在
    AMDRadeonX4000_AMDSIGLContext类的AMDSIGLContext::process_StretchTex2Tex函数中。并且在打开AMDRadeonX4000_AMDGraphicsAccelerator 客户端的连接类型为1的情况下,这个类可以被AMDRadeonX4000_AMDSIGLContext的selector 2用户态函数IOAccelContext2::submit_data_buffers访问。

漏洞成因

下面简要介绍下这两个漏洞的成因:

  1. 第一个漏洞:AMDRadeonX4000_AMDSIGLContext discard_StretchTex2Tex double free漏洞
    这个漏洞可以在用户态触发,但是必须最低要求是能够在目标macOS 系统获取执行低权限的代码。因为没有对用户提供的数据合法验证,能够读取已分配的数据结构,攻击者利用此漏洞再结合其他的漏洞可以在内核态提升权限。
  2. 第二个漏洞:AMDRadeonX4000_AMDSIGLContext Double Free漏洞
    在同一个AMD组件类中,它是在对sideband token的处理过程中,也存在类似的一个double free漏洞。和上一个漏洞一样,是一个本地提权漏洞,必须在目标系统有执行低权限代码的能力,也就是个普通用户。具体是在函数AMDRadeonX4000_AMDSIGLContext::process_StretchTex2Tex中,在对对象操作前,没有对其进行有效的验证,攻击者可以利用此弱点,从内核态提升权限。
    本质上讲,就利用特点来看,两个漏洞可能是相似的,都是对对象在释放之前,没有对对象的数据来源和数据的值进行验证,可以在用户态直接映射内存就行修改索引,达到释放相同的对象,并且释放两次,引发漏洞。
    但具体调用的函数上有一些区别,我们看具体分析:


    CVE-2019-8635:macOS double free_第1张图片
    fig-1-pseudo-code-snippet-stretchtex2tex-function.png

    上图AMDRadeonX4000_AMDSIGLContext: discard_StretchTex2Tex函数的伪代码中我们看v10和v11是如何获取的,如果 (cmdinfo+32)等于0x8c00, 这个IOAccelResource v10和v11从IOAccelShared2(this+172)获取到,索引分别为*(shareMem_start_address_187_offset16+8) 和 *(shareMem_start_address_187_offset16+12),这个函数也会用IOAccelResource2::clientRelease()释放这两个加速资源。如果用户态映射相同的索引,lookupResource函数相当于从这两个索引中取值,客户端也会释放相同的对象两次,这就是漏洞所在、核心在于我们让(shareMem_start_address_187_offset16+8)和(shareMem_start_address_187_offset16+12)的值相同,而shareMem_start_address_187_offset16的区域可以被用户态控制和映射,通过修改而达到目的。
    第二个漏洞:


    CVE-2019-8635:macOS double free_第2张图片
    fig-1-pseudo-code-snippet-process-stretchtex2tex-function.png

    如上图函数AMDRadeonX4000_AMDSIGLContext::process_StretchTex2Tex的伪代码片段。结构和上一个漏洞很类似,如果v15==0x8c00, accelResource_offset8和accelResource_offset12会从IOAccelShared2获取到,索引为共享内存+24和共享内存+28的值。最后,这个函数会从 IOAccelShared2 _rst释放accelResource_offset12, 如果 accelResource_offset8->member2!=10, 这个函数也会从IOAccelShared2释放accelResource_offset8,这样的话,如果我们设置共享内存偏移24和28的位置为相同的值,将会释放相同得对象accelResource两次,引发了漏洞。
    在AMDRadeonX4000_AMDSIGLContext类里的process_StretchTex2Tex函数中,IOAccelResource2::clientRelease()将会释放这两个对象,而这两个对象accelResource2均来自accelShare2共享内存,对象通过IOAccelShared2::lookupResource函数以各自的索引值为参数来得到,这两个索引值可以被用户态通过 IOAccelContext2设置共享内存来控制。 如果用户态映射了两个相同的索引值,就会释放两次相同的对象。

用户态映射内存

接下来看IOAccelContext2::processSidebandBuffer:

CVE-2019-8635:macOS double free_第3张图片
fig-2-pseudo-code-snippet-ioaccelcontext2-processsideandbuffer.png

这两个漏洞的伪代码片段显示了共享内存指向了command stream info +24,这个command stream info buffer在IOAccelContext2::processSidebandBuffer设置,在图中,我们看到 v5=共享内存+16 ,并且v5赋值给了this->member196, this->member196也指向了commandStreamInfo+24。

CVE-2019-8635:macOS double free_第4张图片
fig-3-pseudo-code-snippet-ioaccelcontext2-clientmemoryfortype-function.png

上图是 IOAccelContext2::clientMemoryForType伪代码片段,被API IOConnectMapMemory64所调用。IOConnectMapMemory64函数可以映射用户态到内核态。当调用这个函数时,它的参数应该有连接对象,内存类型,和其他的参数。这里,我们把连接对象设置为IOAccelContext2的实例,内存类型为0,当设置了内存类型为0的时候,clientMemoryForType函数将会创建一个buffer,它的起始地址是这个变量“shareMem_start_vm_address_187”,也就是在IOAccelContext2::processSidebandBuffer命名的。
从这里开始,共享内存可以被控制,两个resource索引也可以被设置相同的,可以触发这个bug。

崩溃日志

接下来我们看一下应用crash的backtrace信息。“AMDRadeonX4000`AMDRadeonX4000_AMDSIGLContext::process_StretchTex2Tex”和“process_StretchTex2Tex(IOAccelCommandStreamInfo&) + 2893”的偏移会有所不同,如果discard_StretchTex2Tex被用来触发漏洞,也同样可以注意到。
当内核检测到系统错误时,这个错误会在内核代码段检测到,就会抛出没有解决的处理器异常,像无效内存地址引用,或者在调用链中的bug等等,mac系统就会产生kernel panic,加入log。我们可以看到所出现的panic log.

* 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
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

在log中也会看到寄存器信息,$r12指向共享内存+16的位置,为0xffffff87299cb9b4,这两个resource索引都为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 //here
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

苹果在 macOS Mojave 10.14.4已更新补丁
引用:

https://blog.trendmicro.com/trendlabs-security-intelligence/cve-2019-8635-double-free-vulnerability-in-apple-macos-lets-attackers-escalate-system-privileges-and-execute-arbitrary-code/

你可能感兴趣的:(CVE-2019-8635:macOS double free)