poc
-
用于在Chrome里检测是否存在漏洞
<html>
<script>
function log(str){
document.write("<p>" + str + "</p>");
}
print = console.log;
const arr = new Uint32Array([2**31]);
function foo() {
return (arr[0] ^ 0) + 1;
}
log(foo());//-2147483647
for(let i=0;i<10000;i++){
print(foo());
}
log(foo());//2147483649
</script>
</html>
如出现不一致,则代表存在漏洞。
-
用于漏洞调试
const arr = new Uint32Array([2**31]);
function foo() {
return (arr[0] ^ 0) + 1;
}
%PrepareFunctionForOptimization(foo);
print(foo());
%OptimizeFunctionOnNextCall(foo);
print(foo());
->
-2147483647
2147483649
root cause
runtime执行
arr[0]是unsigned int32 = 2**31
= 2147483648 = 0x8000 0000
->
arr[0] ^ 0
会转成signed int32 = 2**31^0
= 0x8000 0000 = -2147483648
->
(arr[0] ^ 0) + 1
会转成signed int64,按理说是先符号拓展,得到0xFFFF FFFF 8000 0000,然后再加一,得到0xFFFF FFFF 8000 0001 = -2147483647
JIT compiler
如图可以看出我们之前的分析是合理的。
但因为JIT的x64指令选择存在问题,所以在为ChangeInt32ToInt64 IR生成汇编时会对0x8000 0000进行零拓展,得到0x0000 0000 8000 0000,然后再加一,得到0x0000 0000 8000 0001 = 2147483649
看一下这个问题是什么
case MachineRepresentation::kWord32:
- opcode = load_rep.IsSigned() ? kX64Movsxlq : kX64Movl;
+ // ChangeInt32ToInt64 must interpret its input as a _signed_ 32-bit
+ // integer, so here we must sign-extend the loaded value in any case.
+ opcode = kX64Movsxlq;
从补丁可以看出,存在漏洞的逻辑是根据load_rep.IsSigned()
来选择opcode是kX64Movsxlq还是kX64Movl指令,前者是符号拓展,后者是零拓展。
而load_rep
其实是来自于图上的LoadTypedElement
节点,而这个节点的符号是Unsigned32的,所以会选择kX64Movl指令,最终导致(arr[0] ^ 0) + 1
计算出的结果出错。
如有错漏,请多指教(捂脸
exploit
该漏洞利用的依然是根据类型推断的值,和实际执行的值的误差来完成漏洞利用的。chrome对于typer漏洞做了大量的加固,但仍有绕过,本篇仅分析漏洞,关于漏洞利用请移步阅读。
-
https://github.com/r4j0x00/exploits/tree/master/chrome-0day -
https://faraz.faith/2021-01-07-cve-2020-16040-analysis/ -
https://doar-e.github.io/blog/2020/11/17/modern-attacks-on-the-chrome-browser-optimizations-and-deoptimizations/ -
https://doar-e.github.io/blog/2019/05/09/circumventing-chromes-hardening-of-typer-bugs/
end
招新小广告
ChaMd5 Venom 招收大佬入圈
新成立组IOT+工控+样本分析 长期招新