
شرح لنسخة Sleirsgoevy من تنفيذ الاستغلال CVE-2018-4386 بواسطة Fire30 والمُسمّى Bad_Hoist
[!Note] معلومات أساسية حول PS4:
تتميز وحدة التحكم PlayStation 4 بوحدة معالجة مركزية AMD x86-64 مخصصة (8 أنوية)، ونظام تشغيلها Orbis مبني على FreeBSD (v9.0) مع أجزاء من NetBSD أيضًا. كما يتضمن مجموعة واسعة من البرامج مفتوحة المصدر الإضافية، مثل Mono VM وWebKit.
إن متصفح الإنترنت المستخدم في PS4 مبني في الواقع على مشروع WebKit مفتوح المصدر. وهو محرك التخطيط مفتوح المصدر الذي يعرض صفحات الويب في متصفحات iOS وWii U و3DS وPS Vita وPS4.
يتكون متصفح الإنترنت في PS4 في الواقع من عمليتين منفصلتين. العملية التي نختطفها لتنفيذ التعليمات البرمجية هي WebKit Core Process (التي تتولى تحليل HTML وCSS، وفك تشفير الصور، وتنفيذ JavaScript على سبيل المثال). أما العملية الأخرى فتتعامل مع كل شيء آخر: عرض الرسومات، واستقبال إدخالات وحدة التحكم، وإدارة السجل والإشارات المرجعية، وما إلى ذلك.
يستخدم متصفح WebKit في PS4 عدة مخصِّصات للذاكرة (Heap Allocators)، يخدم كلٌّ منها مكوناتٍ مختلفة. وهي كالتالي:
جوهر CVE-2018-4386 هو خلل منطقي في محرك JavaScriptCore (JSC) الخاص بـ WebKit (v605.1.15)، وهو الإصدار المستخدم في برنامج PS4 الثابت 6.XX. يكمن الخلل في دالة BytecodeGenerator::hoistSloppyModeFunctionIfNecessary ويتعلق بمعالجة غير صحيحة لرفع المتغيرات (hoisting) في JavaScript بالوضع المتراخي (sloppy mode)، وتحديدًا داخل حلقات for-in.
المكوّن القابل للاستغلال (ForInContext):
الشيء الذي نستهدفه بشكل أساسي هو ForInContext. وهو بنية داخلية يستخدمها JavaScriptCore لإدارة حالة حلقة for-in، إذ يتتبع متغير التكرار الحالي ومجموعة الخصائص التي يتم تعدادها.
عندما يتم رفع إعلان دالة داخل حلقة for-in، يجب على المحرك إبطال كائن ForInContext المرتبط إذا تمت الكتابة فوق متغير التكرار. ومع ذلك، وبسبب الخلل، لا يحدث هذا الإبطال. وهذا يسمح باستبدال متغير التكرار بكائن اعتباطي. وعلى الرغم من ذلك، يستمر المحرك في التعامل مع المتغير كاسم خاصية نصي.
عندما يتم استدعاء معالج البايت كود op_get_direct_pname لاحقًا، فإنه يستخدم متغير التكرار مباشرةً ككائن نصي، دون إجراء فحص للنوع.
من خلال تمرير كائن مصمم بعناية بدلاً من سلسلة نصية، مما يؤدي إلى التباس في النوع (type confusion)، نتمكن من استغلال هذا الأمر بطريقة تحقق إفسادًا في الذاكرة، وحتى بدائيات استغلال مفيدة مثل addrof وfakeobj وقراءة/كتابة عشوائية.
معرف البنية (Structure ID): كل كائن في JavaScriptCore، بما في ذلك التمثيلات الداخلية مثل WTF::StringImpl، له معرف بنية (أو وسم نوع) يخبر المحرك بنوع الكائن وكيفية تفسير حقوله.
التباس النوع (Type Confusion): يستغل استغلالنا ثغرة CVE-2018-4386 لجعل كائن JavaScript يُفسَّر ككائن StringImpl. هذا هو الغرض من دالة create_impl()، التي تُرجع كائنًا ملتبس النوع من نوع WTF::StringImpl، ويمكن بعد ذلك تمريره إلى دالة trigger()، باعتباره الكائن الاعتباطي الذي تحدثنا عنه سابقًا في جزء آلية الثغرة من هذا الشرح.
ومع ذلك، لكي يعمل هذا، يجب أن يكون تخطيط الذاكرة ومعرف البنية "قريبين بما يكفي" لما يتوقعه المحرك لكائن نصي حقيقي.
JSString::toIdentifier() داخليًا؟توجد طريقة في محرك WebKit JavaScriptCore الخاص بمتصفح إنترنت PS4 اسمها JSString::toIdentifier(). تقوم هذه الطريقة بتحويل كائن سلسلة نصية في JavaScript (JSString) إلى تمثيل داخلي من نوع Identifier.
يُستخدم هذا الـ Identifier في جميع أنحاء المحرك لمقارنة أسماء الخصائص وأسماء المتغيرات والسلاسل النصية الأخرى التي يجب أن يُشار إليها بسرعة وبشكل متكرر بواسطة محرك JavaScript، وتخزينها والبحث فيها بكفاءة.
تتحقق من أن الكائن سلسلة نصية صالحة وأن معرف البنية الخاص به يطابق ما يتوقعه المحرك لكائن نصي. إذا كانت السلسلة عبارة عن حبل (rope) أي سلسلة ناتجة عن تسلسل سلاسل، فقد تقوم بتسويتها قبل التحويل. ثم تسترجع إما Identifier موجودًا مسبقًا للسلسلة أو تنشئ واحدًا جديدًا إذا لم يكن موجودًا.
يُستخدم هذا الـ Identifier بعد ذلك داخليًا لإجراء عمليات بحث سريعة عن الخصائص والمتغيرات.
JSString::toIdentifier()عندما يدخل الاستغلال في حلقة for التي تتكرر 1024 مرة، ينشئ كل تكرار كائن WTF::StringImpl جديدًا ملتبس النوع مع 32 معرف بنية جديدًا تُرجعه دالة create_impl(). عندما يُستخدم هذا الكائن الملتبس النوع ويُمرَّر إلى trigger() باعتباره الكائن الاعتباطي، يستدعي JSC الدالة JSString::toIdentifier() عليه.
تفحص JSString::toIdentifier() بتات معينة في معرف البنية للتأكد من أن الكائن سلسلة نصية صالحة أو يمكن التعامل معه كواحدة. من خلال توليد العديد من الكائنات بتخطيطات ومعرفات بنية مختلفة، يزيد الاستغلال من فرص أن يكون لدى كائن واحد على الأقل معرف بنية يجتاز الفحوصات الداخلية في JSString::toIdentifier().