
دليل إثبات تعليمي لـ CVE-2024-4947، وهي ثغرة التباس نوعي في V8 Maglev، توضح سلسلة كاملة من المشغّل إلى تنفيذ تعسفي للكود على إصدار d8 غير معزول.
سلسلة استغلال كاملة ومكتفية بذاتها لثغرة CVE-2024-4947 (التباس النوع في V8 Maglev)، بدءًا من المشغِّل الأولي للتباس النوع وصولًا إلى تنفيذ تعسفي للكود. يعمل الـ PoC ضد بناء غير معزول لـ d8 ويطبع CVE-2024-4947-PWNED على stdout كدليل على تنفيذ الكود.
⚠️ هذا PoC بحثي / تعليمي لثغرة عامة وتم إصلاحها. يستهدف بناءً تطويريًا لصدفة V8 (
d8 --allow-natives-syntax) — وليس متصفح Chrome حقيقيًا. راجع إخلاء المسؤولية.
$ ./v8-build-nosandbox.sh # build the vulnerable d8 (WSL2 Ubuntu, ~10-20 min)
$ d8 --allow-natives-syntax --module exploit/exploit_rce.mjs
[engine] dblData0=0004f470 class=0
[inst] addr=0x001dc274 trusted_data(tagged)=0x00202bd5 td=0x00202bd4
[jt] jump_table_start = 0x00000967426cd000 (external code space)
[bridge] memory0_start -> jt, memory0_size -> huge (r/w reach jt+off)
[slot0] before: e9 3b 08 00 00
[shell] wrote 52 bytes @ jt+0x0100: VERIFIED
CVE-2024-4947-PWNED
$ echo $? # 0
الـ shellcode ذو الـ 52 بايت هو write(1, "CVE-2024-4947-PWNED\n", 20); exit_group(0).
CVE-2024-4947 هي ثغرة التباس نوع في مترجم V8 Maglev JIT، تم إصلاحها في Chrome 125.0.6422.60 (الالتزام b3c01ac1e60a). تم استغلالها في البرية من قِبل مجموعة Lazarus APT. عندما يترجم Maglev عملية تخزين إلى كائن فضاء أسماء الوحدات (module namespace object) (JSModuleNamespace)، فإنه يستخدم AccessInfo غير صحيح — إذ تُترجم عملية التخزين إلى تعليمة mov [[obj + 4], rax] بسيطة تكتب قيمة مُتحكَّمًا فيها في حقل map لكائن مجاور، بدلًا من المرور عبر مسار تخزين الخصائص السليم.
يسير الاستغلال وفق الخطوات التالية:
NAME_DICTIONARY_TYPE (0xB2)، الأمر الذي يغيّر المكان الذي يخزّن فيه V8 تجزئة الكائن.new WeakRef(...) بحيث تقع كتابة التجزئة في فتحة الطول (length) لكائن مجاور → وصول خارج الحدود.ومن هنا نحصل على مجموعة أدوات V8 الكلاسيكية: addrOf/fakeObj، ومن ثم قراءة/كتابة تعسفية داخل القفص.
CVE-2024-4947 trigger (fake NAME_DICTIONARY map + WeakRef hash write)
└─► OOB write ─► corrupt doubleArray length
└─► in-cage 4/8-byte arbitrary R/W (the "engine")
└─► overwrite WasmTrustedInstanceData.memory0_start (+0x18)
& memory0_size (+0x20) → huge
└─► wasm load8_u / store8 = clean 64-bit R/W bridge
(no software bounds check in compiled code)
└─► read jump_table_start (+0x38) — external code space
└─► jump table region is RWX (this build)
└─► write shellcode into the slack (jt+0x100)
└─► repoint func0's `e9 rel32` slot at it
└─► call func0 → shellcode → RCE
| # | المرحلة | التفاصيل |
|---|---|---|
| 1 | المشغِّل | opt() تكتب خريطة مزوّفة عبر عملية التخزين المربكة؛ new WeakRef(m) يجعل corruptArray.length خارج الحدود. |
| 2 | المحرك | addrOf/fakeObj؛ إفساد طول doubleArray → قراءة/كتابة تعسفية من 4 بايتات عند أي عنوان مُحاذى لأربعة بايتات داخل القفص. |
| 3 | جسر 64-بت | WasmTrustedInstanceData.memory0_start (+0x18) هو مؤشر 64-بت خام تستخدمه تعليمات wasm المترجمة i32.load8_u/i32.store8 بدون فحص حدود برمجي (الخروج عن النطاق يصطدم بصفحة حراسة → SIGSEGV → مصيدة wasm). أعد توجيهه إلى أي عنوان واجعل memory0_size (+0x20) ضخمًا → قراءة/كتابة تعسفية 64-بت. |
| 4 | العثور على مساحة الكود | jump_table_start (+0x38) هو مؤشر 64-بت خام إلى مساحة الكود الخارجية (خارج قفص الـ 4 جيجابايت). |
| 5 | كتابة الـ shellcode | في هذا البناء، تكون منطقة جدول القفزات RWX (V8 يرقّع المداخل وقت التشغيل): اكتب الـ shellcode ذا الـ 52 بايت في المساحة الفارغة عند jt+0x100. |
| 6 | إعادة توجيه الفتحة | فتحة func0 في جدول القفزات هي قفزة نسبية من 5 بايتات بصيغة e9 <rel32> (الهدف = الفتحة + 5 + rel32). اضبط rel32 → jt+0x100. |
| 7 | تفعيل التوزيع | inst.exports.r(0) → يوزّع JSToWasmWrapper عبر الفتحة 0 → يُنفَّذ الـ shellcode. |
راجع docs/walkthrough.md للتوثيق الفني الكامل، بما في ذلك النتائج التجريبية التي تقف خلف كل صف.
.
├── exploit/
│ ├── Module.mjs # module namespace object corrupted by the trigger
│ ├── exploit_rce.mjs # the full chain (trigger → arbitrary R/W → RCE)
│ └── shellcode.S # assembly source for the 52-byte payload
├── build/
│ └── v8-build-nosandbox.sh # build the vulnerable no-sandbox d8 from V8 source
└── docs/
└── walkthrough.md # deep dive: bridge mechanics, layouts, gotchas
يستورد الـ exploit ملف Module.mjs (كائن فضاء أسماء الوحدات الهشّ) من دليله الخاص، لذا أبقِ الملفين معًا (أو عدّل مسار import).
git ومساحة قرص فارغة تبلغ ~10 جيجابايتdepot_tools (git clone https://chromium.googlesource.com/chromium/tools/depot_tools)# 1. fetch V8 at the vulnerable tag (12.4.254.16 is the pre-fix release)
export PATH="$HOME/depot_tools:$PATH"
cd ~/v8w && fetch v8 && cd v8
git checkout 12.4.254.16 # or the commit just before b3c01ac1e60a
# 2. first build a normal release d8 (needed to seed args.gn), then:
./v8-build-nosandbox.sh # copies args.gn and appends v8_enable_sandbox = false
يستخدم v8-build-nosandbox.sh الأمر ninja -C out.gn/x64.release_nosandbox -j6 d8.
out.gn/x64.release_nosandbox/d8 --allow-natives-syntax --module exploit/exploit_rce.mjs
ينتهي الإخراج المتوقَّع بما يلي:
CVE-2024-4947-PWNED
وتخرج العملية بحالة 0.
العلامة
--allow-natives-syntaxمطلوبة لاستدعاءات وقت التشغيل%PrepareFunctionForOptimization/%OptimizeMaglevOnNextCall. لهذا السبب لا يمكن تشغيل الـ PoC في متصفح حقيقي — فهو إثبات مفهوم لصدفة d8 بحكم تصميمه.
القطع القابلة لإعادة الاستخدام فعلًا (وغير البديهية) في أبحاث استغلال V8:
JSTypedArray.external_pointer على منطقة مستهدفة، أعد توجيه WasmTrustedInstanceData.memory0_start بدلًا من ذلك. لا تحمل تعليمات تحميل/تخزين البايت المترجمة في wasm أي فحص حدود برمجي؛ ومعالج إشارات المصيدة يحوّل فقط أخطاء صفحات الحراسة إلى مصيدة، لذا فإن المناطق المُعيَّنة لكن غير القابلة للكتابة تتعطل بدلًا من التحول إلى مصيدة.try/catch؛ فمصيدة "خارج الحدود" في wasm تعني صفحة حراسة (غير مُعيَّنة)، بينما يعني SIGSEGV الحقيقي منطقة مُعيَّنة لكن بصلاحيات RX فقط.e9 rel32 — قفزة نسبية من 5 بايتات، وليست صيغة movabs التي تفترضها كثير من الكتابات. تم التحقق منها مقابل التفكيك الناتج عن --print-wasm-code.هذا المستودع مُقدَّم لأغراض البحث التعليمي والدفاعي فقط. يوضّح استغلال ثغرة تم إصلاحها ضد بناء V8 خاص بالتطوير فقط. وهو ليس سلاحًا ضد Chrome الحديث، ولا يتجاوز صندوق حماية V8، ويتطلب العلامة غير الافتراضية --allow-natives-syntax. المؤلف غير مسؤول عن أي إساءة استخدام.
الأولية المشغِّلة (trigger primitive) (خريطة NAME_DICTIONARY_TYPE مزوّفة + كتابة تجزئة WeakRef) تتبع التحليلات المنشورة لاستغلال Lazarus في البرية بعد الإصلاح — بما في ذلك المقالات التحليلية لكل من Google وExodus Intelligence حول CVE-2024-4947. أما المراحل اللاحقة (اختبار جدول القفزات، الجسر، ترقيع الفتحات) فقد تم اشتقاقها تجريبيًا ضد هذا البناء المحدد أثناء هذا العمل. أما مفهوم خطة رشّ JIT الاحتياطية فيستند إلى تقنيات استغلال V8 المنشورة (مثل نمط make_array في CVE-2024-5830).
MIT — انظر LICENSE.