
रूट तक विशेषाधिकार बढ़ाने के लिए एक विश्वसनीय एक्सप्लॉइट + राइट-अप। (Ubuntu 22.04 पर परीक्षित)
CVE-2022-37706

नमस्ते दोस्तों, इस बार मैं एक हालिया 0-day के बारे में बात करने वाला हूँ जो मुझे Linux के
मुख्य विंडो मैनेजरों में से एक Enlightenment में मिला (https://www.enlightenment.org/).
यह 0-day किसी भी यूज़र को बहुत आसानी से और तुरंत root विशेषाधिकार दे सकता है।
एक्सप्लॉइट Ubuntu 22.04 पर परीक्षण किया गया है, लेकिन यह किसी भी डिस्ट्रो पर बढ़िया काम करना चाहिए।
सबसे पहले, Enlightenment एक विंडो मैनेजर, कंपोज़िटर और न्यूनतम डेस्कटॉप है
Linux (प्राथमिक प्लेटफ़ॉर्म), BSD और किसी भी अन्य संगत UNIX सिस्टम के लिए।
मैंने इस विंडो मैनेजर को थोड़ा प्रयोग करने के लिए इंस्टॉल किया। यह मेरे लिए दिलचस्प था
क्योंकि इसमें बहुत सारे टूल हैं और सच कहूँ तो यह काफी बढ़िया दिखता है।
apt install enlightenment का उपयोग करके पैकेज इंस्टॉल करने के बाद मैंने अपने सिस्टम पर
इंस्टॉल की गई फाइलों और डायरेक्टरी की जाँच की, बहुत सारे मॉड्यूल और बहुत सारे हेल्पर
बाइनरी थे, लेकिन सबसे दिलचस्प यह है:
➜ enlightenment cd /usr/lib/x86_64-linux-gnu/enlightenment/
➜ enlightenment find . -perm -4000
./utils/enlightenment_ckpasswd
./utils/enlightenment_system
./utils/enlightenment_sys
यह कुछ SUID बाइनरी इंस्टॉल करता है, फिर मैं सोच रहा था कि क्या मैं उनमें से किसी एक का उपयोग कर सकता हूँ
root तक विशेषाधिकार बढ़ाने के लिए, बाइनरी सभी सुरक्षित दिखने वाली और अच्छी तरह से कोडित थीं।
जिस बाइनरी के बारे में हम बात करेंगे वह enlightenment_sys है।
किसी भी अन्य लक्ष्य की तरह हमने कुछ प्रारंभिक मूल्यांकन करने के बाद लागू करने के लिए एक रणनीति चुनी
मेरा ब्लॉग यहाँ देखें यदि अभी तक नहीं देखा है (https://pwn-maher.blogspot.com/2020/10/vulnerability-assessment.html)
मैंने Top-Down दृष्टिकोण से कोड का ऑडिट किया।
और चूँकि यह विंडो मैनेजर ओपन सोर्स है, सोर्स कोड उन सभी बाइनरी और मॉड्यूल के लिए
उपलब्ध होगा।
इसलिए सबसे पहले मैंने सारा सोर्स कोड प्राप्त करने के लिए apt source enlightenment चलाया,
और थोड़ी खोजबीन के साथ हम लक्ष्य बाइनरी कोड तक पहुँच सकते हैं।
लेकिन बाइनरी को डीबग करने के लिए मैंने इसे विश्लेषण के लिए Ghidra में लोड किया और एड्रेस पाने के लिए
ब्रेकपॉइंट्स सेट करने आदि के लिए।
पहली कोशिश में कोई सिम्बल नहीं मिले, लेकिन हाँ उनकी कोई ज़रूरत नहीं थी क्योंकि यह
अपेक्षाकृत छोटी बाइनरी निकली।
हैरानी की बात है कि मुझे सीधे src देखने की तुलना में Ghidra का डिकंपाइल्ड स्यूडो-कोड देखना
बहुत अच्छा लगा (मैक्रोज़ से बचना, उन जाँचों से भी बचना
जो कोड के किसी विशिष्ट ब्लॉक को कंपाइल करने के लिए उपयोग किए जा रहे OS के विरुद्ध होती हैं)।
तो चलिए विश्लेषण शुरू करते हैं।
1- बाइनरी के साथ खेलें।
आइए अपने लक्ष्य के बारे में कुछ जानकारी देखने के लिए फ़ाइल चलाएँ:

बाइनरी चलाने से कोई आउटपुट नहीं मिलता:

--help तर्क देने पर यह आउटपुट मिला:

माफ़ कीजिए, मैं इसका उपयोग root पाने के लिए करूँगा।
अगला, चलिए बस strace करते हैं और देखते हैं कि क्या यह कोई संदिग्ध syscall जैसे
execve या openat का उपयोग करेगा:
strace ./enlightenment_sys 2>&1 | grep open

यह केवल उन स्थानों पर ज्ञात लाइब्रेरीज़ खोलता है जहाँ हमें छेड़छाड़ करने की अनुमति नहीं है।
strace ./enlightenment_sys 2>&1 | grep exec

2- चलिए बाइनरी को रिवर्स इंजीनियर करते हैं और फिर इसे एक्सप्लॉइट करते हैं।
मैंने एक नया Ghidra प्रोजेक्ट बनाया, और इस विशिष्ट बाइनरी को लोड किया।
क्योंकि सिम्बल नहीं मिले थे, हम entry का उपयोग करके main फ़ंक्शन को पहचान सकते हैं।
entry फ़ंक्शन का पहला तर्क स्वयं main होता है।
भविष्य के संदर्भों के लिए मैंने इसका नाम बदलकर main कर दिया।
थोड़ा नीचे स्क्रॉल करने पर मैं पहले से ही system() फ़ंक्शन का उपयोग होते देख सकता हूँ।
एक pwner के रूप में मैं इस विशेष फ़ंक्शन को स्पॉन करने के लिए चुनौतियों पर दिन बिताता हूँ x)
मैंने बाइनरी को मेमोरी करप्शन बग या कुछ हीप समस्याओं की तलाश में रिवर्स किया
, लेकिन वास्तव में यह एक अजीब Command Injection था।
बाइनरी system चलाने से पहले सभी सुरक्षा सावधानियाँ बरतती है, लेकिन दुर्भाग्य से हम
हमेशा अपने इनपुट को वहाँ इंजेक्ट कर सकते हैं।

ठीक है, अब चलिए बाइनरी को ऊपर से हमारे system फ़ंक्शन तक चलते हैं, वहाँ हमारे इनपुट को
इंजेक्ट करने की कोशिश करते हैं।
सबसे पहले बाइनरी सिर्फ जाँचता है कि पहला तर्क --help या -h है या नहीं और वह
संदेश दिखाता है जो हमने पहले देखा था।

दूसरा, यह अपने विशेषाधिकारों को root तक बढ़ाता है।

अगला, यह लगभग सभी पर्यावरण चर (सुरक्षा सावधानियाँ) अनसेट कर देता है ताकि
किसी अन्य अनपेक्षित बाइनरी को न बुलाया जाए।

तो अगर हमारे द्वारा दर्ज किया गया पहला तर्क "mount" है तो यह इस शाखा में प्रवेश करेगा, दिए गए कुछ
फ्लैग्स की जाँच करेगा, वे फ्लैग्स स्टैक पर सेट होंगे।
अगला यह जाँचता है कि mount के बाद अगला पैरामीटर UUID= है या नहीं, हम यहाँ प्रवेश नहीं करना चाहते
इसलिए हमने "/dev/../tmp/;/tmp/exploit" दिया।

इस तरह हम लाइन 410 पर जाँच पास कर लेते हैं, यानी strncmp जाँच।
क्योंकि अगर यह /dev/ से शुरू नहीं होता है तो बाइनरी बाहर निकल जाएगी।
अगला, उस फ़ाइल पर stat64 का कॉल है जो हमने प्रदान की थी, ध्यान दें कि हम ";" नामक
एक फ़ोल्डर बना सकते हैं और यही कमांड इंजेक्शन का कारण बनेगा।
अब तक, एक्सप्लॉइट पहले ही यह फ़ाइल /dev/../tmp/;/tmp/exploit बना चुका है,
लेकिन यह वह एक्सप्लॉइट नहीं है जिसे कॉल किया जाएगा।


अब हम system() के करीब पहुँच रहे हैं।
अब p (पॉइंटर) हमारी SUID बाइनरी को दिए गए अंतिम तर्क,
/tmp///net पर अपडेट हो जाता है।
जब हम /tmp/net पास कर सकते हैं तो /tmp///net क्यों प्रदान करें?
हम इस जाँच को बायपास करेंगे:
if (((next_next == (char *)0x0) || (next_next[1] == '\0')) || ((long)next_next - (long)p != 6))
हमें /tmp/net का मौजूद होना ज़रूरी था और /tmp/// की लंबाई 6 होनी चाहिए।
अब अंतिम stat64 "/dev/net" की मौजूदगी की जाँच करेगा
__snprintf_chk(cmd,0x1000,1,0x1000,"/dev%s",next_next);
और यह उसे ढूँढ लेगा, इसलिए हम वह अंतिम जाँच पास कर लेते हैं।
अब यह कुछ फाइलों की उपलब्धता की जाँच करेगा, लेकिन यह इस बिंदु पर महत्वपूर्ण नहीं है,
क्योंकि हम पूरी तरह तैयार हैं और मनमाना Command Execution ट्रिगर करने के बेहद करीब हैं।
अब eina_strbuf_new() केवल उस कमांड को इनिशियलाइज़ करेगा जो system को पास किया जाएगा,
समस्या यह है कि हमने इसे इस प्रकार दर्ज किया: