
CVE-2018-4185: كشف مؤشرات النواة في iOS 11.2-11.2.6 الناتج عن تخفيف Apple لثغرة Meltdown.
===================================================================================================
قدّم iOS 11.2 تسريبًا للمعلومات من النواة (kernel) يمكن استخدامه لتحديد قيمة إزاحة kASLR. كان السبب في هذه المشكلة ميزة جديدة أُضيفت باسم __ARM_KERNEL_PROTECT__، والتي تسبّبت عن غير قصد في ظهور عنوان دالة النواة Lel0_synchronous_vector_64_long في السجل x18 عند الحصول على قيم سجلات الخيط باستخدام thread_get_state. اكتُشفت المشكلة عندما بدأت مؤشرات النواة تظهر في سجلات تعطل تطبيقات iOS.
في iOS 11.2، أدخلت Apple ميزة على arm64 باسم __ARM_KERNEL_PROTECT__. وفقًا لتعليق في osfmk/arm64/proc_reg.h:
__ARM_KERNEL_PROTECT__ is a feature intended to guard against potential
architectural or microarchitectural vulnerabilities that could allow cores to
read/access EL1-only mappings while in EL0 mode. This is achieved by
removing as many mappings as possible when the core transitions to EL0 mode
from EL1 mode, and restoring those mappings when the core transitions to EL1
mode from EL0 mode.
أي أنه عند الانتقال من EL1 (وضع النواة) إلى EL0 (وضع المستخدم)، تتم إزالة أكبر عدد ممكن من تعيينات النواة. من المفترض أن يحدّ هذا من مساحة الهجوم المحتملة ضد تعيينات ذاكرة النواة عند استغلال ثغرات معمارية دقيقة مثل Spectre أو Meltdown.
إذا اطّلعت على الفرق بين إصداري XNU 4570.20.62 و4570.31.3، ستجد عددًا من الإشارات الجديدة إلى السجل x18 تظهر في ملف osfmk/arm64/locore.s فيما يتعلق بـ __ARM_KERNEL_PROTECT__. على وجه الخصوص، ستلاحظ أن متجه الاستثناء Lel0_synchronous_vector_64، وهو متجه الاستثناء الذي يُستدعى عند إجراء نداء نظام (التعليمة svc #0)، أصبح الآن على النحو التالي:
.text
.align 7
Lel0_synchronous_vector_64:
MAP_KERNEL
BRANCH_TO_KVA_VECTOR Lel0_synchronous_vector_64_long, 8
وماكرو BRANCH_TO_KVA_VECTOR معرّف على النحو التالي:
.macro BRANCH_TO_KVA_VECTOR
#if __ARM_KERNEL_PROTECT__
/*
* Find the kernelcache table for the exception vectors by accessing
* the per-CPU data.
*/
mrs x18, TPIDR_EL1
ldr x18, [x18, ACT_CPUDATAP]
ldr x18, [x18, CPU_EXC_VECTORS]
/*
* Get the handler for this exception and jump to it.
*/
ldr x18, [x18, #($1 << 3)]
br x18
#else
b $0
#endif /* __ARM_KERNEL_PROTECT__ */
.endmacro
ينفّذ هذا الماكرو قفزة غير مباشرة إلى التنفيذ الفعلي لمتجه الاستثناء، Lel0_synchronous_vector_64_long، عبر تحميل مؤشر إلى تلك الدالة في السجل x18. لاحظ، مع ذلك، أن هذا الاستبدال لقيمة x18 يحدث قبل حفظ سجلات مساحة المستخدم بواسطة الدالة fleh_dispatch64، التي يستدعيها Lel0_synchronous_vector_64_long. هذا يعني أنه عند حفظ سجلات المستخدم، سيكون x18 فعليًا مؤشرًا إلى Lel0_synchronous_vector_64_long وليس القيمة الأصلية من مساحة المستخدم.
على الرغم من أن x18 يُمسح عند العودة من الاستثناء، فإن تخزين مؤشر نواة في حالة سجل المستخدم يمثل مشكلة لأن thread_get_state يمكن استخدامه لنسخ حالة سجل المستخدم المحفوظة إلى مساحة المستخدم، بما في ذلك قيمة السجل x18. كل ما يحتاج الخيط إلى فعله للحصول على عنوان دالة Lel0_synchronous_vector_64_long هو استدعاء thread_get_state على نفسه والنظر إلى القيمة المُبلَغ عنها للسجل x18. وهذا يجعل من التافه تحديد إزاحة kASLR بطرح قيمة x18 التي تم الحصول عليها بهذه الطريقة من العنوان الثابت لـ Lel0_synchronous_vector_64_long.
كما ذُكر أعلاه، الاستغلال تافه: ببساطة استدعِ الدالة thread_get_state، وانظر إلى قيمة السجل x18، واطرح منها العنوان الثابت لدالة النواة Lel0_synchronous_vector_64_long.
اكتشفت هذه المسألة في 26 فبراير 2018، بعد أن لاحظت مؤشر نواة في السجل x18 في أحد سجلات تعطل تطبيقات iOS. أظهر فحص سريع أن القيمة نفسها تظهر في السجل x18 في كل سجلات التعطل على الجهاز، مما أشار إلى وجود تسريب معلومات خطير.
بعد ذلك حاولت تحديد ما يحدث بالضبط مع السجل x18 من خلال التجارب. وضعت نقطة توقف في تطبيق iOS فارغ واستخدمت lldb لقراءة قيمة السجل x18، مؤكدًا أن التسريب لا يقتصر على التطبيقات المتعطلة. بعدها حاولت قراءة قيمة x18 باستخدام التجميع المضمّن (inline assembly) ووجدت أن القيمة التي تم الحصول عليها لا تطابق القيمة التي يعرضها المصحح عند استخدام أمر مثل reg read x18. هذا أوحى بأن التسريب ربما يكون في الواقع في thread_get_state، وأن السجل x18 لا يحتوي فعلًا على مؤشر نواة أثناء تنفيذ وحدة المعالجة المركزية في مساحة المستخدم. إثبات مفهوم سريع قرأ قيمة x18 باستخدام thread_get_state أكد أن هذه الدالة هي بالفعل مصدر التسريب.
أبلغت Apple بهذه المشكلة في 26 فبراير 2018، في اليوم نفسه الذي اكتشفتها فيه.
By Brandon Azad