
CVE-2018-4280: macOS 10.13.5 पर launchd में Mach पोर्ट प्रतिस्थापन कमजोरी जिसके कारण स्थानीय विशेषाधिकार वृद्धि और SIP बायपास होता है।
launchd-portrep macOS पर launchd में पोर्ट प्रतिस्थापन भेद्यता के लिए एक शोषण है, जो प्रारंभिक उपयोगकर्तास्थान प्रक्रिया और सेवा प्रबंधन डेमॉन है। बूटस्ट्रैप पोर्ट पर एक क्राफ्टेड Mach संदेश भेजकर, launchd को किसी भी Mach पोर्ट के लिए अपने भेजने के अधिकार को डीलोकेट करने के लिए मजबूर किया जा सकता है, जिसके लिए हमलावर के पास भी भेजने का अधिकार है। यह हमलावर को किसी भी launchd सेवा का प्रतिरूपण करने की अनुमति देता है जिसे वह सिस्टम के बाकी हिस्सों तक देख सकता है।
Launchd अपने मुख्य पोर्ट पर कई अलग-अलग Mach संदेश हैंडलर को मल्टीप्लेक्स करता है, जिसमें अपवाद संदेशों के लिए एक MIG हैंडलर शामिल है। यदि कोई प्रक्रिया अपने स्वयं के बूटस्ट्रैप पोर्ट पर mach_exception_raise या mach_exception_raise_state_identity संदेश भेजती है, तो launchd उस संदेश को होस्ट-स्तरीय अपवाद के रूप में प्राप्त और संसाधित करेगा।
दुर्भाग्य से, इन संदेशों का launchd का हैंडलिंग बगी है। यदि अपवाद प्रकार EXC_CRASH है, तो launchd संदेश में भेजे गए थ्रेड और टास्क पोर्ट को डीलोकेट करेगा और फिर सेवा रूटीन से KERN_FAILURE लौटाएगा, जिससे MIG सिस्टम थ्रेड और टास्क पोर्ट को फिर से डीलोकेट कर देगा। (धारणा यह है कि यदि कोई सेवा रूटीन सफलता लौटाती है, तो इसने Mach संदेश में सभी संसाधनों का स्वामित्व ले लिया है, जबकि यदि सेवा रूटीन कोई त्रुटि लौटाती है, तो इसने किसी भी संसाधन का स्वामित्व नहीं लिया है।)
यहाँ mach_exception_raise संदेशों के लिए launchd की सेवा रूटीन का कोड है, जिसे 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, // जो ग्राहक द्वारा भेजा जाता है।
unsigned int exception, // thread और task पोर्ट
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 पोर्टों को फिर से
} // डीलोकेट करेगा।
पोर्ट नामों का यह दोहरा-डीलोकेट समस्याग्रस्त है क्योंकि एक प्रक्रिया अपवाद संदेश में टास्क और थ्रेड पोर्ट के रूप में जो भी पोर्ट चाहे सेट कर सकती है। Launchd यह जाँच नहीं करता है कि प्राप्त भेजने के अधिकार वास्तव में किसी थ्रेड और कार्य के अनुरूप हैं; पोर्ट, उदाहरण के लिए, launchd के IPC स्थान में पहले से मौजूद पोर्ट के भेजने के अधिकार हो सकते हैं। फिर दोहरा-डीलोकेट वास्तव में launchd को अपने स्वयं के एक पोर्ट पर उपयोगकर्ता संदर्भ छोड़ने का कारण बनेगा।
इस बग का उपयोग launchd के भेजने के अधिकार को किसी भी Mach पोर्ट से मुक्त करने के लिए किया जा सकता है, जिसमें हमलावर प्रक्रिया के पास भी भेजने का अधिकार है। विशेष रूप से, यदि हमलावर प्रक्रिया launchd का उपयोग करके किसी सिस्टम सेवा को देख सकती है, तो वह उस सेवा के लिए launchd के भेजने के अधिकार को मुक्त कर सकती है और फिर सिस्टम के बाकी हिस्सों के लिए सेवा का प्रतिरूपण कर सकती है। उसके बाद सिस्टम विशेषाधिकार प्राप्त करने के लिए कई अलग-अलग रास्ते हैं।
यह बग CVE-2016-7637 का एक कम सामान्य संस्करण है, जो XNU में Ian Beer द्वारा खोजा गया Mach पोर्ट उपयोगकर्ता संदर्भ हैंडलिंग मुद्दा है, जो प्रक्रियाओं को अन्य प्रक्रियाओं में Mach पोर्ट मुक्त करने की अनुमति देता है। Ian Beer ने macOS पर उस भेद्यता का शोषण com.apple.CoreServices.coreservicesd एंडपॉइंट पर launchd के भेजने के अधिकार को बदलकर और सिस्टम के बाकी हिस्सों के लिए coreservicesd का प्रतिरूपण करके किया। Coreservicesd एक आकर्षक लक्ष्य है क्योंकि यह उन कुछ सेवाओं में से एक है जिनके लिए ग्राहक एक Mach संदेश में अपना टास्क पोर्ट भेजेंगे। launchd के भेजने के अधिकार को coreservicesd में अपने स्वयं के पोर्ट से बदलकर और फिर विशेषाधिकार प्राप्त ग्राहकों को coreservicesd को देखने और संवाद करने के लिए ट्रिगर करके, वह एक विशेषाधिकार प्राप्त प्रक्रिया के लिए टास्क पोर्ट प्राप्त करने और फिर उस प्रक्रिया के भीतर कोड निष्पादित करने में सक्षम था।
चूंकि macOS पर व्यवहार नहीं बदला है, मैंने मूल रूप से इस भेद्यता के लिए Ian Beer की शोषण रणनीति की नकल की है। हम launchd को coreservicesd के सेवा पोर्ट वाले अपवाद संदेश भेजते हैं जब तक कि हम उस पोर्ट पर launchd के भेजने के अधिकार को मुक्त नहीं कर देते। हम पता लगा सकते हैं कि हमने सेवा पर फिर से bootstrap_look_up() कॉल करके अधिकार मुक्त कर दिया है: यदि launchd एक अमान्य पोर्ट नाम लौटाता है, तो हमने सफलतापूर्वक पोर्ट पर launchd का भेजने का अधिकार मुक्त कर दिया है। फिर, हम बार-बार launchd के साथ बड़ी संख्या में सेवाओं को पंजीकृत और अपंजीकृत करते हैं जब तक कि हमारे द्वारा पंजीकृत सेवाओं में से एक को launchd के IPC स्थान में मूल coreservicesd पोर्ट के समान Mach पोर्ट नाम नहीं दिया जाता है। इस बिंदु पर, कोई भी प्रक्रिया जो launchd में com.apple.CoreServices.coreservicesd को देखती है, वास्तविक coreservicesd के बजाय हमारी नकली सेवा के लिए एक भेजने का अधिकार प्राप्त करेगी। हम फिर नकली सेवा पोर्ट पर एक MITM सर्वर चलाते हैं, ग्राहकों से प्राप्त संदेशों में सभी Mach पोर्टों का निरीक्षण करते हैं, इससे पहले कि हम उन्हें वास्तविक coreservicesd को भेजें। इस बिंदु पर हम sysdiagnose को एक संदेश भेजते हैं जिससे वह एक tailspin चलाता है, जो sysdiagnose को हमारे नकली coreservicesd पोर्ट से कनेक्ट करने और हमें अपना टास्क पोर्ट भेजने का कारण बनता है। चूंकि sysdiagnose के पास task_for_pid-allow एंटाइटलमेंट है, अब हम किसी भी प्रक्रिया के लिए टास्क पोर्ट प्राप्त कर सकते हैं।
सिस्टम के (अधिकांश) उचित कार्य को बहाल करने के लिए, हम sysdiagnose का उपयोग launchd के टास्क पोर्ट को प्राप्त करने के लिए करते हैं, और फिर launchd के टास्क पोर्ट का उपयोग करके launchd के भेजने के अधिकार को हमारे नकली सेवा पोर्ट से वापस वास्तविक coreservicesd के भेजने के अधिकार से बदल देते हैं। इस तरह भविष्य के ग्राहक वास्तव में coreservicesd तक पहुँच सकते हैं।