
डेमो CVE-2019-2215 (Bad Binder) Android Q के लिए
यह रिपॉजिटरी CVE-2019-2215 (Bad Binder) भेद्यता पर शोध और Kotlin/Jetpack Compose के साथ Android के लिए एक कार्यशील प्रोटोटाइप एक्सप्लॉइट लिखने का एक छोटा परीक्षण प्रोजेक्ट है।
README में मैं:
रिपॉजिटरी में एक GitHub Actions वर्कफ़्लो कॉन्फ़िगर किया गया है, जो प्रत्येक push/PR पर ./gradlew assembleDebug कमांड के साथ प्रोजेक्ट बनाता है और तैयार badbinder-debug.apk को एक आर्टिफैक्ट के रूप में प्रकाशित करता है।
इसे इस प्रकार डाउनलोड किया जा सकता है:
badbinder-debug-apk संग्रह लें।यह सुविधा के लिए किया गया है, यदि कोई स्थानीय वातावरण सेट किए बिना बस एप्लिकेशन का परीक्षण करना चाहता है।
CVE-2019-2215 Android कर्नेल की Binder IPC उपप्रणाली में एक Use-After-Free (UAF) है।
सरलीकृत:
struct binder_thread है जो Binder कॉल करने वाले थ्रेड का वर्णन करती है;waitqueue) में बनी रहती है;remove_wait_queue में पहले से मुक्त मेमोरी के साथ काम करने का प्रयास करता है, जो एक क्लासिक UAF परिदृश्य खोलता है;अधिक विस्तृत सैद्धांतिक विश्लेषण मैंने सामग्री के अनुसार किया:
असाइनमेंट के अनुसार Android 10.0 (Q) x86_64 इमेज वाले AVD का उपयोग करने की सिफारिश की गई है।
मैंने निम्नलिखित किया:
/dev/binder डिवाइस मौजूद है।adb shell
ls -l /dev/binder
इस स्तर पर मुझे एक अप्रिय तथ्य का सामना करना पड़ा:
वर्तमान में आधुनिक AVD इमेज पहले से पैच किए गए कर्नेल के साथ आती हैं, जिसमें CVE-2019-2215 बंद कर दी गई है। अर्थात्, आधुनिक आधिकारिक एमुलेटर पर वास्तव में root प्राप्त करना संभव नहीं होगा — एक्सप्लॉइट बाद के चरणों में विफल हो जाता है या बस विशेषाधिकार वृद्धि नहीं देता।
अंततः मैं AVD का उपयोग एक्सप्लॉइट के तर्क को पुन: प्रस्तुत करने के लिए प्रशिक्षण उपकरण के रूप में करता हूँ:
addr_limit को अधिलेखित करने के प्रयास का निरीक्षण करता हूँ,यह एक महत्वपूर्ण बारीकियाँ है: नीचे दिया गया सभी कोड और रिपोर्ट शैक्षिक हैं, 'लड़ाकू' नहीं।
मैंने एक छोटा Android एप्लिकेशन बनाया:
मुख्य चरण:
Android Studio में एक सामान्य प्रोजेक्ट बनाया (Kotlin, न्यूनतम Android 10 समर्थन)।
NDK और CMake को जोड़ा।
एक्सप्लॉइट के साथ एक मूल फ़ाइल जोड़ी (वही cve-2019-2215.c जिसमें फ़ंक्शन leak_task_struct, overwrite_addr_limit, आदि हैं)।
CMakeLists.txt में libcve-2019-2215.so का निर्माण जोड़ा।
MainActivity में:
init {
System.loadLibrary("cve-2019-2215")
}
external fun runNativeExploit(): String
external fun setNativeLogger(logger: NativeLogger)
Kotlin पक्ष पर ExploitViewModel बनाया, जो NativeLogger इंटरफ़ेस को लागू करता है और सभी संदेशों को StateFlow<List<String>> में संग्रहीत करता है। UI इस प्रवाह की सदस्यता लेता है और 'टर्मिनल' में लॉग प्रदर्शित करता है।
गतिविधि शुरू होने पर मैं setNativeLogger(viewModel) कॉल करता हूँ ताकि मूल कोड को वह ऑब्जेक्ट मिल जाए जिसमें स्ट्रिंग भेजी जा सकें।
एप्लिकेशन बनाएँ और स्थापित करें:
./gradlew installDebug
AVD और स्वयं एप्लिकेशन लॉन्च करें।
स्क्रीन पर मुझे एक 'टर्मिनल' और एक RUN EXPLOIT बटन दिखाई देता है।
दबाने पर:
runNativeExploit() कॉल करता है।वास्तविक कमजोर कर्नेल पर मैं अंत में कुछ इस प्रकार देखने की उम्मीद करूँगा:
[+] Selinux changed: Permissive now.
[+] Root escalation successful!
uid=0(root)...
वर्तमान Android 10 एमुलेटर पर, निश्चित रूप से, ऐसा नहीं होता है, लेकिन बाकी सब कुछ — task_struct लीक, addr_limit को अधिलेखित करने का प्रयास, cred और kernel_base की गणना — एक 'परिदृश्य' के रूप में काम करता है, जो असाइनमेंट के लिए आवश्यक था।
नीचे विशिष्ट C फ़ंक्शन से जुड़ा एक्सप्लॉइट का तार्किक आरेख है।
उच्च-स्तरीय योजना इस प्रकार है:
struct binder_thread ऑब्जेक्ट पर UAF बनाएँ और इसका उपयोग करके अपनी प्रक्रिया के task_struct का पता लीक करें (leak_task_struct)।task_struct में addr_limit फ़ील्ड को अधिलेखित करें (overwrite_addr_limit) — इससे आगे copy_to_user / copy_from_user के लिए user-space और kernel-space पतों के बीच की बाधा हट जाती है।arb_read / arb_write)।cred और कर्नेल बेस का पता लगाएँ (verifying), फिर:
selinux_enforcing = 0),cred फ़ील्ड को अधिलेखित करें (runNativeExploit)।समानांतर में, मैंने JNI लॉगर को एकीकृत किया ताकि ये सभी चरण सीधे UI में दिखाई दें।
task_struct पता लीक (leak_task_struct)मुख्य फ़ंक्शन:
void leak_task_struct() {
android_log("[*] Starting leak_task_struct...");
cpu_set_t cpu_set;
CPU_ZERO(&cpu_set);
CPU_SET(0, &cpu_set);
ret = sched_setaffinity(0, sizeof(cpu_set), &cpu_set);
assert(ret >= 0);
...
}
फ़ंक्शन क्या करता है:
थ्रेड को CPU 0 पर फिक्स करता है (sched_setaffinity), ताकि कर्नेल आवंटक का व्यवहार अधिक पूर्वानुमेय हो। यह UAF शोषण की स्थिरता में सुधार करता है।
/dev/binder खोलता है, epoll डिस्क्रिप्टर बनाता है:
fd = open("/dev/binder", O_RDONLY);
epfd = epoll_create(1000);
Binder डिस्क्रिप्टर epoll में पंजीकृत होता है:
epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
struct iovec iov_buffers[IOVEC_N] की एक सरणी तैयार करता है और मेमोरी आवंटित करता है:
spinner = mmap((void *)0x100000000, page_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
यहाँ यह महत्वपूर्ण है कि पते के निचले 32 बिट शून्य हों:
if (((long) spinner & 0xffffffff) != 0) {
android_log("[!] mmap returned wrong address!");
return;
}
यह शोषण लेखों की तकनीक से मेल खाता है: आगे कर्नेल हमारे डेटा के एक हिस्से की व्याख्या पॉइंटर्स वाली संरचनाओं के रूप में करता है, और ऐसा 'अच्छी तरह से संरेखित' पता दुरुपयोग को सरल करता है।