
empty_list - exploit for p0 issue 1564 (CVE-2018-4243) iOS 11.0 - 11.3.1 kernel r/w
empty_list - p0 मुद्दा 1564 (CVE-2018-4243) iOS 11.0 - 11.3.1 कर्नेल r/w के लिए शोषण @i41nbeer
बग: getvolattrlist, fgetattrlist syscall के माध्यम से एक उपयोगकर्ता-नियंत्रित bufferSize तर्क लेती है।
जब attr list को serialize करने के लिए एक कर्नेल बफर आवंटित किया जाता है, तो निम्नलिखित टिप्पणी होती है:
/*
समस्या यह है कि कोड तब सही ढंग से यह नहीं संभालता कि जब उपयोगकर्ता द्वारा प्रदान किया गया बफर आकार अनुरोधित हेडर आकार से छोटा हो। यदि हम ATTR_CMN_RETURNED_ATTRS पास करते हैं, तो हम निम्नलिखित कोड पर पहुँचेंगे:
/* Return attribute set output if requested. / if (return_valid) { ab.actual.commonattr |= ATTR_CMN_RETURNED_ATTRS; if (pack_invalid) { / Only report the attributes that are valid */ ab.actual.commonattr &= ab.valid.commonattr; ab.actual.volattr &= ab.valid.volattr; } bcopy(&ab.actual, ab.base + sizeof(uint32_t), sizeof (ab.actual)); }
यहाँ कोई जाँच नहीं है कि आवंटित बफर कम से कम उतना बड़ा है।
शोषण: मुझे उम्मीद है कि इसका एक लंबा लिखित विवरण प्रकाशित करूंगा; ये कुछ मोटे नोट्स हैं कि शोषण कैसे काम करता है:
बग आपको kalloc.16 आवंटन के अंत से 8 शून्य बाइट्स लिखने की क्षमता देता है। हालाँकि ऐसा लगता है कि आप उन बाइट्स में कुछ बिट्स को नियंत्रित कर सकते हैं, मुझे यकीन नहीं है कि वास्तव में आप ऐसा कर सकते हैं, इसलिए मैंने इस पर ध्यान केंद्रित किया कि यह अंत में एक NULL पॉइंटर लिख रहा है।
यह काफी सीमित प्रिमिटिव है, इसलिए पहला कदम यह है कि संभावित चीजों की गणना करने की कोशिश करें जो आप कर सकते हैं:
अंत में मैंने पहला विकल्प चुना। फिर दो और आवश्यकताएँ हैं:
मैंने struct ipc_port को लक्षित करना चुना, जिसका दूसरा dword संदर्भ गणना क्षेत्र है, इस प्रकार पहली आवश्यकता पूरी होती है। हालाँकि यह kalloc.16 में आवंटित नहीं है; इसके बजाय यह अपने स्वयं के ज़ोन (ipc_ports) में रहता है।
इसका मतलब है कि हमें kalloc.16 ज़ोन ब्लॉक को ipc_ports वाले के ठीक पहले संरेखित करना होगा, फिर kalloc.16 ब्लॉक में अंतिम kalloc.16 आवंटन से बाहर निकलकर ipc_ports में पहले पर ओवरफ्लो करना होगा।
इसे आसान बनाने के लिए हम दो तरकीबों का उपयोग कर सकते हैं:
Freelist उलटना: ज़ोन आवंटन पहले मध्यवर्ती (आंशिक रूप से भरे हुए) पृष्ठों से आएंगे। इसका मतलब है कि यदि हम ग्रूम के बीच में k.16 ऑब्जेक्ट्स को फ्री और आवंटित करना शुरू करते हैं, तो वे तब तक पुन: उपयोग नहीं किए जाएंगे जब तक कि वर्तमान मध्यवर्ती पृष्ठ या तो पूरा या खाली न हो जाए।
यह एक चुनौती प्रस्तुत करता है क्योंकि ताजा पृष्ठ की freelist अर्ध-यादृच्छिक रूप से भरी जाती है, जिससे उनके आवंटन अंदर से बाहर की ओर जाएंगे:
| 9 8 6 5 2 1 3 4 7 10 | <-- उदाहरण: एक पूरी तरह से मुक्त पृष्ठ से "यादृच्छिक" आवंटन क्रम
इसका मतलब है कि हमारा अंतिम मध्यवर्ती k.16 और पोर्ट पृष्ठ कुछ इस तरह दिखेगा:
| - - - 5 2 1 3 4 - - | - - - 4 1 2 3 5 - - | kalloc.16 ipc_ports
यदि हम ओवरफ्लो का उपयोग करके एक freelist प्रविष्टि को भ्रष्ट करते हैं, तो यदि वह आवंटित हो जाती है तो panic होगा, इसलिए हमें इससे बचना होगा।
ट्रिक यह है कि आवंटन और मुक्ति क्रम को नियंत्रित करके हम freelists को उलट सकते हैं ताकि अंतिम मध्यवर्ती पृष्ठ कुछ इस तरह दिखे: | 1 4 - - - - - 5 3 2 | 2 5 - - - - - 4 3 1 | kalloc.16 ipc_ports
इस बिंदु पर हमारे पास एक kalloc.16 को मुक्त करने और ओवरफ्लो के लिए पुनः आवंटित करने की अधिक संभावना होती है ताकि हम ipc_port के पहले qword पर हिट कर सकें।
सुरक्षित रूप से ओवरफ्लो किए जा सकने वाले आवंटन: चूंकि लक्ष्य (जो बिल्कुल अंत में, ipc_port से ठीक पहले है) पर हिट करने से पहले हमें कई उम्मीदवार आवंटनों से बाहर ओवरफ्लो करना होगा, इसलिए हमें यह सुनिश्चित करना होगा कि kalloc.16 पृष्ठ पर आवंटित ऑब्जेक्ट NULL पॉइंटर द्वारा भ्रष्ट होने पर सुरक्षित हों।
इसके लिए मैं mach message ool_port डिस्क्रिप्टर का उपयोग करता हूँ, क्योंकि NULL एक मान्य मान है।
शोषण प्रवाह: हम kalloc.16 freelists को उलटने के लिए ग्रूम करते हैं और ipc_port में ओवरफ्लो करने का प्रयास शुरू करते हैं।
हम mach port नामों की अनुमानित श्रेणी जानते हैं जिनमें भ्रष्ट होने वाला पोर्ट होता है; प्रत्येक ओवरफ्लो प्रयास के बाद हम इन पोर्ट्स में से प्रत्येक की जाँच करते हैं कि क्या पोर्ट भ्रष्ट हुआ था। सफल भ्रष्टाचार का एक दुष्प्रभाव यह है कि पोर्ट का io_active फ्लैग शून्य पर सेट हो जाएगा। हम mach_port_kobject MIG विधि का उपयोग करके बिना किसी दुष्प्रभाव के इसका पता लगा सकते हैं।
एक बार जब हम भ्रष्ट पोर्ट पा लेते हैं, तो हमें उस पर एक संदर्भ लेने और छोड़ने का कारण बनाना होता है; और अधिक महत्वपूर्ण बात यह है कि हमें उस कोड पथ की आवश्यकता होती है जो ऐसा करता है वह io_active फ्लैग की जाँच न करे। mach_port_set_attributes हमारे लिए ऐसा करेगा।
अब हमने अपने NULL पॉइंटर राइट को kalloc.16 के अंत से एक लटकते mach port में बदल दिया है :)
हम एक ज़ोन gc का कारण बनाते हैं, जिसका लक्ष्य पोर्ट की मेमोरी को kalloc.4096 पृष्ठ के रूप में पुन: उपयोग करना है। हम पहले इसे ool_ports डिस्क्रिप्टर के रूप में पुन: उपयोग कराते हैं जहाँ ip_context क्षेत्र एक भेजने के अधिकार के साथ ओवरलैप होता है जो हम एक कैनरी पोर्ट को भेजते हैं। यह हमें कर्नेल में अपने ऑब्जेक्ट्स का अनुमानित पता सीखने देता है। फिर हम ool_desc को एक पाइप बफर से बदल देते हैं, और कुछ बदलाव के साथ यह पता लगाने में सक्षम होते हैं कि लटकता mach port मेमोरी में कहाँ है।
हम वहाँ एक नकली कर्नेल टास्क पोर्ट बनाते हैं, फिर सफाई करते हैं।
विश्वसनीयता: शोषण काम करता है, जो मेरा लक्ष्य था :) विश्वसनीयता लगभग 30% हो सकती है, यह सब इस पर निर्भर करता है कि आप प्रारंभिक ओवरफ्लो और परीक्षण लूप को कितनी जल्दी कर सकते हैं। यदि कोई और चीज़ आकर kalloc.16 में आवंटन या मुक्ति करती है, तो आप एक freelist प्रविष्टि या कुछ और भ्रष्ट करने की संभावना बढ़ा देते हैं और panic होगा।
मुझे यकीन है कि शोषण को और अधिक विश्वसनीय बनाया जा सकता है; मैंने इसे केवल उस बिंदु तक पहुँचाया है जहाँ मैंने प्रदर्शित किया है कि यह बग शोषण योग्य है। यदि आप इसे एक प्रारंभिक बिंदु के रूप में लेना चाहते हैं और यह प्रदर्शित करना चाहते हैं कि विश्वसनीयता में सुधार कैसे किया जाए, तो मुझे एक ब्लॉग पोस्ट पढ़ना अच्छा लगेगा! मुझे लगता है कि इसमें वास्तव में kalloc.16 आवंटन की निगरानी करना और यह समझना शामिल होगा कि विफलता के मामले क्या हैं और उन्हें कैसे रोका जा सकता है।
सफलता दर सबसे अधिक तब होती है जब डिवाइस को रिबूट किया गया हो और कुछ समय के लिए निष्क्रिय छोड़ दिया गया हो।
सफाई: यदि शोषण काम करता है, तो इसे अपने आप सफाई करनी चाहिए और डिवाइस को panic नहीं करना चाहिए। नकली कर्नेल टास्क पोर्ट जीवित रहेगा।
कर्नेल मेमोरी पढ़ने और लिखने के लिए kmem.h में फ़ंक्शन का उपयोग करें। यदि आप इस प्रक्रिया से बाहर निकलने के बाद कर्नेल मेमोरी एक्सेस रखना चाहते हैं, तो tfp0 के लिए एक भेजने का अधिकार वहाँ बनाए रखें।
मैंने परीक्षण किया: iPod Touch 6G, iPhone 6S, iPhone SE, iPhone 7, iPhone 8 यह iOS 11 से iOS 11.3.1 पर काम करना चाहिए।