
PoC
CVE-2022-37706

नमस्ते दोस्तों, इस बार मैं एक हालिया 0-दिन (ज़ीरो-डे) के बारे में बात करने जा रहा हूँ जो मैंने लिनक्स के एक मुख्य विंडो मैनेजर एनलाइटनमेंट (Enlightenment) (https://www.enlightenment.org/) में पाया है।
यह 0-दिन किसी भी उपयोगकर्ता को बहुत आसानी से और तुरंत रूट विशेषाधिकार दे देगा।
यह एक्सप्लॉइट उबंटू 22.04 पर परीक्षण किया गया है, लेकिन किसी भी डिस्ट्रो पर ठीक काम करना चाहिए।
सबसे पहले, एनलाइटनमेंट एक विंडो मैनेजर, कम्पोज़िटर और न्यूनतम डेस्कटॉप है जो लिनक्स (प्राथमिक प्लेटफ़ॉर्म), बीएसडी और किसी भी अन्य संगत 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 बायनेरिज़ स्थापित करता है, फिर मैं सोच रहा था कि क्या मैं उनमें से किसी एक का उपयोग करके रूट तक विशेषाधिकार बढ़ा सकता हूँ, बायनेरिज़ सभी सुरक्षित दिखते थे और अच्छी तरह से कोडित थे।
जिस बाइनरी के बारे में हम बात करेंगे वह enlightenment_sys है।
किसी भी अन्य लक्ष्य की तरह हम कुछ पूर्व-मूल्यांकन करने के बाद लागू करने के लिए एक रणनीति चुनते हैं
अभी तक नहीं देखा तो मेरा ब्लॉग यहाँ देखें (https://pwn-maher.blogspot.com/2020/10/vulnerability-assessment.html)
मैंने कोड का ऊपर-से-नीचे (Top-Down) दृष्टिकोण से ऑडिट किया।
और चूँकि यह विंडो मैनेजर ओपन सोर्स है, स्रोत कोड उन सभी बायनेरिज़ और मॉड्यूल के लिए उपलब्ध होगा।
तो सबसे पहले मैंने apt source enlightenment किया ताकि सारा स्रोत कोड प्राप्त कर सकूँ,
और थोड़ी खुदाई करके हम लक्ष्य बाइनरी कोड तक पहुँच सकते हैं।
लेकिन बाइनरी को डीबग करने के लिए मैंने इसे विश्लेषण के लिए घिड्रा (Ghidra) में लोड किया और पतों (addresses) को ब्रेकपॉइंट सेट करने आदि के लिए प्राप्त किया।
पहले प्रयास में कोई प्रतीक (symbols) नहीं मिले, लेकिन हाँ, उनकी कोई आवश्यकता नहीं थी क्योंकि यह एक अपेक्षाकृत छोटी बाइनरी निकली।
हैरानी की बात है, मुझे घिड्रा के डीकंपाइल्ड स्यूडो-कोड को देखना सीधे src को देखने की तुलना में बहुत सुखद लगा (मैक्रोज़ से बचें, उन जाँचों से भी बचें जो OS के विरुद्ध किसी विशिष्ट कोड ब्लॉक को संकलित करने के लिए उपयोग होती हैं)।
तो चलिए विश्लेषण शुरू करते हैं।
1- बाइनरी के साथ खेलें।
आइए अपने लक्ष्य के बारे में कुछ जानकारी देखने के लिए फ़ाइल चलाएँ:
Screenshot
बाइनरी चलाने पर कोई आउटपुट नहीं मिलता:
Screenshot
--help तर्क देने पर यह आउटपुट मिला:
Screenshot
क्षमा करें, मैं इसका उपयोग रूट प्राप्त करने के लिए करूँगा।
अगला चलिए बस strace करते हैं और देखते हैं कि क्या यह कोई संदिग्ध syscall जैसे execve या openat का उपयोग करेगा:
strace ./enlightenment_sys 2>&1 | grep open
Screenshot
यह केवल ज्ञात लाइब्रेरीज़ को उन स्थानों पर खोलता है जहाँ हमें छेड़छाड़ करने की अनुमति नहीं है।
strace ./enlightenment_sys 2>&1 | grep exec
Screenshot
2- चलिए बाइनरी को रिवर्स इंजीनियर करते हैं और फिर इसका शोषण करते हैं।
मैंने एक नया घिड्रा प्रोजेक्ट बनाया, और मैंने इस विशिष्ट बाइनरी को लोड किया।
चूँकि प्रतीक नहीं मिले, हम entry का उपयोग करके main फ़ंक्शन का पता लगा सकते हैं।
entry फ़ंक्शन का पहला तर्क स्वयं main है।
मैंने भविष्य के संदर्भों के लिए इसे main नाम दिया।
थोड़ा नीचे स्क्रॉल करने पर मैं पहले से ही system() फ़ंक्शन का उपयोग होते देख सकता हूँ।
एक पॉनर (pwner) के रूप में मैं इस विशेष फ़ंक्शन को प्राप्त करने के लिए चुनौतियों पर दिन बिताता हूँ x)
मैंने मेमोरी करप्शन बग या कुछ हीप समस्याओं की तलाश में बाइनरी को रिवर्स किया,
लेकिन वास्तव में यह एक अजीब कमांड इंजेक्शन था।
बाइनरी system चलाने से पहले सभी सुरक्षा सावधानियाँ बरतती है, लेकिन दुख की बात है कि हम हमेशा वहाँ अपना इनपुट इंजेक्ट कर सकते हैं।
Screenshot
ठीक है, अब चलिए बाइनरी को ऊपर से हमारे system फ़ंक्शन तक चलते हैं, वहाँ अपना इनपुट इंजेक्ट करने का प्रयास करते हुए।
पहले बाइनरी बस जाँचती है कि पहला तर्क --help या -h है या नहीं और वह संदेश दिखाती है जो हमने पहले देखा था।
Screenshot
दूसरे, यह अपने विशेषाधिकारों को रूट तक बढ़ाता है।
Screenshot
अगला, यह लगभग सभी पर्यावरण चरों को अनसेट करता है (सुरक्षा सावधानियाँ) ताकि किसी अन्य गैर-इच्छित बाइनरी को आमंत्रित न किया जाए।
Screenshot
इसलिए यदि हमारे द्वारा दर्ज किया गया पहला तर्क "mount" है, तो यह इस शाखा में प्रवेश करेगा, कुछ दिए गए फ्लैग की जाँच करेगा, वे फ्लैग स्टैक पर सेट हो जाएँगे।
अगला, यह जाँचता है कि क्या mount के बाद अगला पैरामीटर UUID= है, हम यहाँ प्रवेश नहीं करना चाहते, इसलिए हमने "/dev/../tmp/;/tmp/exploit" दिया।
Screenshot
इस प्रकार हम पंक्ति 410 पर strncmp जाँच पास करते हैं।
क्योंकि यदि यह /dev/ से शुरू नहीं होता है तो बाइनरी बाहर निकल जाएगी।
अगला, हमारे द्वारा प्रदान की गई फ़ाइल पर stat64 का कॉल होता है, ध्यान दें कि हम ";" नामक एक फ़ोल्डर बना सकते हैं और यह कमांड इंजेक्शन का कारण बनेगा।
अब तक, एक्सप्लॉइट पहले ही यह फ़ाइल /dev/../tmp/;/tmp/exploit बना चुका है,
लेकिन यह वह एक्सप्लॉइट नहीं है जिसे कॉल किया जाएगा।
Screenshot
Screenshot
हम अब 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);
और यह इसे ढूँढ लेगा, इसलिए हम वह अंतिम जाँच पास करते हैं।
अब यह कुछ फ़ाइलों की उपलब्धता की जाँच करेगा, लेकिन इस बिंदु पर यह महत्वपूर्ण नहीं है, क्योंकि हम सब तैयार हैं और मनमानी कमांड निष्पादन (arbitrary Command Execution) को ट्रिगर करने के करीब हैं।
अब eina_strbuf_new() बस उस कमांड को प्रारंभ करेगा जो system को पास किया जाएगा, समस्या यह है कि हमने इसे इस प्रकार दर्ज किया:
/bin/mount -o noexec,nosuid,utf8,nodev,iocharset=utf8,utf8=0,utf8=1,uid=$(id -u), "/dev/../tmp/;/tmp/exploit" /tmp///net
लेकिन बाइनरी eina_strbuf_append_printf() को कई बार कॉल करती है और यह बन जाती है:
/bin/mount -o noexec,nosuid,utf8,nodev,iocharset=utf8,utf8=0,utf8=1,uid=$(id -u), /dev/../tmp/;/tmp/exploit /tmp///net
ध्यान दें कि डबल कोट्स हटा दिए गए हैं, और हम /tmp/exploit को रूट के रूप में कॉल करने में सक्षम होंगे।
Screenshot
बाइनरी ने किसी भी गैर-इच्छित व्यवहार को कम करने की पूरी कोशिश की, लेकिन हमेशा की तरह किसी भी चीज़ को पॉन किया जा सकता है। मुझे इस तरह के तार्किक बग के माध्यम से इसका शोषण करने की उम्मीद नहीं थी।
मैं चाहता हूँ कि अगला CVE एक मेमोरी करप्शन हो जो LPE रूट की ओर ले जाए।