Apple Mac OSX / iOS – Multiple Kernel Uninitialized Variable Bugs Leading to Code Execution Vulnerabilities

  • 作者: Google Security Research
    日期: 2016-01-28
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/39358/
  • Source: https://code.google.com/p/google-security-research/issues/detail?id=618
    
    The _ool variations of the IOKit device.defs functions all incorrectly deal with error conditions.
    
    If you run the mig tool on device.defs you can see the source of the kernel-side MIG handling code; here
    is the relevant generated code for io_service_get_matching_services_ool:
    
    mig_internal novalue _Xio_service_get_matching_services_ool
    (mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP)
    {
    
    ... // some typedefs
    
    Request *In0P = (Request *) InHeadP;
    Reply *OutP = (Reply *) OutHeadP;
    
    kern_return_t RetCode;
    io_object_t existing; <-- (a)
    
    ... // check the input types
    
    RetCode = is_io_service_get_matching_services_ool(In0P->Head.msgh_request_port, (io_buf_ptr_t)(In0P->matching.address), In0P->matchingCnt, &OutP->result, &existing);<-- (b)
    
    if (RetCode != KERN_SUCCESS) {
    MIG_RETURN_ERROR(OutP, RetCode);
    }
    
    OutP->existing.name = (mach_port_t)iokit_make_object_port(existing); <-- (c)
    
    
    At (a) it declares an io_object_t existing on the stack (io_object_t is just a pointer.) It then passes the address of that local to is_io_service_get_matching_services_ool, and if that
    function succeeds passes the value of existing to iokit_make_object_port. Here's is_io_service_get_matching_services_ool (which importantly is NOT generated code):
    
    /* Routine io_service_get_matching_services_ool */
    kern_return_t is_io_service_get_matching_services_ool(
    mach_port_t master_port,
    io_buf_ptr_t matching,
    mach_msg_type_number_t matchingCnt,
    kern_return_t *result,
    io_object_t *existing )
    {
    kern_return_t kr;
    vm_offset_t data;
    vm_map_offset_t map_data;
    
    kr = vm_map_copyout( kernel_map, &map_data, (vm_map_copy_t) matching );
    data = CAST_DOWN(vm_offset_t, map_data);
    
    if( KERN_SUCCESS == kr) {
    // must return success after vm_map_copyout() succeeds
    *result = internal_io_service_get_matching_services(master_port,
    (const char *) data, matchingCnt, existing);
    vm_deallocate( kernel_map, data, matchingCnt );
    }
    
    return( kr );
    }
    
    Note here that it returns kr which *only* indicates if the vm_map_copyout failed. This will of course succeed so the return value of this function
    will always be KERN_SUCCESS, even if internal_io_service_get_matching_services fails... Let's look at that function:
    
    static kern_return_t internal_io_service_get_matching_services(
     mach_port_t master_port,
     const char * matching,
     mach_msg_type_number_t matching_size,
     io_iterator_t *existing )
    {
    kern_return_t kr;
    OSObject *obj;
    OSDictionary *dict;
    
    if( master_port != master_device_port)
    return( kIOReturnNotPrivileged);
    
    obj = matching_size ? OSUnserializeXML(matching, matching_size)
    : OSUnserializeXML(matching);
    if( (dict = OSDynamicCast( OSDictionary, obj))) {
    *existing = IOService::getMatchingServices( dict );
    kr = kIOReturnSuccess;
    } else
    kr = kIOReturnBadArgument;
    
    if( obj)
    obj->release();
    
    return( kr );
    }
    
    Indeed, if this function fails it doesn't set existing to a safe value but does return an error code. However, the _ool variation ignores this error code (it
    just returns it to userspace via the result parameter.) This means that the generated code thinks that is_io_service_get_matching_services_ool succeed
    and it therefore pass existing in iokit_make_object_port which will eventually (if the uninitialized value wasn't NULL) call a virtual function on it
    (taggedRetain) when adding the object to the dictionary storing all iokit user objects.
    
    All of the _ool variations of IOKit API's have this problem; PoCs are included for all of them but they may or may not crash depending on the
    state of the stack.
    
    
    Proof of Concept:
    https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/39358.zip