
Android कर्नेल भेद्यता CVE-2019-2215 को ट्रिगर करना और विश्लेषण करना
नवंबर 2017 में, syzkaller प्रणाली द्वारा लिनक्स कर्नेल में एक use-after-free बग का पता लगाया गया था। फरवरी 2018 में इसे कुछ लिनक्स कर्नेल और एंड्रॉइड संस्करणों में पैच किया गया था।
यह सुधार एंड्रॉइड मासिक सुरक्षा बुलेटिन में कभी शामिल नहीं किया गया था, इसलिए इसे Pixel और Pixel2 जैसे कई नए जारी उपकरणों में पैच नहीं किया गया।
सितंबर 2019 में, Project Zero द्वारा एंड्रॉइड को इस बग के सुरक्षा निहितार्थों के बारे में सूचित किया गया। फिर एंड्रॉइड ने इस भेद्यता को अधिक औपचारिक और ज्ञात बनाने के लिए इसे CVE-2019-2215 प्रदान किया।
CVE-2019-2215 binder.c में एक use-after-free है जो किसी एंड्रॉइड एप्लिकेशन से विशेषाधिकार की प्राप्ति (रूट एक्सेस प्राप्त करना) की अनुमति देता है। इस भेद्यता का शोषण करने के लिए उपयोगकर्ता की सहभागिता की कोई आवश्यकता नहीं है। इसके लिए केवल एक दुर्भावनापूर्ण स्थानीय एप्लिकेशन की स्थापना की आवश्यकता होती है।
यहाँ हम इस एंड्रॉइड कर्नेल भेद्यता को अधिक विस्तार से पेश करने जा रहे हैं और इस भेद्यता का उपयोग पूरे एंड्रॉइड डिवाइस का रूट एक्सेस (विशेषाधिकार वृद्धि) प्राप्त करने के लिए करेंगे।
हम इस प्रूफ ऑफ कॉन्सेप्ट (PoC) का उपयोग करेंगे:
https://github.com/cloudfuzz/android-kernel-exploitation
हम पहले आपको एंड्रॉइड एमुलेटर पर इस भेद्यता को ट्रिगर करने और कर्नेल क्रैश कराने का एक तरीका दिखाएंगे। फिर यह देखने के लिए कि यह कितना खतरनाक हो सकता है, हम अनुकरणित एंड्रॉइड डिवाइस में रूट एक्सेस प्राप्त करने के लिए PoC का उपयोग जारी रखेंगे। फिर हम यह देखने के लिए कर्नेल कोड का विश्लेषण करेंगे कि इसका कारण क्या है (स्थैतिक और गतिशील विश्लेषण)।
विश्लेषण के बाद, हम देखेंगे कि हमें रूट एक्सेस कैसे मिला। अंत में हम देखते हैं कि पैच का उपयोग करके इस भेद्यता को कैसे कम किया जाता है।
इस भेद्यता को ट्रिगर करके कर्नेल क्रैश कराने के लिए हम इन चरणों का उपयोग करते हैं:
निम्नलिखित वीडियो देखें:
[वीडियो]
इस (और अगले) अनुभाग में हम स्थैतिक और गतिशील विश्लेषण का उपयोग करके यह समझने जा रहे हैं कि क्रैश क्यों होता है।
यहाँ हम समस्या को समझने के लिए कर्नेल कोड का विश्लेषण (स्थैतिक विश्लेषण) करेंगे। crash_report.txt में KASan से रिपोर्ट है जो कहती है कि यह एक use-after-free बग है। इसका अर्थ है कि एक ऑब्जेक्ट हीप में आवंटित किया जाता है (और हमारे पास उसका एक संदर्भ होता है), फिर हम ऑब्जेक्ट को हीप से मुक्त कर देते हैं और फिर हम गलती से उसे संदर्भ द्वारा कॉल करते हैं। इस रिपोर्ट में इन तीन चरणों का स्टैक ट्रेस मुद्रित होता है।
यदि आपको ऊपर से याद है, हमने PoC में 'trigger.cpp' का उपयोग किया था।
यहाँ trigger.cpp का मुख्य कोड है:``` int main() { //1 int fd, epfd; //2 struct epoll_event event = {.events = EPOLLIN}; //3 fd = open("/dev/binder", O_RDONLY); //4 epfd = epoll_create(1000); //5 epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event); //6 ioctl(fd, BINDER_THREAD_EXIT, NULL); }
आइए देखें कि 'trigger.cpp' क्या कर रहा है।
एंड्रॉइड में (अन्य Unix-आधारित ऑपरेटिंग सिस्टम की तरह) कुछ प्रोसेस होते हैं। हम जो भी प्रोग्राम चलाते हैं, वह एक (या अधिक) प्रोसेस बनाता है और इन प्रोसेस को OS द्वारा हैंडल किया जाता है। OS उनके बीच स्विच कर सकता है (मल्टी-टास्किंग) या किसी प्रोसेस को समाप्त कर सकता है, आदि। सुरक्षा कारणों से, प्रोसेस डिफ़ॉल्ट रूप से एक दूसरे से अलग (isolated) होते हैं।
कुछ मामलों में, एक प्रोसेस को दूसरे प्रोसेस के साथ डेटा का आदान-प्रदान करने की आवश्यकता हो सकती है। इसे इंटरप्रोसेस कम्युनिकेशन (IPC) कहा जाता है। लिनक्स में प्रोसेस के बीच संवाद करने के कई तरीके हैं। एंड्रॉइड ने **'Binder'** नामक एक विशिष्ट IPC तंत्र पेश किया है। Binder इंटर-प्रोसेस संचार को सुगम बनाने के लिए एक कर्नेल ड्राइवर है।
एंड्रॉइड में, IPC कुछ कर्नेल मेथड्स को सीधे कॉल करके (उनमें से अधिकांश drivers/binder.c में हैं) या उच्च-स्तरीय इम्प्लीमेंटेशन का उपयोग करके (उदाहरण के लिए java में) किया जा सकता है।

**binder** का उपयोग करने के लिए, हमें कर्नेल binder मॉड्यूल को खोलना चाहिए। यह trigger.cpp की लाइन 3 का उपयोग करके किया जाता है। फिर हमारे पास एक फ़ाइल डिस्क्रिप्टर पॉइंटर होता है, इस fd का उपयोग करके IPC के कर्नेल इनिशिएटर और प्राप्तकर्ताओं की पहचान की जा सकती है।
ड्राइवर के साथ सभी इंटरैक्शन **'ioctl**' कमांड्स के एक छोटे से सेट के माध्यम से होंगे (BINDER_THREAD_EXIT, BINDER_WRITE_READ, ...)।
binder के बारे में अधिक: [link1](https://www.nds.ruhr-uni-bochum.de/media/attachments/files/2012/03/binder.pdf), [link2](http://rts.lab.asu.edu/web_438/project_final/CSE_598_Android_Architecture_Binder.pdf)
लिनक्स में, हमारे पास '**event polling**' नामक एक अवधारणा है। '**epoll**' API का उपयोग तब किया जाता है जब हम कई फ़ाइल डिस्क्रिप्टरों को मॉनिटर करना चाहते हैं (फ़ाइल डिस्क्रिप्टर वे होते हैं जो हमें ड्राइवर खोलने या IO के साथ काम करने आदि पर मिलते हैं)।
**epoll** एक कर्नेल स्ट्रक्चर है जिसमें दो महत्वपूर्ण फ़ील्ड होते हैं।
* interest list = उन फ़ाइल डिस्क्रिप्टरों की सूची जिन्हें हम मॉनिटर करना चाहते हैं।
* ready list = उन फ़ाइल डिस्क्रिप्टरों की सूची जो I/O के लिए तैयार हैं।
इवेंट पोलिंग का उपयोग करने के लिए, हम पहले एक epoll बनाते हैं (लाइन 4), फिर एक फ़ाइल डिस्क्रिप्टर (**fd**) से जुड़े इवेंट (&event) को कर्नेल की **epoll_ctl** विधि को कॉल करके अपने बनाए गए epoll (**epfd**) में जोड़ते या हटाते हैं (EPOLL_CTL_ADD)।
=> `epoll_event event` एक इवेंट है, जो तब ट्रिगर होता है जब संबंधित फ़ाइल (fd) रीड ऑपरेशन के लिए उपलब्ध होती है।
अब हम समझ गए हैं कि **trigger.cpp** क्या कर रहा है (गहराई में जाने की आवश्यकता नहीं!)। यह **binder** मॉड्यूल को खोलता है, उसे सुनने के लिए एक **epoll** बनाता है जब वह तैयार हो। फिर लाइन 6 पर, हम उस binder से बाहर निकलते हैं जिसे हमने लाइन 3 में शुरू किया था।
#### आवंटन:
open() को कॉल करके, हम वास्तव में **open_binder()** को कॉल करते हैं (binder.c में open() का इम्प्लीमेंटेशन), open_binder() में, एक नया **'binder_proc'** स्ट्रक्चर बनाया जाएगा और:
` fd->pricate_data = binder_proc`
**epoll_create()** को कॉल करने पर, एक नया epoll स्ट्रक्चर बनाया जाएगा और एक कतार संरचना में जोड़ा जाएगा।

**epoll_ctrl(epdf, ADD, fd, event)** को कॉल करने पर, एक नया **ep_item** बनाया जाता है, **fd** (सुनने के लिए फ़ाइल डिस्क्रिप्टर) को इस **ep_item** से संबद्ध किया जाता है, और इसे event_poll के **रेड-ब्लैक ट्री** (ep में ep_items को सहेजने के लिए एक डेटा संरचना) में डाला जाता है। यह **ep_item_poll()** को भी कॉल करता है, यह विधि ep_item के साथ कॉलबैक फ़ंक्शन संबद्धता को संभालती है।
यह नया **binder_thread** स्ट्रक्चर बनाता है (**आवंटन यहाँ होता है**), इसे **binder_proc** (ऊपर बनाया गया) से जोड़ता है, फिर एक **epoll_entry** स्ट्रक्चर बनाया जाता है, इसकी दो सूचियाँ होती हैं, **epoll_entry->wait** और **epoll_entry->whead**, इन दोनों सूचियों में पहले बनाए गए **binder_thread** का पॉइंटर होता है।