
CVE-2018-4241: XNU कर्नेल हीप ओवरफ्लो MPTCP में खराब सीमा जाँच के कारण iOS 11 - 11.3.1 के लिए इयान बीयर द्वारा जारी
@i41nbeer
mptcp_usr_connectx, AP_MULTIPATH सॉकेट परिवार के लिए connectx syscall का हैंडलर है।
इस फ़ंक्शन का तर्क उन स्रोत और गंतव्य sockaddrs को सही ढंग से संभालने में विफल रहता है जो AF_INET या AF_INET6 नहीं हैं:
// verify sa_len for AF_INET:
if (dst->sa_family == AF_INET &&
dst->sa_len != sizeof(mpte->__mpte_dst_v4)) {
mptcplog((LOG_ERR, "%s IPv4 dst len %u\n", __func__, dst->sa_len), MPTCP_SOCKET_DBG, MPTCP_LOGLVL_ERR);
error = EINVAL;
goto out;
}
// verify sa_len for AF_INET6:
if (dst->sa_family == AF_INET6 &&
dst->sa_len != sizeof(mpte->__mpte_dst_v6)) {
mptcplog((LOG_ERR, "%s IPv6 dst len %u\n", __func__, dst->sa_len), MPTCP_SOCKET_DBG, MPTCP_LOGLVL_ERR);
error = EINVAL;
goto out;
}
// code doesn't bail if sa_family was neither AF_INET nor AF_INET6
if (!(mpte->mpte_flags & MPTE_SVCTYPE_CHECKED)) {
if (mptcp_entitlement_check(mp_so) < 0) {
error = EPERM;
goto out;
}
mpte->mpte_flags |= MPTE_SVCTYPE_CHECKED;
}
// memcpy with sa_len up to 255:
if ((mp_so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING)) == 0) {
memcpy(&mpte->mpte_dst, dst, dst->sa_len);
}
उस संरचना में चारों ओर देखने पर जिसमें आप ओवरफ्लो करते हैं, आप देखते हैं कि आप यहाँ दोनों फ़ील्ड को हिट कर सकते हैं:
if (mpte->mpte_itfinfo_size > MPTE_ITFINFO_SIZE) _FREE(mpte->mpte_itfinfo, M_TEMP);
mpte_itfinfo_size, mpte_itfinfo से ठीक पहले है।
जब संरचना को आरंभ किया जाता है, तो mpte_itfinfo पॉइंटर एक छोटे इनलाइन ऐरे की ओर इशारा करता है। यदि उस में फिट होने से अधिक सबफ़्लो जोड़े जाते हैं, तो उन्हें हीप बफ़र में रखा जाता है, और mpte_itfinfo उस ओर इशारा करेगा।
यदि आपके पास कोई अन्य बग होता (जैसे async_wake से कर्नेल हीप डिस्क्लोज़र बग), तो आप mpte_itfinfo फ़ील्ड को किसी भी वैध ज़ोन ऑब्जेक्ट के साथ ओवरराइट कर सकते थे और यह फ्री हो जाता (वास्तव में, आप इसे उस ऑब्जेक्ट में एक ऑफ़सेट के साथ भी ओवरराइट कर सकते थे और भी अधिक मज़े के लिए!)
हालांकि, हमारे पास वह नहीं है।
इसके बजाय एक और तरीका पॉइंटर को आंशिक रूप से ओवरराइट करना है। यदि हम इसे NULL बाइट्स के साथ आंशिक रूप से ओवरराइट करते हैं, तो हम इसे 256 बाइट, 65k, 16MB या 4GB एलाइन किए गए मान पर इंगित कर सकते हैं।
इस शोषण में मैं 3-बाइट NULL ओवरराइट चुनता हूँ, जो अगले 16MB सीमा तक नीचे गोल किए गए mpte_itfinfo पते का kfree करेगा।
शोषण प्रवाह इस प्रकार है:
बारी-बारी से 16MB ipc_kmsgs आवंटित करें, उसके बाद बहुत सारे mptcp सॉकेट्स आवंटित करें। लक्ष्य उस 16MB सीमा पर kalloc.2048 आवंटन प्राप्त करना है।
बग का उपयोग करके एक ipc_kmsg को मुक्त करें, उस पृष्ठ को मध्यवर्ती सूची में ले जाएँ और 16MB-एलाइन आवंटन को kalloc.2048 मध्यवर्ती पृष्ठ फ्रीलिस्ट पर रखें।
बहुत सारे भरे हुए 2047-बाइट पाइप आवंटित करें; इन पाइपों के बैकिंग बफ़र kalloc.2048 से आएंगे, उम्मीद है कि हमारा 16MB-एलाइन पता भी शामिल होगा।
बग को दूसरी बार ट्रिगर करें, उसी पते को मुक्त करें और इस बार kalloc.2048 से बहुत सारे पूर्व-आवंटित ipc_kmsg बफ़र आवंटित करें।
अब हमारे पास एक ipc_kmsg (जिसमें हम संदेश भेज सकते हैं और फिर प्राप्त कर सकते हैं) और एक पाइप बफ़र (जिसे हम पढ़ और लिख सकते हैं) एक-दूसरे पर ओवरलैप हो रहे हैं।
मैं extra_recipe से थ्रेड एक्सेप्शन पोर्ट ट्रिक का उपयोग करता हूँ ताकि प्री-आवंटित ipc_kmsg बफ़र में संदेश भेजे जा सकें। हर बार हम प्रत्येक पाइप की जाँच करते हैं कि क्या उनमें से कोई संदेश है। जब हम सही (ipc_kmsg, pipe) जोड़ी पाते हैं, तो हम संदेश को फिर से लिख सकते हैं ताकि हम स्वयं को एक नकली पोर्ट भेज सकें जो पाइप बफ़र के अंदर रहता है। मैं उस नकली पोर्ट को async_wake (जो मैंने @qwertyoruiopz और @marcograss के yalu 10.2 पर आधारित किया है) की तरह संरचित करता हूँ ताकि मुझे एक प्रारंभिक कर्नेल रीड प्रिमिटिव मिल सके।
कर्नेल रीड प्रिमिटिव का उपयोग करके मैं कर्नेल टास्क ढूँढता हूँ और एक नकली पोर्ट बनाता हूँ जो mach_vm_read/mach_vm_write के माध्यम से आसान कर्नेल मेमोरी रीड/राइट की अनुमति देता है।
चेतावनी: mptcp सॉकेट्स को कनेक्ट करने के लिए आपको com.apple.developer.networking.multipath एंटाइटलमेंट की आवश्यकता है, जिसके लिए एक Apple डेवलपर प्रमाणपत्र चाहिए, जो कोई भी Apple से खरीद सकता है।
विश्वसनीयता: यह एक सुरक्षा अनुसंधान उपकरण है और यह बिल्कुल सही नहीं है। हालांकि, यह अधिकांश समय काम करना चाहिए, और जब यह काम करता है तो यह सफाई का अच्छा काम करेगा ताकि बाद में पैनिक न हो।
इसके काम करने की संभावना बढ़ाने के लिए:
समर्थित उपकरण: यह iOS 11.0 - 11.3.1 (समावेशी) पर काम करना चाहिए। मैंने परीक्षण किया है: iPod Touch 6g, iPhone 6s, iPhone SE, iPhone 7, iPhone 8
API: #include "sploit.h" और शोषण चलाने के लिए go() कॉल करें। यदि यह काम करता है, तो आप कर्नेल मेमोरी पढ़ने और लिखने के लिए kmem.h में फ़ंक्शन का उपयोग कर सकते हैं।
नोट्स: कई लोगों ने इस बग को पैच से सार्वजनिक रूप से bindiff किया है (या उनके 0day को पैच किया गया है ;) अधिक विवरण के लिए उनकी सामग्री पढ़ें: @elvanderb ने 31 मई को पेरिस में rump.beer पर इस बग के बारे में एक लाइटनिंग टॉक दी: https://www.rump.beer/2018/slides/ios_48h.pdf @jaakerblom ने 1 जून को github पर एक काम करने वाला शोषण प्रकाशित किया: https://github.com/potmdehex/multipath_kfree जॉन की तकनीक मेरी जैसी है, लेकिन वह तीन बाइट के बजाय दो-बाइट ओवरफ्लो करता है, और विभिन्न वस्तुओं से बदलता है। अच्छा काम!