在macOS存在一個(gè)double free漏洞,這個(gè)漏洞由AMD組件的內(nèi)存損壞所引起。如果成功被利用,攻擊者可以完成權(quán)限提升并且可以再root權(quán)限下執(zhí)行惡意代碼。
漏洞簡單介紹
其實(shí)這個(gè)cve包含2個(gè)相似的漏洞:
- discard_StretchTex2Tex方法。
- 還有一個(gè)存在一個(gè)類中AMD Radeon class(AMDRadeonX400_AMDSIGLContext)對(duì)sideband tokens的處理過程。它是IOAccelGLContext2的派生類,并從IOAccelGLContext2擴(kuò)展。 這些類用于在macOS機(jī)器上渲染圖形。具體是在
AMDRadeonX4000_AMDSIGLContext類的AMDSIGLContext::process_StretchTex2Tex函數(shù)中。并且在打開AMDRadeonX4000_AMDGraphicsAccelerator 客戶端的連接類型為1的情況下,這個(gè)類可以被AMDRadeonX4000_AMDSIGLContext的selector 2用戶態(tài)函數(shù)IOAccelContext2::submit_data_buffers訪問。
漏洞成因
下面簡要介紹下這兩個(gè)漏洞的成因:
- 第一個(gè)漏洞:AMDRadeonX4000_AMDSIGLContext discard_StretchTex2Tex double free漏洞
這個(gè)漏洞可以在用戶態(tài)觸發(fā),但是必須最低要求是能夠在目標(biāo)macOS 系統(tǒng)獲取執(zhí)行低權(quán)限的代碼。因?yàn)闆]有對(duì)用戶提供的數(shù)據(jù)合法驗(yàn)證,能夠讀取已分配的數(shù)據(jù)結(jié)構(gòu),攻擊者利用此漏洞再結(jié)合其他的漏洞可以在內(nèi)核態(tài)提升權(quán)限。 -
第二個(gè)漏洞:AMDRadeonX4000_AMDSIGLContext Double Free漏洞
在同一個(gè)AMD組件類中,它是在對(duì)sideband token的處理過程中,也存在類似的一個(gè)double free漏洞。和上一個(gè)漏洞一樣,是一個(gè)本地提權(quán)漏洞,必須在目標(biāo)系統(tǒng)有執(zhí)行低權(quán)限代碼的能力,也就是個(gè)普通用戶。具體是在函數(shù)AMDRadeonX4000_AMDSIGLContext::process_StretchTex2Tex中,在對(duì)對(duì)象操作前,沒有對(duì)其進(jìn)行有效的驗(yàn)證,攻擊者可以利用此弱點(diǎn),從內(nèi)核態(tài)提升權(quán)限。
本質(zhì)上講,就利用特點(diǎn)來看,兩個(gè)漏洞可能是相似的,都是對(duì)對(duì)象在釋放之前,沒有對(duì)對(duì)象的數(shù)據(jù)來源和數(shù)據(jù)的值進(jìn)行驗(yàn)證,可以在用戶態(tài)直接映射內(nèi)存就行修改索引,達(dá)到釋放相同的對(duì)象,并且釋放兩次,引發(fā)漏洞。
但具體調(diào)用的函數(shù)上有一些區(qū)別,我們看具體分析:
fig-1-pseudo-code-snippet-stretchtex2tex-function.png
上圖AMDRadeonX4000_AMDSIGLContext: discard_StretchTex2Tex函數(shù)的偽代碼中我們看v10和v11是如何獲取的,如果 (cmdinfo+32)等于0x8c00, 這個(gè)IOAccelResource v10和v11從IOAccelShared2(this+172)獲取到,索引分別為*(shareMem_start_address_187_offset16+8) 和 *(shareMem_start_address_187_offset16+12),這個(gè)函數(shù)也會(huì)用IOAccelResource2::clientRelease()釋放這兩個(gè)加速資源。如果用戶態(tài)映射相同的索引,lookupResource函數(shù)相當(dāng)于從這兩個(gè)索引中取值,客戶端也會(huì)釋放相同的對(duì)象兩次,這就是漏洞所在、核心在于我們讓(shareMem_start_address_187_offset16+8)和(shareMem_start_address_187_offset16+12)的值相同,而shareMem_start_address_187_offset16的區(qū)域可以被用戶態(tài)控制和映射,通過修改而達(dá)到目的。
第二個(gè)漏洞:
fig-1-pseudo-code-snippet-process-stretchtex2tex-function.png
如上圖函數(shù)AMDRadeonX4000_AMDSIGLContext::process_StretchTex2Tex的偽代碼片段。結(jié)構(gòu)和上一個(gè)漏洞很類似,如果v15==0x8c00, accelResource_offset8和accelResource_offset12會(huì)從IOAccelShared2獲取到,索引為共享內(nèi)存+24和共享內(nèi)存+28的值。最后,這個(gè)函數(shù)會(huì)從 IOAccelShared2 _rst釋放accelResource_offset12, 如果 accelResource_offset8->member2!=10, 這個(gè)函數(shù)也會(huì)從IOAccelShared2釋放accelResource_offset8,這樣的話,如果我們?cè)O(shè)置共享內(nèi)存偏移24和28的位置為相同的值,將會(huì)釋放相同得對(duì)象accelResource兩次,引發(fā)了漏洞。
在AMDRadeonX4000_AMDSIGLContext類里的process_StretchTex2Tex函數(shù)中,IOAccelResource2::clientRelease()將會(huì)釋放這兩個(gè)對(duì)象,而這兩個(gè)對(duì)象accelResource2均來自accelShare2共享內(nèi)存,對(duì)象通過IOAccelShared2::lookupResource函數(shù)以各自的索引值為參數(shù)來得到,這兩個(gè)索引值可以被用戶態(tài)通過 IOAccelContext2設(shè)置共享內(nèi)存來控制。 如果用戶態(tài)映射了兩個(gè)相同的索引值,就會(huì)釋放兩次相同的對(duì)象。
用戶態(tài)映射內(nèi)存
接下來看IOAccelContext2::processSidebandBuffer:

這兩個(gè)漏洞的偽代碼片段顯示了共享內(nèi)存指向了command stream info +24,這個(gè)command stream info buffer在IOAccelContext2::processSidebandBuffer設(shè)置,在圖中,我們看到 v5=共享內(nèi)存+16 ,并且v5賦值給了this->member196, this->member196也指向了commandStreamInfo+24。

上圖是 IOAccelContext2::clientMemoryForType偽代碼片段,被API IOConnectMapMemory64所調(diào)用。IOConnectMapMemory64函數(shù)可以映射用戶態(tài)到內(nèi)核態(tài)。當(dāng)調(diào)用這個(gè)函數(shù)時(shí),它的參數(shù)應(yīng)該有連接對(duì)象,內(nèi)存類型,和其他的參數(shù)。這里,我們把連接對(duì)象設(shè)置為IOAccelContext2的實(shí)例,內(nèi)存類型為0,當(dāng)設(shè)置了內(nèi)存類型為0的時(shí)候,clientMemoryForType函數(shù)將會(huì)創(chuàng)建一個(gè)buffer,它的起始地址是這個(gè)變量“shareMem_start_vm_address_187”,也就是在IOAccelContext2::processSidebandBuffer命名的。
從這里開始,共享內(nèi)存可以被控制,兩個(gè)resource索引也可以被設(shè)置相同的,可以觸發(fā)這個(gè)bug。
崩潰日志
接下來我們看一下應(yīng)用crash的backtrace信息。“AMDRadeonX4000`AMDRadeonX4000_AMDSIGLContext::process_StretchTex2Tex”和“process_StretchTex2Tex(IOAccelCommandStreamInfo&) + 2893”的偏移會(huì)有所不同,如果discard_StretchTex2Tex被用來觸發(fā)漏洞,也同樣可以注意到。
當(dāng)內(nèi)核檢測到系統(tǒng)錯(cuò)誤時(shí),這個(gè)錯(cuò)誤會(huì)在內(nèi)核代碼段檢測到,就會(huì)拋出沒有解決的處理器異常,像無效內(nèi)存地址引用,或者在調(diào)用鏈中的bug等等,mac系統(tǒng)就會(huì)產(chǎn)生kernel panic,加入log。我們可以看到所出現(xiàn)的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中也會(huì)看到寄存器信息,$r12指向共享內(nèi)存+16的位置,為0xffffff87299cb9b4,這兩個(gè)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已更新補(bǔ)丁
引用:

