
تقنيات استغلال ChakraCore
CVE-2016-7190 [0] هو تجاوز في الكومة (heap overflow) في دالة Array.map() لمحرك ChakraCore يسمح بالكتابة فوق الذاكرة المجاورة. الفكرة الرئيسية للحصول على وصول عشوائي للقراءة والكتابة هي أولاً تخصيص عدد من المصفوفات الصحيحة المتتالية في JavaScript، واستغلال التجاوز للتلاعب بحجم إحدى المصفوفات. بعد ذلك، نستفيد من هذه المصفوفة لتغيير العنوان الأساسي لـ Uint8Array إلى أي عنوان نريد قراءة/كتابته. سبب هذه الخطوة الإضافية هو أن التجاوز محدود بـ 2x حجم المصفوفة الصحيحة المملوءة.
هذه الثغرة مناسبة جدًا لاختبار استراتيجيات استغلال مختلفة لأنه يمكن إعادة إدخالها بسهولة في الإصدارات الحالية من ChakraCore (انظر undo-cve-2016-7190.patch).
في هذا المثال، نقوم باختطاف تدفق التحكم عن طريق الكتابة فوق مؤشر الجدول الافتراضي (vtable pointer) لكائن Uint8Array من C++ إلى عنوان ذاكرة مُتحكم فيه، واستدعاء دالة من كائنات Uint8Array مما يؤدي إلى استدعاء دالة افتراضية.
سلامة تدفق التحكم (CFI) هي تقنية دفاعية للتخفيف من هجمات اختطاف تدفق التحكم. الفكرة العامة لـ CFI هي حساب رسم بياني لتدفق التحكم (CFG) لتطبيق ما أثناء وقت الترجمة، ثم تزويد التطبيق بفحوصات وقت التشغيل لضمان أن تدفق التحكم لا ينحرف عن CFG المحسوب إحصائيًا أثناء وقت التشغيل. ومع ذلك، إذا كان أحد التطبيقات، مثل متصفح الويب، يدعم التوليد الديناميكي للكود، فيجب أن يكون CFG قابلاً للتوسيع أثناء وقت التشغيل. يفرض هذا عددًا من التحديات:
خلال فترة إجراء بحثنا، ركزنا على التحدي الأخير. الفكرة الرئيسية هي التلاعب بمدخلات (بيانات) المترجم في الوقت المناسب (JIT). ونتيجة لذلك، سيقوم مترجم JIT بتوليد كود ضار يتم دمجه بعد ذلك في السياق الحالي — وحتى يتم تقويته بـ CFI. بالتزامن مع بحثنا، أظهرت theori [4] كيفية تجاوز CFI عن طريق التلاعب بمخرجات مترجم JIT. يتم تخفيف هذا الهجوم عن طريق التحقق من سلامة المخرجات من خلال مجموع اختباري (checksum). هذا لا يمكن أن يوقف هجومنا لأننا نتلاعب بمدخلات مترجم JIT. ومع ذلك، من خلال الاستعانة بمصادر خارجية لترجمة JIT إلى عملية أخرى (تُعرف بـ Arbitrary Code Guard [3]) يتم تخفيف كلا الهجومين.
نوضح أن المهاجم يمكنه استغلال هجوم يعتمد على البيانات فقط ضد مترجم JIT لتوليد كود أصلي عشوائي. على وجه الخصوص، نقوم بتعديل التمثيل الوسيط (IR) لمترجم JIT لحقن تعليمات يتحكم فيها المهاجم. عندما يقوم مترجم JIT بعد ذلك بتوليد الكود الأصلي بناءً على IR المعدل، فإنه سينشئ كودًا أصليًا يتحكم فيه المهاجم.
بين وقت إجراء البحث الأصلي والآن، تم تغيير IR في ChakraCore. لم نبذل جهدًا في نقل الهجوم، لذلك، للتجربة مع هذا الهجوم، يرجى استخدام الملفات الثنائية التالية.
أثناء تحليل ACG [3] لاحظنا، من بين أمور أخرى [1,2]، أن البيانات العالمية للقراءة فقط، المخزنة في القسم .mrdata، يجب تعديلها لدمج الكود المولد ديناميكيًا. يتم ذلك من خلال دالة LdrProtectMrdata() التي تستخدم قفلًا لجعلها آمنة للخيوط. ومع ذلك، يتم أولاً إعادة تعيين القسم .mrdata كقابل للكتابة قبل الحصول على القفل. المثير للاهتمام أن القسم .mrdata يحتوي على العنوان الأساسي وحجم القسم .mrdata والذي يُستخدم لاحقًا لإعادة تعيين هذا القسم مرة أخرى كقابل للقراءة فقط.
يمكن للمهاجم استغلال هذا لإنشاء بدائية هجوم تسمح بإعادة تعيين الذاكرة المقروءة فقط كقابلة للكتابة. لذلك، ينفذ المهاجم الخطوات التالية:
.mrdata.mrdata كقابل للكتابة.mrdata.mrdata قابلاً للكتابة. لتعيين ذاكرة عشوائية كقابلة للكتابة، يحتاج المهاجم فقط إلى تغيير العنوان الأساسي للقسم .mrdata، ثم تكرار الخطوات المذكورة أعلاه.نتيجة هذا الهجوم هي أن البيانات الموثوقة سابقًا، أي البيانات المعينة كقابلة للقراءة فقط، تصبح غير موثوقة. في إثبات المفهوم الخاص بنا، نستغل هذه البدائية لتجاوز Control-flow Guard (CFGuard) عن طريق تغيير المؤشر إلى دالة التحقق الخاصة بـ CFGuard المحفوظة في _guard_dispatch_icall_fptr. يمكن ملاحظة التأثير عن طريق إرفاق المصحح وتغيير متغير bypass_cfguard.
[0] https://bugs.chromium.org/p/project-zero/issues/detail?id=923
[1] http://alex-ionescu.com/publications/euskalhack/euskalhack2017-cfg.pdf
[2] https://sites.google.com/site/bingsunsec/dataonlyattack
[3] https://blogs.windows.com/msedgedev/2017/02/23/mitigating-arbitrary-native-code-execution/
[4] http://theori.io/research/chakra-jit-cfg-bypass