
مشكلة أمنية في البرنامج الثابت (firmware) لمُشغّل الأجهزة الافتراضية (hypervisor) في بعض شرائح Qualcomm الأقدم
CVE-2022-22063 هي مشكلة أمنية في البرنامج الثابت للمُشغِّل (hypervisor) لبعض شرائح Qualcomm الأقدم. يمكن إساءة استخدام مكوّن عتادي غير محمي (مُعاد التوجيه عند الإقلاع "boot remapper") للحصول على وصول كامل للقراءة/الكتابة إلى المُشغِّل من نظام تشغيل معدَّل (تصعيد صلاحيات). استغلال الثغرة أمر تافه على المنصات المتأثرة، إذ لا يتطلب معرفة بإصدار البرنامج الثابت المحدد (مثل العناوين أو المتغيرات).
ملاحظة: على الرغم من أن Qualcomm قدّمت إصلاحات للعملاء (مع متّسع من الوقت لإصدار التحديثات)، فإن العديد من الأجهزة المتأثرة قديمة بالفعل وقد لا تتلقى الإصلاح من البائع. لا يمكن استغلال الثغرة إلا من نظام تشغيل معدَّل أو مخترَق (باستخدام ثغرة أمنية أخرى). قد يكون إبقاء نظام التشغيل محدّثًا وآمنًا كافيًا حتى لو كان البرنامج الثابت عرضة للثغرة.
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:Hنُشرت الثغرة أيضًا في [النشرة الأمنية لشهر ديسمبر 2022 من Qualcomm].
تعتمد الثغرة على مزيج من العتاد والبرمجيات التي تعمل على الهدف المتأثر:
hyp على وحدة التخزين الداخلية).APCS_BOOT_START_ADDR_NSEC)، وهي غير محمية بواسطة المُشغِّل، وبالتالي يمكن الوصول إليها من نواة نظام التشغيل الأقل صلاحية (مثل Linux).هناك العديد من الشرائح الأخرى التي يحتمل أن تحتوي على العتاد المتأثر (مثل MSM8909 وMSM8953)، لكنها لا تحتوي على برنامج ثابت منفصل للمُشغِّل يمكن اختراقه.
تصعيد الصلاحيات: بوجود نواة نظام تشغيل مخترقة بالفعل (مثل Linux)، تسمح الثغرة بتصعيد الصلاحيات بسهولة إلى مستوى المُشغِّل (EL1 -> EL2 على ARM). يمكن قراءة أو كتابة جميع الذاكرة التي يديرها المُشغِّل. يؤدي هذا إلى كسر عزل مجالات الأمان المختلفة أو الأجهزة الافتراضية التي يديرها المُشغِّل (إن وُجدت، اعتمادًا على الإعدادات).
(انظر أيضًا: [مقدمة إلى التحكم في الوصول على منصات Qualcomm Snapdragon])
الإقلاع الآمن: تستخدم معظم أجهزة Qualcomm المتوفرة في الإنتاج الإقلاع الآمن لمنع التعديل غير المصرح به للبرنامج الثابت. البرنامج الثابت موقَّع تشفيريًا ويتم التحقق منه بواسطة سلسلة الإقلاع. تسمح الثغرة بتعديل أو حتى استبدال البرنامج الثابت للمُشغِّل المُحمَّل بالكامل أثناء التشغيل من نظام تشغيل معدَّل (إما عبر "فتح قفل مُحمِّل الإقلاع" المدعوم رسميًا أو عبر ثغرة أخرى).
(انظر أيضًا: [النظرة الفنية العامة للإقلاع الآمن والتحقق من صورة Qualcomm (الإصدار 1.0)] و(الإصدار 2.0))
ملاحظة: عُثر على الثغرة في الأصل على منصة Qualcomm Snapdragon 410 (MSM8916). قد تكون بعض الشروحات التالية خاصة بمنصة MSM8916، على سبيل المثال:
ومع ذلك، ينطبق المفهوم العام بشكل مشابه على جميع المنصات المتأثرة.
يعرّف معمار ARMv8-A 64-بت 4 مستويات صلاحية ("مستويات استثناءات"، EL). توجد مستويات منفصلة تُستخدم عادةً للتطبيقات ونوى أنظمة التشغيل والمُشغِّل:
تتبدل وحدة المعالجة المركزية بين المستويات أثناء الاستثناءات، على سبيل المثال بسبب انقطاع وارد. من الممكن أيضًا التبديل بين بعض المستويات باستخدام تعليمات خاصة، مثل استدعاء المُشغِّل (hvc).
(انظر أيضًا: [نموذج استثناءات AArch64])
يمكن للمُشغِّل استضافة جهاز افتراضي واحد أو أكثر مع نوى أنظمة تشغيل منفصلة. يمكن إعطاء كل جهاز افتراضي عرضه الخاص من الذاكرة باستخدام الترجمة من المرحلة الثانية. تمر جميع عمليات الوصول إلى الذاكرة من جهاز افتراضي عبر مرحلتين من الترجمة: الأولى تُدار بواسطة نظام التشغيل (الافتراضي)، بينما تُدار المرحلة الثانية بواسطة المُشغِّل. يمكن إخفاء الذاكرة المستخدمة من قبل المُشغِّل أو الأجهزة الافتراضية الأخرى بحذفها من جداول الترجمة.
(انظر أيضًا: [محاكاة AArch64], [إدارة ذاكرة AArch64])
يعمل البرنامج الثابت للمُشغِّل من Qualcomm في EL2 ويستخدم الترجمة من المرحلة الثانية لمنع الوصول إلى ذاكرة المُشغِّل من نواة نظام التشغيل الرئيسية التي تعمل في EL1 (عادةً Linux). لاحظ أن الترجمة من المرحلة الثانية في هذا الإعداد تُستخدم بشكل أساسي لحماية الذاكرة، دون ترجمة عناوين. يحصل نظام التشغيل الرئيسي على وصول مباشر إلى معظم مكوّنات العتاد في مساحة الإدخال/الإخراج المعيّنة في الذاكرة (MMIO)، مثل وحدة تحكم SD أو نظام الكاميرا. الوصول إلى الذاكرة التي تنتمي إلى المُشغِّل/EL2 (hyp) والمراقب الآمن/EL3 (جزء من tz) مقيد:
لا علاقة لمُعاد التوجيه عند الإقلاع بالمحاكاة الافتراضية: فهو مطلوب أثناء الإقلاع المبكر لنواة وحدة المعالجة المركزية. على منصة العتاد هذه، تبدأ نوى وحدة المعالجة المركزية التنفيذ دائمًا عند العنوان 0x0. مُعاد التوجيه عند الإقلاع هو مكوّن عتادي إضافي مبني حول وحدة المعالجة المركزية يعيد توجيه أول 64 أو 128 كيبي بايت (0x00000 - 0x20000) إلى منطقة ذاكرة قابلة للإعداد.
افتراضيًا، يشير مُعاد التوجيه عند الإقلاع إلى ذاكرة الإقلاع (أول كود يعمل عند تشغيل الجهاز). لاحقًا يتم تغيير التعيين بحيث تبدأ نوى وحدة المعالجة المركزية الأخرى التنفيذ فورًا في البرنامج الثابت EL3 (جزء من tz) الذي تم تحميله في ذاكرة الوصول العشوائي:
لاحظ كيف أن العنوان الذي تصل إليه وحدة المعالجة المركزية (داخل tz) يمكن الوصول إليه باستخدام عنوانين فيزيائيين مختلفين: العنوان الحقيقي في ذاكرة الوصول العشوائي (0x8650xxxx) والعنوان المعاد توجيهه باستخدام مُعاد التوجيه عند الإقلاع (0x0000xxxx).
يوجد في الواقع مثيلان منفصلان لمُعاد التوجيه عند الإقلاع:
APCS_BOOT_START_ADDR_SEC (= 0x0b010004) ولكن فقط في الحالة الآمنة.APCS_BOOT_START_ADDR_NSEC (= 0x0b010008)، حتى في الحالة غير الآمنة.يمكن إعداد كلا مثيلي مُعاد التوجيه عند الإقلاع باستخدام سجل ذاكرة يحتوي على العنوان الأساسي للمنطقة المعاد توجيهها وبتّي إعدادات: REMAP_EN لتفعيل إعادة التوجيه وBOOT_128KB_EN لإعادة توجيه أول 128 كيبي بايت بدلًا من 64 كيبي بايت فقط.
(انظر أيضًا: [دليل مرجعي فني لـ Qualcomm Snapdragon 410E مراجعة D], الصفحتان 85 و116)
باستخدام المعرفة من القسمين السابقين، الفكرة الأساسية بسيطة: استخدم مُعاد التوجيه عند الإقلاع لتجاوز حماية الذاكرة (الترجمة من المرحلة الثانية) الخاصة بالمُشغِّل.
مُعاد التوجيه عند الإقلاع لا يعمل فقط أثناء بدء تشغيل وحدة المعالجة المركزية. يمكن استخدامه في أي وقت ويتيح وصولًا كاملًا للقراءة/الكتابة/التنفيذ إلى المنطقة المعاد توجيهها. أيضًا، يبدو أن مُشغِّل Qualcomm لا يمنع نظام التشغيل من إعداد والوصول إلى المثيل غير الآمن لمُعاد التوجيه عند الإقلاع على الأجهزة المتأثرة (فهو غير محمي باستخدام الترجمة من المرحلة الثانية). لذلك، من السهل استغلال الثغرة عن طريق:
hyp الذي يبدأ عند العنوان 0x8640xxxx)، متبوعًا بـيمكن إزاحة المنطقة المعاد توجيهها ديناميكيًا (كتلة بكتلة) للوصول إلى مناطق ذاكرة أكبر من 64/128 كيبي بايت المتاحة عبر مُعاد التوجيه عند الإقلاع. من الممكن أيضًا استخدام هذا لتعطيل حماية ذاكرة المُشغِّل بالكامل (انظر إثبات المفهوم).
ملاحظة: لا يعمل نفس الاستغلال مع برنامج العالم الآمن الثابت (tz). بينما يسمح مُعاد التوجيه عند الإقلاع بتجاوز الترجمة من المرحلة الثانية، يبدو أن منطقة ذاكرة tz في DRAM محمية بواسطة مكوّن عتادي إضافي (خارج وحدة المعالجة المركزية) يمنع الوصول بعد مروره عبر مُعاد التوجيه عند الإقلاع:
من المرجح أن منطقة ذاكرة tz يمكن الوصول إليها فقط في الحالة الآمنة. يسمح الاستغلال فقط بتجاوز حماية ذاكرة المُشغِّل، بينما تظل آليات الأمان الأخرى في العتاد قائمة.
يمكن استخدام مُعاد التوجيه عند الإقلاع لتعطيل واستبدال البرنامج الثابت الأصلي للمُشغِّل بالكامل أثناء التشغيل، دون معرفة بإصدار البرنامج الثابت. على وجه الخصوص، ليس من الضروري استخدام الهندسة العكسية للحصول على عناوين ذاكرة للمتغيرات والدوال التي يمكن تعديلها. يكفي معرفة المنطقة التقريبية لذاكرة البرنامج الثابت للمُشغِّل، على سبيل المثال من حجز الذاكرة في كود Linux مفتوح المصدر أو بقراءة ترويسات ELF لملف البرنامج الثابت للمُشغِّل (المتاح في قسم hyp على وحدة التخزين الداخلية).
الفكرة العامة هي:
hvc) للتبديل من نظام التشغيل إلى المُشغِّل (من EL1 إلى EL2).الكود الذي ينفذ ذلك ليس طويلًا، لكنه يتضمن بعض لغة التجميع AArch64 منخفضة المستوى وتفاعلًا دقيقًا مع مخابئ وحدة المعالجة المركزية. ومع ذلك، يبقى السؤال الرئيسي مفتوحًا: أين بالضبط يجب كتابة كود الصدفة، دون جعل الكود خاصًا بإصدار واحد معين من البرنامج الثابت للمُشغِّل؟
أثناء استدعاء المُشغِّل (أو أي استثناء بشكل عام)، يُجبر تنفيذ وحدة المعالجة المركزية على الانتقال إلى عنوان ذاكرة خاص: متجه الاستثناء. متجهات الاستثناءات هي جزء من جدول متجهات أكبر يحتوي على الكود الذي يعالج أنواعًا مختلفة من الاستثناءات، القادمة من مستويات الاستثناءات الحالية أو الأدنى:
يمثل كل مربع متجه استثناء بمساحة تكفي لـ 32 تعليمة تجميع. هذه المساحة ليست كافية لذلك تحتوي عادةً على تعليمات قفز تقفز إلى مكان آخر به مساحة أكبر لكود إضافي.
الإزاحات نسبية إلى سجل عنوان قاعدة المتجهات (VBAR) الذي يحدد العنوان الأساسي لجدول المتجهات لكل مستوى استثناء. يكتب المُشغِّل العنوان الأساسي إلى سجل وحدة المعالجة المركزية VBAR_EL2.
استدعاء المُشغِّل هو استثناء متزامن صادر من مستوى استثناء أدنى (نواة نظام التشغيل التي تعمل في EL1 إلى المُشغِّل في EL2). إذا كانت نواة نظام التشغيل تعمل في وضع 32-بت، ستقفز وحدة المعالجة المركزية إلى VBAR_EL2+0x600، أو إلى VBAR_EL2+0x400 في وضع 64-بت. بعد استخدام مُعاد التوجيه عند الإقلاع لكتابة كود مخصص إلى هذا العنوان وإجراء استدعاء المُشغِّل، ستبدأ وحدة المعالجة المركزية في تنفيذ كود الصدفة.
للأسف، VBAR_EL2 غير قابل للقراءة بواسطة نواة نظام التشغيل التي تعمل في EL1. يمكن قراءته فقط من المُشغِّل (EL2) نفسه أو من مستوى أعلى. ومع ذلك، تسهّل هذه المعرفة تخمين عنوان الدخول باستخدام القوة الغاشمة: يجب أن يكون العنوان الأساسي لجدول المتجهات محاذى (لمضاعف) لحجمه (0x800 = 2 كيبي بايت). هذا يعني أنه لا يوجد سوى 64 موقعًا محتملًا داخل منطقة بحجم 128 كيبي بايت، أو 512 داخل منطقة بحجم 1 ميبي بايت:
تعرض المربعات الحمراء جميع المواقع المحتملة التي قد تقفز إليها وحدة المعالجة المركزية أثناء استدعاء المُشغِّل. كتابة كود الصدفة إلى جميع هذه المواقع كافٍ لإبقاء النهج مستقلًا عن إصدار واحد معين من البرنامج الثابت (والذي سيكون له في الواقع جدول المتجهات في عنوان واحد محدد).
يمكن تحسين هذا أكثر: يسمح مُعاد التوجيه عند الإقلاع بكل من القراءة والكتابة، لذا سيكون من الممكن إضافة بعض الاستدلالات بناءً على الكود/البيانات الموجودة المقروءة من مواقع الذاكرة. يجب أن يحتوي على تعليمات AArch64 (A64) صالحة وربما بعض بايتات الحشو المتكررة مثل NOP أو تعليمات القفز. (غالبًا ما تكون المساحة المخصصة للتعليمات الـ 32 لكل متجه استثناء مستخدمة جزئيًا فقط لأنه من الأسهل القفز إلى دالة مناسبة بمساحة أكبر.)
كود إثبات المفهوم المضمن في هذا المستودع هو تعديل على مُحمِّل الإقلاع Little Kernel (LK) مفتوح المصدر من Qualcomm لمنصة Snapdragon 410 (MSM8916/APQ8016)، المخصص في الأصل للاختبار مع لوحة تطوير DragonBoard 410c. اختير كل هذا من أجل البساطة؛ يمكن أيضًا استغلال الثغرة من أنظمة تشغيل أخرى (مثل Linux)، ومنصات متأثرة أخرى، وحتى أجهزة ذات إقلاع آمن — طالما توجد طريقة لتنفيذ كود مخصص داخل نواة نظام التشغيل.
ينفذ الكود النهج الموصوف أعلاه لتعطيل المُشغِّل الجاري بالكامل ثم يستبدله بإصدار مختلف. "المُشغِّل" الجديد لا يدعم أي أجهزة افتراضية ولكنه قادر على إعطاء الإجابة على السؤال النهائي للحياة والكون وكل شيء باستخدام استدعاء مُشغِّل بسيط:``` $ fastboot oem CVE-2022-22063 < waiting for any device > (bootloader) Hypervisor, what is the Answer to The Ultimate Question of (bootloader) Life, the Universe and Everything? (bootloader) Old hypervisor returned answer: -2 (bootloader) Old non-secure boot remapper base address: 0x100000 (bootloader) Setting boot remapper to hypervisor memory (0x86400000) (bootloader) Using boot remapper to copy shell code to hypervisor memory (bootloader) Copying to all possible vector tables (starting at 0x600) (bootloader) Calling shell code to disable running hypervisor (bootloader) Found old EL2 vector base address at 0x86404000 (bootloader) Copying new code directly to hypervisor memory (0x86400000) (bootloader) Hypervisor, what is the Answer to The Ultimate Question of (bootloader) Life, the Universe and Everything? (bootloader) New hypervisor returned answer: 42 OKAY [ 0.015s] Finished. Total time: 0.015s
### الاختبار
يمكن بناء واختبار كود إثبات المفهوم كما يلي:```shell
$ git clone https://git.codelinaro.org/clo/la/kernel/lk.git -b caf_migration/LA.BR.1.2.9.1_rb1.5
$ cp CVE-2022-22063.c lk/platform/msm8916/
$ git apply LK.diff
# Build LK for msm8916 and flash it to device
$ fastboot oem CVE-2022-22063
...
ومع ذلك، فإن هذا التكوين مناسب فقط للوحة تطوير DragonBoard 410c والأجهزة الأخرى التي لا تحتوي على إقلاع آمن (Secure Boot)، حيث يمكن استبدال محمّل الإقلاع الأساسي بسهولة (وبأمان). الكود الموجود في هذا المستودع مُقدَّم بشكل أساسي كمرجع وليس للاستخدام/الاختبار السهل.
من المخطط في المستقبل دمج هذا الكود في إصدار جديد من lk2nd، والذي يمكن اختباره بسهولة أكبر بكثير على أجهزة متنوعة دون استبدال محمّل الإقلاع الأساسي.
وفقًا لشركة Qualcomm، تم إصلاح المشكلة بمنع الوصول إلى منطقة معيد توجيه الإقلاع (boot remapper) باستخدام ترجمة المرحلة الثانية (Stage 2). لا يزال تكوين معيد توجيه الإقلاع (سجل APCS_BOOT_START_ADDR_NSEC) قابلاً للوصول، لكن كِلتا منطقتي الذاكرة أصبحتا محظورتين الآن:
إصلاح محتمل آخر هو حماية سجل APCS_BOOT_START_ADDR_NSEC في الهايبرفايزر (hypervisor). في هذه الحالة، يمكن أن تظل المنطقة المعاد توجيهها قابلة للوصول، إذ لن يكون لدى نظام التشغيل أي وسيلة لإعادة تكوين معيد توجيه الإقلاع إلى مناطق ذاكرة أخرى.
وفقًا لـ Qualcomm، كانت المشكلة صعبة المعالجة بشكل خاص لأنه تعذّر استخدام أدوات الأتمتة المعتادة لديهم. معظم المشكلات التي يتلقونها هي مشكلات برمجية، يمكن فيها تحديد الأجهزة المتأثرة من خلال فحص الكود المصدري. أما في هذه المشكلة، فقد كان مطلوبًا فحص العتاد والبرمجيات يدويًا (ما إذا كانت المنصة تحتوي على السجل المُشكِل، وما إذا كان الهايبرفايزر معرضًا للخطر). لسوء الحظ، فإن عدم استخدام الأتمتة عنى أيضًا أن المشكلة لم تُدرَج تلقائيًا في نشرة أمنية. طُلِب التمديد الثاني لفترة الحظر لإطلاع العملاء على المشكلة الأمنية ومنحهم وقتًا لتحديث أجهزتهم. وهم يعملون على تحسين العملية لتجنب حدوث مثل هذه المشكلات في البلاغات المستقبلية.
CVE-2022-22063 Report and Diagrams © 2022 by Stephan Gerhold are licensed under Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0).
كود إثبات المفهوم (Proof of Concept) (CVE-2022-22063.c) مُقدَّم بموجب رخصة MIT.