CVE-2018-4421
A memory corruption vulnerability exists in the IntelHD5000 kernel extension when dealing with graphics resources inside of OSX 10.13.4. A library inserted into the VLC media application can cause an out-of-bounds access inside of the KEXT leading to a use after free and invalid memory access in the context of the kernel. This can be used for privilege escalation.
OS X 10.13.4 - MacBookPro11,4
8.8 - CVSS:3.0/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
CWE-416: Use After Free
Apple supports multiple different GPU versions inside of OSX. With this functionality comes multiple different kernel extensions assigned to deal with the details of interaction between user space and the kernel to get the graphics buffers drawn effectively. On the retina Macbook Pro the provided GPU is the Apple Intel HD 5000 processor. Therefore this kernel extension is used in graphics rendering and processing throughout and is the subject to a use-after-free privilege escalation vulnerability. The vulnerability is also reachable from inside the Safari sandbox creating a larger potential attack surface.
A brief look at Apple kernel extensions shows that it uses a restricted subset language and a unique way of communication between user space and the kernel known as IOKit. Essentially an IOKit extension inherits from a UserClient class and registers its own methods to handle user interaction. There are also various types that can be passed in to connect to different UserClients stored under the same umbrella name. Upon connection, a port is returned and this port is forwarded through in all further communications. In the POC included, VLC is used to handle this basic connection and port setup.
The issue itself arises in the IntelAccelerator user client type 6, IGAccelSharedUserClient. The kernel extension is responsible for resource delegation in regards to graphics processing. The data stored and returned here gets passed through into the GPU for rendering and processing. There are multiple methods for allocating and deleting resources as well as creating shared memory with user space. The issue arises inside of the IOAccelSharedUserClient2::delete_resource(uint)
function. A snippet from this function is shown below.
int IOAccelSharedUserClient2::delete_resource(IOAccelSharedUserClient2 *this, unsigned int index)
{
IOGraphicsAccelerator2 *v2; // rbx
void *resource; // [rsp+8h] [rbp-28h]
IOGraphicsAccelerator2::lock_busy(v2);
(*(*v2 + 2128LL))(v2, "", 0LL);
if ( IOAccelNamespace::lookupId(*(*(this + 32) + 32LL), index, &resource) ) [0]
{
if ( *(resource + 20) != 10 )
IOAccelResource2::sharedRelease(resource) [1]
IOGraphicsAccelerator2::unlock_busy(v3);
IOLockUnlock(v3[17]);
}
A user supplied index value is passed in from user space and then used as an argument to the lookupId
function, [0]. By passing in a valid value but not the intended value an incorrect resource can be selected for release. This resource is then passed into the release function and it is subsequently removed. By selecting a specific resource to delete we can cause it to be reused although it has already been released. As stated above the resources used inside of the IGAccelSharedUserClient eventually get passed into the GPU for processing in the form of data buffers.
if(inputCnt > 0 && IGAccelSharedUserClient == connection && selector == 1){
//change resource to delete from 11 - 3
if(input[0] == 11 && times_called > 700 ) input[0] = 3; [4]
}
The code above is injected into a VLC process and sits inside the IOConnectCallMethod used to interact between userspace and the kernel. As can be seen, [4], converting the passed in argument from the value 11 to the value 3 causes this vulnerability to trigger. The function where the crash happens is below.
__int64 __fastcall IGAccelGLContext::process_token_IndirectStateBaseAddress(IOAccelContext2 *this, __int64 a2)
{
....
IGAccelResource::get_tex_data(object, &v61, &v62, 0); [2]
v14 = *(_QWORD *)&object->resource_obj_1;
if ( !v14 ) [3]
v14 = *(_QWORD *)&object->resource_obj_2;
v15 = (*(__int64 (__fastcall **)(__int64, unsigned int *))(*(_QWORD *)v14 + 288LL))(v14, &v61);
At location [2], a resource object is attained and accessed. Location [3] shows a check to verify the data is not null. Upon failure of this check the resource object is accessed again and a new value is returned. This returned value is then used directly without any further verification. This object is a reference to the object passed into delete above, [1], creating a use after free scenario. This can be leveraged by an attacker to execute arbitrary code in the context of the kernel. The attacker also has a large window of time to set up the attack as the execute function above is actually triggered from user space as well.
*** Panic Report ***
panic(cpu 2 caller 0xffffff800a4d7bc6): Kernel trap at 0xffffff7f8c977c55, type 14=page fault, registers:
CR0: 0x000000008001003b, CR2: 0x0000000000000000, CR3: 0x000000033279a06f, CR4: 0x00000000001627e0
RAX: 0xffffff8043696000, RBX: 0x0000000017b37000, RCX: 0x0000000000000000, RDX: 0xffffff8030875f00
RSP: 0xffffff91fcea38d0, RBP: 0xffffff91fcea3950, RSI: 0x0000000000000000, RDI: 0x0000000000000000
R8: 0xffffff7f8b61c5fa, R9: 0x0000000000000082, R10: 0x0000000000001000, R11: 0xffffff802fa9b800
R12: 0xffffff8043657608, R13: 0xffffff81d98f7030, R14: 0xffffff91fcea390c, R15: 0xffffff8043657000
RFL: 0x0000000000010246, RIP: 0xffffff7f8c977c55, CS: 0x0000000000000008, SS: 0x0000000000000010
Fault CR2: 0x0000000000000000, Error code: 0x0000000000000000, Fault CPU: 0x2, PL: 0, VF: 0
Backtrace (CPU 2), Frame : Return Address
0xffffff91fcea33a0 : 0xffffff800a37e1f6 mach_kernel : _handle_debugger_trap + 0x4e6
0xffffff91fcea33f0 : 0xffffff800a4e6b74 mach_kernel : _kdp_i386_trap + 0x164
0xffffff91fcea3430 : 0xffffff800a4d79da mach_kernel : _kernel_trap + 0x51a
0xffffff91fcea34a0 : 0xffffff800a3221f0 mach_kernel : trap_from_kernel + 0x26
0xffffff91fcea34c0 : 0xffffff800a37d8ea mach_kernel : _panic_trap_to_debugger + 0x20a
0xffffff91fcea35f0 : 0xffffff800a37d6bc mach_kernel : _panic + 0x5c
0xffffff91fcea3650 : 0xffffff800a4d7bc6 mach_kernel : _kernel_trap + 0x706
0xffffff91fcea37c0 : 0xffffff800a3221f0 mach_kernel : trap_from_kernel + 0x26
0xffffff91fcea37e0 : 0xffffff7f8c977c55 com.apple.driver.AppleIntelHD5000Graphics : __ZN16IGAccelGLContext38process_token_IndirectStateBaseAddressER24IOAccelCommandStreamInfo + 0x227
0xffffff91fcea3950 : 0xffffff7f8b602771 com.apple.iokit.IOAcceleratorFamily2 : __ZN15IOAccelContext221processSidebandBufferEP24IOAccelCommandDescriptorb + 0x111
0xffffff91fcea3990 : 0xffffff7f8b602c83 com.apple.iokit.IOAcceleratorFamily2 : __ZN15IOAccelContext218processDataBuffersEj + 0x55
0xffffff91fcea39c0 : 0xffffff7f8b60915a com.apple.iokit.IOAcceleratorFamily2 : __ZN17IOAccelGLContext218processDataBuffersEj + 0x324
0xffffff91fcea3a00 : 0xffffff7f8c97e096 com.apple.driver.AppleIntelHD5000Graphics : __ZN16IGAccelGLContext18processDataBuffersEj + 0xf4
0xffffff91fcea3a30 : 0xffffff7f8b6007cd com.apple.iokit.IOAcceleratorFamily2 : __ZN15IOAccelContext219submit_data_buffersEP33IOAccelContextSubmitDataBuffersInP34IOAccelContextSubmitDataBuffersOutyPy + 0x499
0xffffff91fcea3ac0 : 0xffffff800aa67fd9 mach_kernel : _shim_io_connect_method_structureI_structureO + 0x1c9
0xffffff91fcea3b20 : 0xffffff800aa66160 mach_kernel : __ZN12IOUserClient14externalMethodEjP25IOExternalMethodArgumentsP24IOExternalMethodDispatchP8OSObjectPv + 0x340
0xffffff91fcea3b70 : 0xffffff800aa6ed67 mach_kernel : _is_io_connect_method + 0x217
0xffffff91fcea3cb0 : 0xffffff800a48c124 mach_kernel : __Xio_connect_method + 0x174
0xffffff91fcea3dc0 : 0xffffff800a383ca7 mach_kernel : _ipc_kobject_server + 0x127
0xffffff91fcea3e10 : 0xffffff800a356cad mach_kernel : _ipc_kmsg_send + 0x10d
0xffffff91fcea3e60 : 0xffffff800a371a9b mach_kernel : _mach_msg_overwrite_trap + 0x37b
0xffffff91fcea3ef0 : 0xffffff800a4bff8a mach_kernel : _mach_call_munger64 + 0x23a
0xffffff91fcea3fa0 : 0xffffff800a3229f6 mach_kernel : _hndl_mach_scall64 + 0x16
Kernel Extensions in backtrace:
com.apple.iokit.IOAcceleratorFamily2(378.18.1)[BAA0383C-9650-3934-B04A-69008F757A2C]@0xffffff7f8b5fb000->0xffffff7f8b691fff
dependency: com.apple.driver.AppleMobileFileIntegrity(1.0.5)[54CD88E5-9FD7-30FC-89A0-E4B2D0CE6F85]@0xffffff7f8b574000
dependency: com.apple.iokit.IOSurface(211.12)[E998B85B-3174-3C25-B82B-C0D8BD9720E5]@0xffffff7f8b589000
dependency: com.apple.iokit.IOPCIFamily(2.9)[1850E7DA-E707-3027-A3AA-637C80B57219]@0xffffff7f8ac94000
dependency: com.apple.iokit.IOGraphicsFamily(519.15)[D5F2A20D-CAB0-33B2-91B9-E8755DFC34CB]@0xffffff7f8b5a5000
com.apple.driver.AppleIntelHD5000Graphics(10.3.2)[18CF1D18-BC94-3F6A-8711-8921CE16E38B]@0xffffff7f8c93c000->0xffffff7f8c9d9fff
dependency: com.apple.iokit.IOSurface(211.12)[E998B85B-3174-3C25-B82B-C0D8BD9720E5]@0xffffff7f8b589000
dependency: com.apple.iokit.IOPCIFamily(2.9)[1850E7DA-E707-3027-A3AA-637C80B57219]@0xffffff7f8ac94000
dependency: com.apple.iokit.IOGraphicsFamily(519.15)[D5F2A20D-CAB0-33B2-91B9-E8755DFC34CB]@0xffffff7f8b5a5000
dependency: com.apple.iokit.IOAcceleratorFamily2(378.18.1)[BAA0383C-9650-3934-B04A-69008F757A2C]@0xffffff7f8b5fb000
BSD process name corresponding to current thread: VLC
Boot args: debug=0x146 kdp_match_name=en6 kext-dev-mode=1 pmuflags=1 -v keepsyms=1
Mac OS version:
17E199
Kernel version:
Darwin Kernel Version 17.5.0: Fri Apr 13 19:32:32 PDT 2018; root:xnu-4570.51.2~1/DEVELOPMENT_X86_64
Kernel UUID: 8202BF9D-0810-353F-9453-77740F1F2D33
Kernel slide: 0x000000000a000000
Kernel text base: 0xffffff800a200000
__HIB text base: 0xffffff800a100000
System model name: MacBookPro11,4 (Mac-06F11FD93F0323C5)
Attached is the proof of concept. This needs to be built as shared libraries using the command in the comments at the head of the file. These libraries are then injected into VLC with the provided movie as an argument. These instructions are also included at the head of each of the files. I have tested with VLC versions 3.0.2 and 2.2.4 with successful results. Other builds may differ.
2018-05-30 - Vendor disclosure
2018-12-24 - Vendor patched and released update
2019-01-03 - Public release
Discovered by Tyler Bohan of Cisco Talos.