كسر حماية Switch 2: سلسلة الثغرات التي تجاوزت Secure Boot
سلسلة ثغرات جمعت تجاوز مفسّر BASIC وخللاً في مسار نظام الملفات وسباقاً في المحمّل. ماذا تعلّمنا عن سلاسل الاستغلال وSecure Boot؟

يأتي الجهاز الجديد ومعه نواة صغيرة (microkernel) محصّنة، ومصادقة للمؤشرات على كل عنوان عودة، وذاكرة للتنفيذ فقط، وتحقق من تدفق التحكم، وسلسلة إقلاع موقّعة. بعد أسبوعين، يشغّل هاوٍ كوداً أصلياً تعسفياً بصلاحيات خدمة نظام الملفات، وهي من أكثر العمليات ثقةً على الجهاز. هذه قصة أول كسر حماية عام لجهاز Switch 2، ويجب أن تُقلق كل مهندس يبني أنظمة آمنة ولو قليلاً. ليس لأن الحمايات فشلت، فمعظمها عمل كما صُمّم، بل لأن الاستغلال لم يحتج إلى مواجهتها مباشرة. التفّ حولها، خطأً منطقياً تلو الآخر.
عملت لسنوات في الجانب الدفاعي من أمن البنية التحتية، والنمط هنا مألوف لي من مراجعات الحوادث في بيئات الإنتاج أكثر مما أودّ: سلسلة من الأخطاء المتواضعة فردياً، يكسب كل منها صلاحية تكفي للوصول إلى الحلقة التالية. لم تكن نقطة الدخول الأولى داخل Switch 2 نفسه، بل كانت خللاً في إدارة الذاكرة داخل لعبة من Switch 1، وهي مفسّر SmileBASIC، يعمل في وضع التوافق الخلفي. ومن هناك صعدت السلسلة عبر خدمة نظام الملفات، واستغلت سباقاً في محمّل البرامج، وانتهت بالقدرة على تشغيل كود أصلي بهوية أي sysmodule. يستحق كل رابط نظرة، لأن كل واحد منها يطابق خطأً رأيت فرقاً يرتكبه في أنظمة الخوادم أيضاً.
لماذا أُغلق باب المتصفح، ولم يُغلق باب الألعاب
WebKit هو الهدف الكلاسيكي الأول لكسر حماية الأجهزة، وقد حصل الباحث على موطئ قدم هناك عبر بوابة الأسر (captive portal) وثغرة n-day. لكنه توقف بسرعة. مكدّس المتصفح في Switch 2 ملفوف بثلاث حمايات تتكامل بشكل قاسٍ: ذاكرة التنفيذ فقط (XOM) تعني أنه حتى مع القراءة والكتابة التعسفية لا يمكنك تحديد موقع أدوات برمجية مفيدة؛ ومصادقة المؤشرات (PAC) تعني أنك لا تستطيع تزوير مؤشر كود دون آلية توقيع؛ وCFI تعني أن مؤشراً موقّعاً بصحة لن يجتاز فحوص الأنواع في معظم نقاط الاستدعاء. كسر أي واحدة منها مشروع بحثي، وكسر الثلاث مجتمعة، متسلسلةً من بيئة JavaScript المعزولة، يعني مسيرة مهنية كاملة.
لذلك تحوّل الهجوم إلى أضعف مستأجر على الجهاز: ألعاب Switch 1 التي تطوّرها جهات خارجية. كان المنطق باردًا وصحيحاً. تُبنى ملفات الألعاب من قِبل الاستوديوهات لا من فريق الأمن في Nintendo، لذلك يقل احتمال تجميعها مع مكدّس الحماية الكامل، وجهاز Switch 1 لم يحتوِ على PAC أصلاً، فأي شيء يعمل في بيئة توافقه يرث هذا الضعف. والأفضل من ذلك أن جذر الثقة في Switch 1 اختُرق قبل سنوات، ما يعني أن كود الألعاب يمكن فك تشفيره وتدقيقه دون اتصال. كان SmileBASIC، وهو مفسّر BASIC ضخم مكتوب بلغة C++ وقد استُغل سابقاً على 3DS، الهدف الطري الواضح.
هناك درس هنا يتجاوز الأجهزة: وضعك الأمني هو وضع أقل الشيفرات تحصيناً التي توافق على تشغيلها. التوافق الخلفي سطح هجوم ترثه ولا تختاره. تعلّم نظام أمان Xbox هذا بالطريقة الصعبة في الألفية الجديدة، إذ كانت ثغرات ملفات الحفظ في عناوين غير محصّنة هي المدخل الذي كسر نواةً أكثر إحكاماً، وهي قصة تناولناها في ما يعلّمه اختراق Xbox الأصلي عن نماذج أمان العتاد. بعد عشرين عاماً، ما زالت الحركة نفسها تنجح.
تجاوز عدد صحيح يفتح الباب الأول
الخلل في SmileBASIC مثال كلاسيكي على تجاوز عدد صحيح في فحص الحدود، وهو النوع الذي تقول كل قائمة مراجعة للشيفرة الآمنة إنه يجب اكتشافه، ومع ذلك يستمر في الوصول إلى الإنتاج. كان أمر إزالة عناصر المصفوفة في المفسّر يتحقق من معاملاته على النحو التالي:
// intent: ensure the removed range is inside the array
if (position + count <= array.length) {
memmove(backing + position * 8, // start of the gap
backing + (position + count) * 8, // elements after the gap
(length - position) * 8); // bytes to copy
}
استدعِ REMOVE R, &H60000000, &H3FFFFFFF على مصفوفة من أربعة عناصر، فيلتفّ position + count إلى 0x9FFFFFFF، وهي قيمة سالبة بعمق كعدد موقّع 32 بت، أي أصغر بكثير من الطول. كما تلتف عمليات الضرب في 8، وينتهي بك الأمر إلى تنفيذ memmove(backing, backing - 8, 32). يطبع الأمر بعد ذلك خطأ نفاد الذاكرة، لكن النسخ قد حدث بالفعل. والجزء الجميل هنا: البايتات الثماني السابقة مباشرة لأي مصفوفة في SmileBASIC تحمل مؤشراً إلى المصفوفة نفسها. بعد الإزالة، يُفسَّر R[0] كنص يكون حقل طوله هو ذلك المؤشر فعلياً، أي نص طويل بشكل فلكي يمكنك القراءة منه والكتابة إليه. قراءة وكتابة تعسفية، مُقدَّمة كهدية من تخطيط الذاكرة الخاص بالمفسّر نفسه.
إذا كنت تكتب C أو C++ لكسب الرزق، فتأمّل ذلك الفحص لحظة. position + count <= length هو أكثر نمط شائع لفحص الحدود في الشيفرة النظامية، وهو خاطئ كلما أمكن أن يفيض المجموع. الصيغة الصحيحة هي position <= length && count <= length - position، أو الحساب المُتحقَّق منه. وجدت نسخاً من هذا الخلل في محلّل حزم في وظيفة سابقة، وفي محرك تخزين مفتوح المصدر. ينجو الخلل من مراجعة الشيفرة لأنه يبدو صحيحاً بوضوح. هذه فجوة المواصفة والتنفيذ مصغّرةً: تقول المواصفة «تحقّق من النطاق»، ويتحقق التنفيذ من شيء مجاور للنطاق، والفرق بينهما قابل للاستغلال. كتبنا عن أن فجوة المواصفة والتنفيذ هي مكمن الأخطاء؛ ومفسّرات الأجهزة هي فقط المكان الذي يجدها فيه مراهقون متحمسون بدلاً من أدوات الـfuzzing.
التصعيد إلى خدمة نظام الملفات بواسطة تجاوز في معالجة المسارات
القراءة والكتابة داخل عملية لعبة في فضاء المستخدم لا تفيدك وحدها، فتصميم النواة الصغيرة في Switch يعني أن كل خدمة عملية منفصلة لها سطح IPC خاص بها. كان الهدف القيّم هو fs، وحدة نظام الملفات المدمجة، لسبب تكراري لذيذ: fs هو المكوّن الذي يقدّم الكود المفكوك التشفير إلى المحمّل. إذا امتلكت fs استطعت تفريغ كود كل sysmodule آخر للتحليل دون اتصال. حارس الحصن نفسه يسلّمك المخططات.
كان الخلل فيضاناً في الكومة (heap overflow) داخل تطبيع المسارات. أمر IPC باسم GetEntryType يطبّع وسيط المسار، والمسارات التي تحتوي على مكوّن الدليل الأب مع شرطة مائلة عكسية، أي /..\، تُوجَّه إلى دالة مساعدة تخصص مخزناً ثابتاً بحجم 0x301 بايت، ثم تنسخ المسار إليه دون فحص كافٍ للطول. مسار طويل بما يكفي يفيض فوق كائنات الكومة المجاورة. وكان تعليق الباحث على اكتشاف ذلك كلمة واحدة: «lol».
ينبغي أن تؤلمك تلك الـ«lol»، فتطبيع المسارات من النوع الروتيني غير اللامع الذي لا يحظى أبداً بالتدقيق الأمني الذي تحظى به الشيفرة التشفيرية. حوّل الاستغلال الفيضان إلى قراءة وكتابة تعسفية كاملة، بإفساد كائن تخزين مؤقت مجاور تُعيد بتّته dirty bit توجيه عمليات الإدخال والإخراج عبر مؤشر يتحكم فيه المهاجم، وهي أداة تهيئة كومة نظيفة. ثم، بالبحث في الذاكرة، وجد الاستغلال كائن الاتصال الخاص بعمليته داخل جداول fs، وأعاد كتابة معرّف العملية المخزّن ليصبح 1، فمنح عملية اللعبة صلاحية «FullPermission» على نظام الملفات. لاحظ ما حدث: لم يُكسر أي فحص تشفيري واحد. كان نموذج الصلاحيات مفروضاً ببيانات في الذاكرة، والمهاجم يستطيع الكتابة في الذاكرة.

تجاوز فحوص التوقيع بتبديل نظام الملفات أثناء التنفيذ
أذكى حلقة في السلسلة هي التي تتجاوز توقيع الشيفرة دون لمس أي مفتاح. يتحقق محمّل البرامج (ldr) من توقيعين قبل تشغيل أي شيء: توقيع Nintendo على بيانات صلاحيات العملية والمفتاح الخاص بالمطوّر، وتوقيع المطوّر على تجزئة ترويسة أرشيف المحتوى. نظرياً، تثبّت هذه التجزئة بشكل غير مباشر كل بايت في الملف التنفيذي. عملياً، هناك فجوة: وظيفة fs هي التأكد من أن نظام الملفات الذي تقدمه يطابق التجزئة التي أبلغت عنها سلفاً، وكان لدى المهاجم قراءة وكتابة تعسفية داخل fs.
لذلك ينتظر الاستغلال حتى يطلب المحمّل ملف نظام الملفات التنفيذي وتكون fs قد سلّمته التجزئة والتوقيع الصحيحين، ثم يبدّل كائن نظام الملفات خلف مقبض IPC الخاص بالمحمّل بكائن مصطنع يقدّم ملف البيانات الوصفية الموقّع الأصلي، لكن مع ملف تنفيذي بديل من اختيار المهاجم. تنجح فحوص التوقيع لأنها كانت فحوصاً على بايتات لم يحتج المهاجم أصلاً إلى تعديلها. ويُفرض التوقيت بإعادة كتابة المقبض الذي تنتظره fs لاتصال المحمّل إلى حدث يتحكم فيه المهاجم من عملية اللعبة، فيحوّل حالة السباق (race condition) إلى تدخّل حتمي.
كانت السلسلة التشفيرية سليمة من تجزئة الترويسة إلى محتويات الملف التنفيذي، على الورق. الحلقة الوحيدة التي كانت مجرد افتراض وقت التشغيل، وهو «العملية التي تقدّم الملفات أمينة»، هي التي انكسرت.
هذه مشكلة TOCTOU (وقت الفحص مقابل وقت الاستخدام) ترتدي معطف مطر، وهي أكثر الدروس قابلية للنقل في هذا التحليل. كلما تحقق المكوّن A من البيانات ثم عمل المكوّن B عليها لاحقاً عبر مرجع قابل للتغيير، تكون أمام نافذة تبديل. راجعت مرة نظام نشر داخلياً يتحقق من تجزئة صورة حاوية، ثم يسحب الصورة بالوسم من السجل وقت الإطلاق، وهي المشكلة نفسها بقبعة مختلفة. والإصلاح في الحالتين واحد: تحقّق عند نقطة الاستخدام، أو سلّم المستهلك كائناً ثابتاً ومتحققاً منه بدلاً من مقبض إلى شيء عليه أن يثق بطرف ثالث ليبقيه أميناً. التصميم الدفاعي الذي يفترض أن كل وسيط مخترق ليس بارانويا؛ على جهاز يستطيع المهاجم الكتابة فيه جزئياً أصلاً، إنه نمذجة دقيقة. هذه هي الحجة المحورية في الهندسة الدفاعية للأنظمة المقاومة للكوارث، وهذا الاستغلال دراسة حالة نظيفة لها.
استعارة مقبض من النواة وترقيع fs من الداخل
الحيلة الأخيرة صغيرة وجميلة. لترقيع الكود الخاص بعملية fs، احتاج الاستغلال إلى مقبض نواة إليها. حصل عليه بالكتابة فوق حقل مقبض بحجم 4 بايت في الذاكرة بالقيمة السحرية 0xffff8001، التي تفسرها النواة على أنها «مقبض العملية الحالية». استدعت عملية اللعبة واجهة برمجية حميدة، فاستلمت مقبض عمليتها الخاص، وأرسلته إلى عملية مارقة تعمل بصلاحيات sysmodule المسؤول عن تحميل الشيفرة، فاستخدمت تلك العملية استدعاءات نواة شرعية لربط كود fs وترقيعه. كانت كل عملية فردية مصرّحاً بها. أما التركيب فكان اختراقاً كاملاً لأكثر العمليات ثقةً على الجهاز.
إذا بدا ذلك مألوفاً، فذلك لأن مشكلة «النائب المربك» (confused deputy) منتشرة في كل مكان ما إن تبدأ بالبحث: قواعد sudo التي تسمح بملف ثنائي حميد مع ملف إعدادات قابل للكتابة، أو أدوار IAM في السحابة تسمح بتمرير نفسها إلى خدمة أقوى، أو أنظمة بناء توقّع كل ما تسلّمه مهمة CI. تناولنا جانب تصعيد الصلاحيات في هذا النمط في تاريخ تصعيد الصلاحيات في Unix، تتغير الآليات لكن شكل الخلل لا يتغير. المقابض والقدرات والرموز لا تكون أقوى من سلامة الذاكرة التي تخزّنها.
ما يخطئ فيه مصمّمو الإقلاع الآمن باستمرار
تراجع خطوة إلى الوراء فيتضح نمط ثابت عبر السلسلة كلها، وهو النمط نفسه وراء معظم الاختراقات المؤسسية الخطيرة التي راجعتها: صمد اللب المتحقَّق منه، وسقط كل ما حوله. كانت ROM الإقلاع في Nintendo، ومخطط التوقيع، وحمايات النواة سليمة. ما سقط كان فحص حدود في المفسّر، ومخزن تطبيع مسارات، وحقل صلاحيات مخزّن في ذاكرة قابلة للكتابة، ونافذة TOCTOU بين خدمتين موثوقتين، وجدول مقابض يأخذ مؤشرات النواة من بنى قابلة للكتابة من مساحة المستخدم. لا شيء من ذلك غريب، وكلها أشياء تنجو من المراجعة لأن كلاً منها يبدو معقولاً محلياً.
- دقّق الكود المملّ. مطبّعات المسارات وفحوص الأطوال وجداول المقابض هي حيث تبدأ سلاسل الاستغلال فعلياً، لا في التشفير. اجعل وقت المراجعة متناسباً مع مدى وصول المهاجم، لا مع تعقيد الخوارزمية.
- تعامل مع كل فجوة في الحماية كخريطة طريق. إذا كانت عملية واحدة في النظام تفتقر إلى PAC/CFI، سواء كانت طبقة توافق أو ملفاً ثنائياً من طرف ثالث أو مفسّراً قديماً، فافترض أنها نقطة الدخول وانمذج ما الذي يمكنها الوصول إليه.
- ألغِ الثقة القائمة على الموقع. مكوّن يتحقق من البيانات ثم يسلّم مقبضاً قابلاً للتغيير إلى ما بعده قد خلق نافذة تبديل. مرّر كائنات ثابتة وذاتية التحقق، أو أعد التحقق عند الاستخدام.
- افترض أن الذاكرة القابلة للكتابة في أي مكان هي قابلة للكتابة في كل مكان. أعلام الصلاحيات وحقول PID وقيم المقابض المخزنة في ذاكرة يصل إليها المهاجم جزء من سطح هجومك، لا آلية الإنفاذ الخاصة بك.
الخلاصة غير المريحة الأخرى تتعلق بالأدوات. طُوّر الاستغلال بواسطة وكيل ذكاء اصطناعي منحته وصولاً مادياً إلى العتاد عبر أجهزة KVM-over-IP: فيديو داخل، ومدخلات وحدة التحكم عبر USB، وأداة ذراع Bluetooth تضغط زر الطاقة بعد كل انهيار. عملت عدة نسخ من الوكيل على أسطح هجوم مختلفة بالتوازي، على مدار الساعة، لمدة أسبوعين. مهما كان رأيك في أبحاث الأمن المدعومة بالذكاء الاصطناعي، فقد تغيّر اقتصاد اكتشاف الثغرات في العتاد المغلق. الأخطاء التي كانت تحتاج خبيراً صبوراً صارت تحتاج خبيراً صبوراً يستطيع الإشراف على ثلاثة مساعدين لا يتعبون. إذا كان نموذج التهديد لديك يفترض أن «لا أحد سيكلف نفسه عكس هندسة هذا sysmodule»، فقد انتهى ذلك الافتراض هذا الشهر.
السلسلة هي المنتج
من المغري أن تقرأ مقال كسر الحماية كقائمة بأخطاء Nintendo. اقرأه بدلاً من ذلك كمرآة. كل نظام ساعدت في الدفاع عنه كان فيه نظير لـ SmileBASIC (المكوّن القديم الذي لم يُحصَّن أحد)، ونظير لمخزن المسار (شيفرة الأنابيب التي تجاوزت افتراضاتها)، ونظير لسباق المحمّل (التحقق الذي وثق بوسيط قابل للتغيير). ميزة المهاجمين ليست في العبقرية في أي خطوة منفردة، بل في أنهم يفكرون بالسلاسل، بينما يفكر المدافعون بالمكوّنات.
إذن التمرين لفريقك هذا الأسبوع ليس «هل يستطيع أحد تزوير توقيعاتنا». بل: اختر خدمتك الداخلية الأكثر ثقة، وافترض أن المهاجم يملك بالفعل كتابة تعسفية في أقل العمليات تحصيناً التي تتحدث معها، ثم اتبع السلسلة إلى الأمام. أين يقع أول تصعيد؟ وماذا تتيح تلك الهوية الوصول إليه؟ ستجد نظام fs الخاص بك قريباً بما يكفي. الأفضل أن تجده أنت قبل أن يجده شخص يملك KVM وذراع Bluetooth وأسبوعين من الصبر.


