در این مقاله قصد داریم که تحلیل تکنیکال جاسوس افزار پگاسوس (Pegasus) که منتصب به شرکت NSO Group است را با هم داشته باشیم و ببینیم این جاسوس افزار نحوه عملکردش به چه صورت بوده و آسیب پذیری های باینری مورد استفاده آن چه مواردی بوده اند، همچنین تشریح فنی رفتار تاکتیکی و تکنیکی (Tactics, Techniques and Procedures) جاسوس افزار هم قدم به قدم مورد بررسی قرار خواهیم داد.
معرفی شرکت NSO
شرکت NSO یک شرکت اسرائیلی بوده است که بنیانگذاران آن Omri Lavie و Shalev Hulio هستند، شرکت NSO واقع در Herzliya در مناطق شمالی تل آویو بوده که ماموریت آن نفوذ به اهدافی خاص است، از این روی یکی از تهاجمی ترین جاسوس افزار های تلفن همراه را تا به امروز طراحی کرده است، و برای اولین بار بدست آزمایشگاه Lookout کشف و گزارش شده است. یکی از نقاط قوت این جاسوس افزار استفاده ی از آسیب پذیری های (روز صفر) سطح هسته سیستم عامل های iOS و Android بود، به همین دلیل به محض ایجاد دسترسی اولیه از سیستم عامل قربانی سعی بر ارتقاء سطح دسترسی (Privilege Escalation) میکند تا پردازش مختص به خودش را در سطح هسته سیستم عامل اجرا کند.
اما هدف این جاسوس افزار چه کسانی بود؟ روزنامه نگاران، فعالین حقوق بشر و برخی از عوامل دولتی کشور پاکستان بوده است. بنیانگذاران NSO متنصب به واحد 8200 دستگاه اطلاعاتی کشور جعلی اسرائیل هستند که در آن بخش مامور شنود و جاسوسی در حوزه شبکه های سیگنالی و مخابراطی است. این جاسوس افزار بعد از هدف قرار دادن یک فعال حقوق بشر به نام احمد منصور در امارات متحده عربی بسیار رسانه ای شد و همین موضوع موجب کشف آن در آزمایشگاه Lookout شد، که با بررسی هایی که این آزمایشگاه انجام داد متوجه این جاسوس افزار شده و گزارشها آن را منتشر کرده است.
در ژوئیه ۲۰۲۱ واشینگتن پست، گاردین، لو موند، و چهارده رسانه دیگر در گزارشی آشکار کردند که پگاسوس با آلوده سازی تلفن های اندروید و آیفون شماره تلفن ۵۰ هزار نفر را رهگیری میکرده است. بیش از ۶۰۰ سیاستمدار (سه رئیسجمهور، ده نخستوزیر، و پادشاه مراکش)، ۱۸۹ روزنامهنگار، ۶۴ مدیر شرکت، ۸۵ فعال حقوق بشر، و اعضای خانواده کشورهای عربی از اهداف پگاسوس بوده اند. روزنامه نگارانی که هدف این بدافزار بودند در خبرگزاری فرانسه، سیانان، نیویورک تایمز، و الجزیره کار میکردند.
رفتار تاکتیکی و تکنیکی (Android)
جاسوس افزار Pegasus برای هر دو سیستم عامل Android و iOS طراحی شده که TTP هر کدوم رو بهصورت مجزا بررسی و تشریح خواهیم کرد، اولین TTP مربوط به سیستم Android است که در تصویر زیر مشاهده میکنید.
ایجاد دسترسی (Initial Access)
مرحله اول حمله یعنی Initial Access یا ایجاد دسترسی را در تصویر بالا مشاهده میکنیم که تکنیک Deliver Malicious App via Authorized App Store هستش، این تکنیک به اینصورت است که یک برنامه اولیه ای وارد Play Store شده سعی بر دور زدن مکانیزم های Verify فروشگاه مربوط را دارد، بدین ترتیب قربانیان از طریق نصب این نرمافزار از روی Play Store اولین مرحله حمله را پشت سر گذرانیده اند، اما برنامه قرار است که بهصورت یک دانلودر عمل کند و فایل بهرهبرداری (Exploit) رو در ادامه بهصورت Runtime Execution اجرا کند.
یک برنامه میتواند پس از نصب، کد پویا (که در بسته برنامه اصلی گنجانده نشده است) را دانلود و اجرا کند تا از تکنیک های تحلیل استاتیک (و تکنیکهای تحلیل پویا) که برای بررسی برنامه ها یا بررسی فروشگاه برنامه استفاده میشوند، اجتناب کند. در اندروید، کد پویا میتواند شامل کد بومی، کد Dalvik است باشد یا کد جاوا اسکریپتی که از قابلیت Android WebView و همچنین JavascriptInterface استفاده میکند.
در مرحله بعدی کد دانلود و اجرا میشود، کد دانلود در اصل کد بهرهبرداری Pegasus از آسیب پذیری روز صفر سطح هسته (Zero Day Kernel Exploit) است که مبهم سازی (Obfuscated Files or Information) شده بوده و قابلیت شناسایی نخواهد داشت، در هر صورت مسئله مهمی که در این تکنیک وجود دارد این است که مهاجم میبایست تمام تلاش خودش رو بکار بگیرد تا بواسطه حساب کاربری Developer در App Store مربوط بدافزار شمار یک خود را رو Autorize کرده و مورد تایید مکانیزم های شناسایی کننده آن App Store قرار دهد.
عملکرد مکانیزم PHAs
گوگل در Google Play خودش از مکانیزمی با نام Potentially Harmful Applications که در مقابله با بدافزارها و جاسوس افزارها عمل میکند. Google Play Protect برنامههایی را که به عنوان مضر امضا میشوند را حذف میکند، زیرا برنامه حاوی رفتارهای مخرب است. برای مثال، دستگاهی که آخرین نسخه اندروید را اجرا میکند تحت تأثیر برنامههای مضری قرار نمیگیرد که از API های منسوخ برای انجام رفتارهای مخرب استفاده میکنند، اما دستگاهی که هنوز نسخه بسیار اولیه Android را اجرا میکند ممکن است در خطر باشد.
چندین دسته برای طبقهبندی PHA ها وجود دارد که به Play Protect کمک می کند آنها را شناسایی کرده و اقدام مناسب را تعیین کند. این دسته ها شامل برنامه های مخرب مانند تروجان ها، جاسوس افزارها و برنامه های فیشینگ و همچنین برنامه های مورد نظر کاربر میشوند. اگر Play Protect یک PHA را شناسایی کند، یک هشدار نمایش می دهد. برای برخی از برنامه های مخرب، Play Protect به طور خودکار برنامه را غیرفعال یا حذف می کند. هنگامی که Play Protect تشخیص می دهد که یک PHA دارای ویژگی هایی از چندین دسته است، برنامه را بر اساس مضرترین ویژگی ها طبقهبندی می کند. به عنوان مثال، اگر برنامه ای در هر یک از دو دسته باج افزار و جاسوس افزار باشد پیغام Verify Apps آن را به عنوان باج افزار شناسایی خواهد کرد.
بدافزار Chrysaor در اندروید
اواخر سال گذشته، چندین دستگاه Android مشخص شده بود که به جاسوس افزار Pegasus آلوده هستند و عامل اصلی نصب جاسوس افزار بدافزاری با نام Chrysaor بوده است. اگر چه برنامه ها هرگز در Google Play در دسترس نبودند، اما بلافاصله با استفاده از Verify Apps برنامه به مشکل خورده و موجب شناسایی آن شده است. ما اطلاعاتی را از دستگاههای آسیبدیده جمعآوری کردیم و همزمان، سعی کردیم برنامههای Chrysaor را برای درک بهتر تأثیر آن بر کاربران بهدست آوریم.
استفاده از آسیب پذیری WhatsApp
در نسخه های جدید Pegasus برای اندروید، جاسوس افزار از یک آسیب پذیری Zero Click بهرهبرداری کرده است که مبتنی بر برنامه پیام رسان WhatsApp در نسخه v2.19.134 و WhatsApp Business در نسخه v2.19.44 بوده که با شناسه CVE-2019-3568 ثبت شده است این آسیب پذیری همچنین در سیستم عامل iOS در نسخه v2.19.51 هم وجود داشته که در قسمت خودش بهش پرداخته میشود. اما این آسیب پذیری از نوع Buffer Overflow بوده که مبتنی بر پروتکل ارتباطی Secure Real-time Transport Protocol است که یک پروتکل مبتنی بر ارتباطات VoIP است.
بررسی وصله WhatsApp
در تصویر بالا اشاره شده که یک تابع برای کنترل ورودی RTCP Handler پروتکل به چه صورت است، تابع مربوط بررسی میکند که آرگومان ورودی آیا طولی برابر با اندازه حداکثر 1480 بایت (0x5C8
) بوده است یا خیر. جالبه که ماژول دارای آسیب پذیری که مامور ایجاد نشست ارتباط تماس صوتی واتس اپ مبتنی بر پروتکل RTCP است، قبل از برقراری ارتباط فراخوانی میشود، یعنی آسیب پذیری بدون برقراری ارتباط کامل صوتی میتواند اصطلاحا Trigger شود.
در تصویر بالا مشاهده میشه که یک شرط اول وجود داره که طول فیلد پکت پروتکل اگر کمتر از طول ورودی باشه وارد بلاک شرط میشه، این اولین مرحله چک هستش اما در ادامه یکبار دیگر قبل از کپی مقادیر بواسطه تابع qmemcpy
یک بار دیگر شرطی قرار داده شده که اینبار بررسی میکند طول ورودی از 1480 بایت بیشتر نباشد. در این دو مورد هم اعتبارسنجی میشود که طول ورودی از فیلد طول بسته پروتکل از تجاوز نکرده باشد و در بررسی دوم مشخص میشود که طول ورودی از 1480 تجاوز نکرده باشد.
اجرا (Execution)
مرحله دوم حمله Execution است که در جاسوس افزار Pegasus از روش Broadcast Receivers برای مرحله Execution استفاده شده است، این روش به این صورت است که برنامه اولی که بهصورت Autorized در Google Play قرار داشته است اقدام به دانلود کد بهرهبرداری (Exploit) کرده و آن را بهصورت Broadcast یا پخش شده اجرا می نماید، این نوع اجرا نیازمند Register کردن Receive Broadcasting خواهد بود که در زمان اجرا (Runtime) یک برنامه یا System Components را به اجرا درآورد، البته این ویژگی از نسخه Android 8 به بعد و API 26 به بالا تغییر کرده و میبایست برنامه هایی که نیازمند Broadcasting هستند در Manifest برنامه متقاضی مشخص شده و حتما عملیات Registering رو انجام دهند.
بعد از نصب نرمافزار، تا زمانی که یک بار دستگاه بازیابی مجدد یا Reboot نشود برنامه غیر فعال خواهد ماند و بعد از Boot شدن و باز شدن توسط کاربر یک android.intent.action.BOOT_COMPLETED
که بهصورت Broadcast ارسال میشود فعال شده و راهاندازی میگردد، برنامه در اولین مرحله راهاندازی سعی بر انجام یکسری پیکربندی داشته که آنها با استفاده از ارسال Query String به تاریخچه مرورگر و مقادیر بر URL انجام میدهد، همچنین سعی بر خواندن فایل های محلی کرده است. در ادامه کد میبینیم که شرطی وجود داره که میگه اگر مقدار Query String زده شده null
نبوده و مساوی با رشته rU8IPXbn
بود بیا و سرشماری url
ها و ip address
هارا پیدا کن. اما این رشته rU8IPXbn
دارای معنی های مختلفی در کاراکتر های خود است.
- کاراکتر
t
معادل توکن مورد استفاده برای تولید امضا برای دستورها و شناسایی آنها در دستگاه کاربر است. - کاراکتر
c
معادل یک فرمان و امضای رمزگذاری شده بهصورت Base64 است و با همان فرمت دستورها ارسال شده از طریق پیامک دریافت خواهد شد. - کاراکتر
d
که معادل گزینه پیکربندیuserNetwork
است که شامل کد کشور تلفن همراهITU-T E.212 (MCC)
است. - کاراکتر
b
که معادل گزینه نصب پیکربندی ها بهصورت Boolean خواهد بود. - کاراکتر
r
که معادل گزینه پیکربندی بهصورت Boolean برایwindowYuliyus
را تنظیم می کند.
پس از بارگیری یک پیکربندی، بدافزار تلاش میکند تا مسیرهای آن را پاک کند حذف URL حاوی اطلاعات پیکربندی از تاریخچه مرورگر پاک کند، حذف URL حاوی اطلاعات پیکربندی از تاریخچه مرورگر.
ماندگاری (Persistence)
مرحله بعدی که این جاسوس افزار پیادهسازی میکند بحث ماندگاری و تثبیت جایگاه خود در سیستم عامل قربانی بوده است، به همین منظور از دو تکنیک Broadcast Receivers و Modify System Partition استفاده شده که اولین مورد رو پیشتر توضیح دادیم و نحوه عملکرد اون رو متوجه شدیم، اما یک نکته! ویژگی Broadcast Receivers که میتواند در فراخوانی بدافزار مربوط در صورت بسته شدن، استفاده شود میتواند تثبیت بدافزار اولیه رو تضمین کند. اما در خصوص تکنیک دوم یعنی Modify System Partition هم میبایست توضیحاتی داده شود که نحوه عملکرد آن برمیگردد به بعد از عملیات ارتقاء سطح دسترسی (Privilege Escalation) که بواسطه یک آسیب پذیری Binary عملیاتی شده و سطح دسترسی ارتقاء مییابد، بعد از ارتقاء سطح دسترسی جاسوس افزار Pegasus اقدام به اصلاح پارتیشن های سیستم عامل اندرویدکرده و در این اصلاح روند اجرای جاسوس افزار رو در Bootloader قرار خواهد داد تا اگر سیستم عامل قربانی شروع مجدد یا (Restart) شد، جاسوس افزار بواسطه دستکاری پارتیشن ها و تزریق کد آغازگر در Bootloader اقدام به اجرای مجدد کرده و جاسوس افزار اصلی فعال خواهد شد.
تجزیه و تحلیل نشان داد که اگر Pegasus برای اندروید روی یک دستگاه سامسونگ اجرا شود و می توانست دسترسی سطح هسته را به دستآورد، مکانیزم بروز رسانی سیستم را حذف خواهد کرد، این مسدود سازی بواسطه غیر فعال کردن و حذف این Component است com.sec.android.fotaclient
و در ادامه کد خواهیم دید و به اجزای مختلف آن اجازه میدهد تا در پارتیشن system/
رو دستکاری کرده و این دستگاه رو در معرض خطر باقی خواهد گذاشت. Pegasus در مرحله دیگری با 0
کردن مقدار Settings.System.SOFTWARE_UPDATE_AUTO_UPDATE
مکانیزم بروز رسانی اتوماتیک رو هم خاموش خواهد کرد.
ارتقاء سطح دسترسی (Privilege Escalation)
در این قسمت عملیات ارتقاء سطح دسترسی انجام میشود تا سطح دسترسی جاسوس افزار از UserLand به KernelLand ارتقاء بیابد و دسترسی کامل به تمامی Component های سیستم عامل پیدا کرده و عملیاتی هایی که نیازمند سطح دسترسی Root هستند رو عملیاتی سازد، این مرحله بواسطه تکنیک Exploit OS Vulnerability بوده است که به معنی بهرهبرداری از آسیب پذیری های باینری از درایور های در سطح هسته است که در اینجا آسیب پذیری مورد استفاده جاسوس افزار Pegasus رو تشریح خواهیم نمود، چرا که آسیب پذیری مورد استفاده یک آسیب پذیری روز صفر بوده و همواره میتواند مورد استفاده و بهرهبرداری قرار گیرد.
ارتقاء سطح دسترسی در Chrysoar
تجزیه و تحلیل این نمونه نشان داد که تنها هدف آن شروع اتصال به یک آدرس راه دور باشد، و در فایل data/data/com.network.android/.coldboot_init
، قبل از کپی کردن آن در mnt/obb/.coldboot_init/
تغییراتی در مجوز های این فایل به 0711
انجام دهد، این دانلود فقط برای فایل libsgn.so
بوده که بهصورت باینری دریافت شده است. بخشی از کد جاوا مامور دانلود را در تصویر بالا مشاهده میکنید و در زیر بخشی از کد از فایل libsgn.so
آمده است که تلاش می کند بار بازیابی شده را در مسیرهای مختلف بنویسد.
استفاده از Framaroot
در بدافزار Chrysoar رصد شده که در نسخه های قدیم خود از برنامه ای با نام Framaroot استفاده میکند که یک ابزار از چندین Exploit برای نسخه های مختلف اغلب سطح پایین در اندروید است که امکان ارتقاء سطح دسترسی را به بدافزار خواهد داد. این ابزار از نسخه اندروید 4.4 تا 8.0 به بالا را میتواند به راحتی ارتقاء سطح دسترسی دهد. کد های بهرهبرداری Frameroot این موارد هستند:
- کد بهرهبرداری
Gandalf
برای دستگاه های Qualcomm - کد بهرهبرداری
Boromir
,Faramir
وBarahir
برای دستگاه های MTK - کد بهرهبرداری
Pippin
برای دستگاه های Huawei K3V2 - کد بهرهبرداری
Gollum
برای دستگاه های AMLogic - کد بهرهبرداری
Legolas
وAragorn
برای دستگاه های سامسونگ - کد بهرهبرداری
Sam
,Frodo
,Legolas
وAragorn
برای دستگاه های Exynos - کد بهرهبرداری
Gimli
برای دستگاه های Omap36XX
و برای هر نوع از پردازنده و نسخه سیستم عامل این Exploit ها مورد استفاده قرار خواهند گرفت، لیست دستگاه های مورد پشتیبان Framaroot را در اینجا میتوانید ببینید. اما نحوه پیادهسازی به چه صورت است؟
$ cat /sdcard/myappneedsystem.apk > /system/app/myappneedsystem.apk $ pm install /system/app/myappneedsystem.apk
بعد از وارد شدن به شاخه system/app/
با استفاده از ADB یا Android Debug Bridge اقدام به نصب برنامه میکند، بدافزار Chrysoar مستقیما فایل APK فایل Framaroot را دانلود و نصب میکند، و بعد از نصب اقدام به اجرای آن میکند و در صورت موفقیت در ارتقا سطح دسترسی بقیه عملیات خود را پیش میبرد، اما در خصوص نسخه های جدید تر آسیب پذیری های بهرهبرداری مورد هدف Pegasus میتوان به آسیب پذیری CVE-2019-2215 اشاره کرد که در ادامه وارد توضیح آن میشویم.
بهرهبرداری از آسیب پذیری روز صفر سطح هسته
قبل از هر چیزی علاقمند هستم که ساختمان سیستم عامل اندروید رو یک بررسی کلی داشته باشیم و ببینیم که آسیب پذیری مورد استفاده جاسوس افزار Pegasus چگونه توانسته موجب ارتقاء سطح دسترسی خود بشه.
همونطور که در تصویر بالا مشاهده میکنید ما یک ساختار کلی از مراتب Rendering یک برنامه APK مشاهده میشه و نکته ای که اینجا برای ما مهمه اینه که یک برنامه اندرویدی میتونه با استفاده از Java API ارتباط بگیره، این Java API در اختیار JVM یا Java Virtual Machine قرار داره که مامور پارس کردن فایل فرمت APK رو بر عهده خواهد داشت، حالا همین JVM مبتنی بر درایور های سطح هسته کار کرد و عملیات Compile در لحظه برنامه های سطح بالا را پیش میبرد، در نتیجه میتوان اینطور تصور کرد که کد بهرهبرداری (Exploit) دانلود شده و مبتنی بر بهرهبرداری از آسیب پذیری سطح هسته سیستم عامل یعنی Binder IPC اجرا خواهد شد.
در اندروید (مانند سایر سیستم عامل های مبتنی بر یونیکس) ما چند فرآیند داریم. هر برنامه ای که اجرا می کنیم یک (یا چند) پردازش ایجاد می کند و این فرآیندها توسط سیستم عامل مدیریت می شوند. سیستم عامل ممکن است بین آنها جابجا شود (multi-tasking) یا یک فرآیند را خاتمه دهد و غیره. به دلایل امنیتی، فرآیندها به طور پیش فرض از یکدیگر جدا هستند. در برخی موارد، ممکن است یک فرآیند نیاز به تبادل داده با فرآیند دیگر داشته باشد. به آن ارتباطات بین فرآیندی (IPC) می گویند. راه های مختلفی برای ارتباط فرآیندها در لینوکس وجود دارد. اندروید مکانیزم IPC خاصی به نام Binder را معرفی کرد، Binder یک محرک هسته برای تسهیل ارتباطات بین فرآیندی است.
در اندروید، IPC را می توان با فراخوانی مستقیم برخی از متدهای هسته (بیشتر آنها در drivers/binder.c
) یا با استفاده از پیادهسازی های سطح بالا (مثلاً در جاوا) انجام داد. همه فعل و انفعالات در Driver از طریق یک مجموعه دستورها IOCTL رخ میدهد، دستوراتی مانند (BINDER_THREAD_EXIT، BINDER_WRITE_READ، …).
اما ماهیت کارکرد درایور Binder IPC چیست؟
آسیب پذیری Use After Free که یک آسیب پذیریه باینری بوده و مدل رخدادش به این صورت است که اگر شما در برنامه سطح بالایی اقدام به تعریف یک زیر شاخه از پردازش (Thread) کنید، اینکار بواسطه درایور Binder (Inter Process Communication) انجام خواهد شد و این درایور تحت کنترل مکانیزم SELinux محافظت شده و قوانین مدیریتی (Policy Management) بر آن جاری میشود، این درایور میتواند ارتباط پردازش های (Process) سطح کاربری (UserLand) سیستم عامل اندروید را هندل کرده و هنگام ایجاد یک زیرشاخه از پردازش (Thread) جدید، مدیریت Context را انجام داده و Node بعدی را برای ارتباط یک Device دیگر فراهم سازد.
ارتباطات بین فرآیندی
همانطور که قبلاً گفته شد، هر فرآیند اندروید فضای آدرس sandbox خاص خود را دارد. امکانات ارتباطی بین فرآیندی یا Inter-Process Communication به برنامه ها امکان میدهد سیگنال ها و داده ها را به صورت ایمن مبادله کنند، IPC اندروید بر پایه درایور Binder است که یک پیادهسازی سفارشی از OpenBinder است. اکثر سرویس های سیستم عامل اندروید و تمام سرویس های IPC سطح بالا، به Binder وابسطه هستند و به ویژگی های Binder اتصال دارند برخی از این ویژگی ها موارد زیر هستند:
- Binder Driver: درایور در سطح هسته.
- Protocol Binder: پروتکل سطح پایین مبتنی بر IOCTL که برای برقراری ارتباط با درایور Binder استفاده میشود.
- Interface Binder: یک رفتار کاملاً تعریف شده که اشیاء Binder پیادهسازی میکند.
- Object Binder: اجرای عمومی رابط.
- Service Binder: اجرای Object Binder. به عنوان مثال، خدمات مکان، و خدمات حسگر.
- Client Binder: یک شی که از سرویس Binder استفاده می کند.
چارچوب Binder شامل یک مدل ارتباطی Client – Server است. برای استفاده از IPC، برنامه ها از کلاس Binder یک Object ساخته و بهصورت پراکسی فراخوانی انجام میدهند. این Object بهصورت پراکسی به طور شفاف پارامترهای تماس را در یک بسته قرار میدهد و یک Transparently را به سرور Binder ارسال میکند که به عنوان یک درایور کاراکتر dev/binder/
پیادهسازی میشود. سرور یک Thread Pool برای رسیدگی به درخواست های دریافتی نگه می دارد و پیام ها را به Object مقصد تحویل میدهد. از منظر برنامه مشتری، همه اینها مانند یک روش معمولی به نظر میرسد، تمام کارهای سنگین توسط چارچوب Binder انجام می شود.
شناسه و جزئیات دقیق تر آسیب پذیری
اگر اهل تحقیق در این حوزه باشید میدانید که از این درایور متعدد آسیب پذیری کشف میشه و این اولین بار نیست که در ساختار های خودش آسیب پذیری Use After Free دیده شده، اما به انحصار میخوایم راجعبه شناسه CVE-2019-2215 صحبت کنیم چرا که این شناسه مورد بهرهبرداری جاسوس افزار Pegasus بوده و محل توجه ماست، یک نکته! معمولا در یک زنجیره حمله مهاجم اول میبایست سعی کنه از طریق یک آسیب پذیری مثلا مبتنی بر File Format یا موتور اجرایی در لحظه مرورگرها (Just in Time Compiler) یک دسترسی اولیه ای رو حاصل کرده و در مرحله بعد، آسیب پذیری سطح کرنل رو Trigger یا عملیاتی کنه.
اما نکاتی در حاشیه این آسیب پذیری هست که دانستنشان خالی از لطف نیست، مثلا این آسیب پذیری از نوع روز صفر بوده و هنوز هم بر روی بسیاری از Device های مربوط قابلیت عملیاتی شدن داشته و امکان بهرهبرداری از آن در نسخه هسته لینوکس 4.14 به قبل وجود دارد. مورد بعد این است که این آسیب پذیری بواسطه زنجیره ای از آسیب پذیری های مبتنی بر پروتکل یا مرورگر Trigger شده است و مهاجم میباست دسترسی اولیه ای مانند آسیب پذیری های مبتنی بر مرورگر Chrome پیادهسازی کند، اینجا یک چالش دیگری هم بهوجود خواهد اومد با نام مقابله با Sandbox مرورگر که بواسطه isolated_app
که یک مکانیزم در سطح SELinux است میتواند جلوی ایجاد یک پردازش (Process) مجزا را بگیرد.
نسخه های آسیب پذیر به این شناسه موارد زیر هستند.
- Pixel 2 with Android 9 and Android 10 preview
- Huawei P20
- Xiaomi Redmi 5A
- Xiaomi Redmi Note 5
- Xiaomi A1
- Oppo A3
- Moto Z3
- Oreo LG phones (run same kernel according to website)
- Samsung S7, S8, S9
در گزارش آسیب پذیری عنوان شده که در November 2017 گزارش شده و در February 2018 اصلاح یا Patch براش ارائه شده است، این آسیب پذیری بواسطه Fuzzer شرکت گوگل Syzkaller کشف شده که یک Fuzzer در سطح هسته است و مبتنی بر فازر Address Sanitizer یا ASAN عمل میکند، با این تفاوت که مخصوص سطح هسته سیستم عامل های رایج است، خود ASAN از پتانسیل های Shadow Memory در خصوص اجرای در سایه و بررسی آن بهره میگیرد و با استفاده از این تکنیک میتواند انحراف های عمدی ای که برای اشاره گر ها تولید میشود را رهگیری کرده و در صورت موفقیت آمیز بودن Fuzzing آسیب پذیری شناسایی شود. یک نمونه از گزارش کشف آسیب پذیری سطح هسته اندروید را که بواسطه Syzkaller شناسایی شده است، میتوانید در اینجا مطالعه نمایید.
در تصویر بالا سابقه ی رخداد آسیب پذیری های باینری از درایور های هسته سیستم عامل اندروید رو مشاهده میکنید که مبتنی بر مجازی ساز QEMU عملیاتی شده بعضا تا سال 2019 بهصورت Unpatched مانده بودند و امکان بهرهبرداری از آنها وجود داشته است، اما نکته ای که در تصویر بالا میتونه بسیار مهم هستش، آن سه آسیب پذیری مربوط به ماژول Binder IPC سطح هسته است که بیان شده دو مورد آن Talos Mitigate شده است.
یکی از علت های شایع رخداد این آسیب پذیری این است که اشاره گر های (Pointers) متعددی در جریان اجرای سیستم عامل تعریف شده است که بدلیل بالا بودن تعداد آنها در کتابخانه های Include
شده از هم، میتوان این آسیب پذیری را بعضا در ساختمان های مورد استفاده ی درایور های دیگر، کشف نمود.
همونطور که در تصویر بالا مشاهده میکنید، لاگ عملیات فازر مشخص شده که تونسته آسیب پذیریه UAF رو شناسایی کنه و آدرس Offset های دو اشاره گر مربوط به منطقه حافظه Heap رو برای ما مشخص کرده است. خب تا اینجای کار مشخص شد که آسیب پذیری ای که جاسوس افزار Pegasus در خصوص ارتقاء سطح دسترسی استفاده می کرده است، در کدام قسمت از هسته سیستم عامل قرار داره، اما بیاید بررسی دقیقتری بر روی آسیب پذیری کشف شده داشته باشیم و روند بهرهبرداری آن را بهصورت تئوری پیش ببریم.
برای ردیابی محل آسیب پذیری و نحوه رخداد آن، میبایست به نسخه 4.14 هسته اندروید مراجعه نمود چرا که در این نسخه آسیب پذیری کشف شده است، اولین قدم برای کشف آسیب پذیری، بررسی دقیق Log مربوط به گزارش ربات فازر syzkaller است که در آن گزارش Crash مربوط به آسیب پذیری ثبت شده است.
همانطور که قبلا توضیح داده شد آسیب پذیری از نوع UAF بوده که اینجا در منطقه یک ساختمان رخ داده است، همانطور که میدانید ساختمان ها هم بدلیل Objective بودن در منطقه حافظه Heap تعریف میشوند. آسیب پذیری در درایور drivers/android/binder.c
قرار دارد، در ساختمانی با نام binder_thread
که از نامش پیداست، مسئولیت ایجاد Thread برای پردازش های متقاضی را دارد. در این ساختمان یک Member وجود دارد با نام wait
که از ساختمان wait_queue_head_t
ساخته شده است، wait
یک ارجاع از اشاره گری با نام epoll
است (epoll
یک API در سیستم عامل های لینوکسی است که امکان مانیتورینگ چند وجهی اطلاعات فایل ها بوده است)، که بعد از استفاده از ساختمان binder_thread
آن را آزاد میسازد.
یک کنترلر ورودی و خروجی درخواست ها به حافظه (ioctl
) وجود دارد با نام BINDER_THREAD_EXIT
که در صورت فراخوانی تماسی با تابعی به نام binder_thread_release
برقرار میکند که این تابع ساختمان binder_thread
را آزاد خواهد کرد. اگر Thread یک برنامه با epoll
تماس بگیرد binder_poll
ارتباطی را با epoll
برقرار میکند و ساختمان wait
را فعال میکند، این wait
با wait queue که در ساختمان binder_thread
است ادغام پیدا میکند.
زمانی که ساختمان binder_thread
آزاد میشود، اینجا epoll
اشاره گر خود را به منطقه wait queue آزاد شده قرار می دهد. به طور معمول، wait queue که برای Polling روی یک فایل استفاده می شود، تا زمانی که release handler فایل فراخوانی نشود، تضمین میشود که برقرار باشد. موارد نادری نیاز به استفاده از POLLFREE
دارد. در مقابل، درایور Binder فقط در صورتی کار میکند که دائما ساعت epoll
را حذف کرده و دوباره اضافه کنید. این مشکل اساسی است که موجب ایجاد Bug و آسیب پذیری Use After Free میشود.
در تصویر بالا میبینیم که تابع remove_wait_queue
فعال شده است که یک اشاره گر با نام q
که از wait_queue_head_t
ساخته شده که خود یک کاربر از ساختمان binder_thread
است و در اینجا این اشاره گر به ساختمان binder_thread
زمانی اشاره دارد که قبل آن، این ساختمان آزاد شده است. اینجا Use After Free میتواند Trigger شود و همانطور که در گزارش KASAN دیدیم قبلا، تماسی برقرار شده بود از تابع spin_lock_irqsave
که ورودی آن q->lock&
که اشاره گر به منطقه آزاد شده دارد. با این حال، فراخوانی remove_wait_queue__
برای بهرهبرداری جالب تر است. همانطور که در بالا نشان داده شده است، remove_wait_queue__
به سادگی list_del
را در task_list
در wait queue فراخوانی می کند و به ما یک unlinking primitive می دهد.
در گزارش آمده یک محتوا برای Trigger کردن آسیب پذیری تعریف شده که در کد بالا مشاهده میکنید. در اولین قدم محقق مربوط برای آزمون آسیب پذیری یک تکه کدی را نوشته و در Comment های گزارش خود به گوگل ارسال کرده است.
#include <fcntl.h> #include <sys/epoll.h> #include <sys/ioctl.h> #include <unistd.h> #include <stdio.h> #define BINDER_THREAD_EXIT 0x40046208ul int main() { int fd, epfd; struct epoll_event event = { .events = EPOLLIN }; // 1 fd = open("/dev/binder", O_RDONLY); // 2 epfd = epoll_create(1000); // 3 epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event); // 4 ioctl(fd, BINDER_THREAD_EXIT, NULL); // 5 printf("Finished here."); }
قبل از اینکه کد بالا را تحلیل نمایم میبایست یکبار دیگر به مفهوم اصلی epoll
بپردازیم، epoll
یک I/O از سمت کاربر به سمت هسته سیستم عامل است که اقدام به ساخت، کنترل و مسدود سازی File Descriptors ها نموده است. epoll
میتواند نظارت کند بر فایل های سطح هسته مانند Driver ها تا رویداد های آنها را ثبت و ضبط نماید، اما epoll
API دارای سه مولفه epoll_create
و epoll_ctl
و epoll_wait
است که اولی برای ساخت یک Instance مبتنی بر فایل استفاده میشود، دومی در خصوص کنترل فایل ثبت شده اقدام می نماید و سومین مورد اقدام به مسدود سازی تماس های Thread های مختلف با فایل مورد نظر میشود. ساختار epoll
در هسته دارای دو فیلد لیست است:
- لیست علاقه = فهرست توصیفگرهای فایلی که می خواهیم نظارت کنیم.
- لیست آماده = لیستی از توصیفگرهای فایل که برای I/O آماده هستند.
اما بهتر است پرچم های سه ویژگی epoll
را هم بشناسیم، اولین پرچم EPOLL_CTL_ADD
هستش که دو سوئیچ EPOLLIN
و EPOLLOUT
دارد، که در تماس با epoll_ctl
مقدار دهی میشود، از این دو پرچم در راستای خواندن و نوشتن در فایل استفاده میشود. برای استفاده از binder باید ماژول kernel binder را باز کنیم. در کامنت 2 با فراخوانی تابع open
اقدام به خواندن درایور binder داشته و نهایتا در این خط یک نشانگر توصیفگر فایل خواهیم داشت، با استفاده از این آغازگر هسته fd
و گیرندگان IPC را می توان شناسایی کرد، حال برای اینکه بتوان تحولات رخداده در درایور را خواند میبایست یک epoll_event
ایجاد کرد این موضوع را در کامنت 1 مشاهده میکنید، پرچم مورد استفاده در ساختمان event
پرچم EPOLLIN
است که پیشتر توضیح داده شد و به معنی خواندن میباشد.
در ادامه کامنت 3 را میبینیم که تابع epoll_create
استفاده شده که به منظور باز کردن یک مانیتور رویداد یا event به اندازه 1000 است. در کامنت 4 تابع epoll_ctl
که قبلا معرفی شد، تعریف شده است که کنترل Interface توصیفگر فایل را انجام میدهد، زمانی که از پرچم EPOLL_CTL_ADD
استفاده میشود یعنی در درایور مربوط که با fd
معرفی شده است، یک خواندن رویداد (event&
) را بهصورت Reference یا ارجاع داده شده و با اندازه مشخص انجام شود. اما نهایتا در کامنت 5 از تابع ioctl
استفاده شده است که ماموریت برقراری تماس های سطح سیستم را دارد و بدلیل آنکه از پرچم BINDER_THREAD_EXIT
استفاده شده است، حافظه ساختمان binder_thread
در منطقه Heap آزاد میشود.
زمانی که یک (epfd, EPOLL_CTL_ADD, fd, &event)epoll_ctl
ساخته میشود، یک ep_item
جدید ایجاد میشود که در متغیر fd
مشخص شده است و درایور binder
را مورد گوش دادن قرار میدهد، این ep_item
در ساختاری با نام ep_items
در ep
ذخیره میشود. همچنین اگر تماسی با ep_item_poll
برقرار شود یک ارتباط Call Back با ep_item
برقرار میسازد. اینجا دقیقا جایی است که تخصیص حافظه به ساختمان binder_thread
اتفاق می افتد و آن را به binder_proc
پیوند میدهد، سپس یک ساختار در eppoll_entry
ایجاد شده و دو لیست eppoll_entry->wait
و eppoll_entry->whead
را به یک اشاره گر در ساختمان binder_thread
اشاره میدهد.
تا اینجای کار جمعبندی به این صورت میشود که با فراخوانی epoll_create
ما در اصل اقدام به رزرو حافظه Heap بواسطه اشاره ، دو اشاره گر فوق به ساختمان binder_thread
انجام میشود، همچنین استفاده از BINDER_THREAD_EXIT
را هم استفاده میکردیم برای آزاد سازی ساختمان binder_thread
. اما چگونگی اشاره دوباره به منطقه حافظه Heap آزاد شده در قسمتی که ساختمان binder_thread
و مخصوصا Element آسیب پذیر ما یعنی wait
قرار دارد به چه صورت است؟ همانطور که در تصویر بالا مشخص است بواسطه ep_item->pwqlist
دسترسی به دو Element ساختمان eppoll_entry
برقرار میشود که بهصورت لیست پیوندی (linked list) تعریف شده اند،
- نکته: اصولا تعریف اشاره گر در ساختمان ها به منظور ایجاد لیست های پیوندی (linked list) و سایر ساختمان داده هایی که با تخصیص حافظه پویا سروکار دارند به کار میرود.
اولین اشاره گر متغیر wait
بوده که از ساختمان wait_queue_t
ساخته شده است، و دومین اشاره گر متغیر whead*
بوده که بهصورت اشاره گر (Pointer) از ساختمان wait_queue_head_t
ساخته شده است، هر دوی این اشاره گر ها متعلق به ساختمان binder_thread
و Element آسیب پذیر یعنی wait
بوده است، حال اگر تماسی با epoll_ctl
گرفته شود و پرچم EPOLL_CTL_DEL
تعریف شود، اینجا تماسی با تابع ep_remove
برقرار میشود که اقدام به حذف epitem ساخته شده از قبل را انجام میدهد.
اما اگر با استفاده از eppoll_entry->wait
به آیتم لیست پیوندی قبل اشاره بشود (entry.next.prev = entry.prev
)، ایجا آسیب پذیری Use After Free رخ خواهد داد.
خلاصه
ما یک eventpoll
ایجاد کردیم که دارای red_black_tree
است، هر node یک epitem
که دارای یک فیلد است که لیستی از eppoll_entry
دارد، هر eppoll_entry
دارای دو نشانگر binder_thread
برای ساختمان (wait
، whead
) است. با فراخوانی ioctl
حافظه binder_thread
را از آزاد شده سپس در هنگام خروج از این ساختار از طریق یک اشاره گر که هنوز در دسترس بود، به آن ساختمان دسترسی پیدا شد!
بهرهبرداری از آسیب پذیری UAF در نسخه 4.14.177
ما میدانیم که دو مکان وجود دارد که در آن دو انحراف یا dangling فعال میشود که به ساختمان binder_thread
اشاره خواهد داشت. اولین استفاده زمانی اتفاق می افتد که تابع remove_wait_queue
سعی میکند spin lock
بدست آورد.
struct binder_thread { struct binder_proc *proc; struct rb_node rb_node; struct list_head waiting_thread_node; int pid; int looper; /* only modified by this thread */ bool looper_need_return; /* can be written by other thread */ struct binder_transaction *transaction_stack; struct list_head todo; bool process_todo; struct binder_error return_error; struct binder_error reply_error; wait_queue_head_t wait; struct binder_stats stats; atomic_t tmp_ref; bool is_dead; struct task_struct *task; };
استفاده دوم در تابع داخلی اتفاق می افتد، remove_wait_queue__
جایی که سعی میکند unlink برای poll wait queue را لغو کند. این از نقطه نظر بهرهبرداری بسیار جالب است، زیرا ما یک نمونه اولیه به دست میآوریم که میتوانیم اشاره گر را binder_thread->wait.head
به binder_thread->wait.head.next
و binder_thread->wait.head.prev
روی یک dangling chunk پیاده کند.
یکی از فیلدهای ساختمان binder_thread
اشاره گر task_struct
است، این ساختار فیلدی به نام addr_limit
دارد. وقتی می خواهیم به آدرسی در یک فرآیند دسترسی داشته باشیم، بررسی میشود که آیا آدرس در فضای کاربر است یا نه، اگر در فضای هسته بود این دسترسی باید مسدود شود. این بررسی با مقایسه آدرس ما با addr_limit
انجام می شود، اگر آدرس ما کمتر از addr_limit
باشد، دسترسی معتبر است.
برای درک بیشتر در مورد addr_limit
، بیایید نمونه اولیه read
و write
فراخوانی سیستم را ببینیم.
ssize_t read(int fd, void *buf, size_t count); ssize_t write(int fd, const void *buf, size_t count);
read
، write
و غیره، فراخوانی سیستم می تواند یک اشاره گر به آدرس فضای کاربر به توابع سیستم ارسال کند. این جایی است که addr_limit
به تصویر میرسد. این توابع سیستم از access_ok
تابع برای تأیید اعتبار استفاده می کنند که آیا آدرس ارسال شده واقعاً یک آدرس فضای کاربر است و قابل دسترسی است.
در اینجا دو مرحله برای بهرهبرداری داریم، اول پیدا کردن آدرس task_struct
در فضای هسته، دوم تغییر addr_limit
در task_struct
خواهد بود. ما میتوانیم آدرس task_struct
را بدست آورده و یک بازنویسی بر روی addr_limit
انجام دهیم. مقدار addr_limit
تعیین میکند که هنگام باز تعریف ارجاع نشانگر های فضای کاربر، به کدام محدوده آدرس میتوان دسترسی داشت. عملیات کپی از سمت کاربر فقط به آدرس های addr_limit
امکانپذیر خواد بود، همچنین با افزایش addr_limit
بواسطه بازنویسی حافظه، میتوان به منطقه حافظه هسته سیستم عامل دسترسی پیدا کرد.
استفاده از Vectored I/O
استفاده از Vectored I/O که در کنفرانس CodeBlue 2017 که در خصوص The Art of Exploiting Unconventional UAF Bugs in Android Kernel بوده است. اما این روش ورود و خروج مبتنی بر scatter/gather
یک نوع روش انجام عمل input/output است که در آن تنها یک بار فراخوانی کردن یک بافر خواندن/نوشتن، باعث میشود تا به یکباره چندین بافر به صورت متوالی و پشت سر هم در یک Data Stream نوشته یا از آن خوانده شود. این روش میتواند خواندن های برداری داده ها را از یک منبع داده (در اینجا یک فایل) به مجموعه ای از داده ها منتقل کند، همچنین بافرهای متفاوت (پراکنده)، پس از هر بافر به سراغ بافر بعدی میرود که پر شده است. ویژگی اصلی Vectored I/O این است که میتواند در منطقه حافظه یک Process یا Thread اقدام به نوشتن یا خواندن بافر داده کند.
struct iovec { void __user *iov_base; __kernel_size_t iov_len; };
ویژگی Vectored I/O دارای روش های witev
و readv
و ecvmsg
است، همچنین استفاده از ساختمان iovec
که با استفاده از آن میتوان عملیات Input و Output بهصورت آرایه ای از بافرها خوانده یا نوشته میشود. هر iovec
یک اشاره گر به iov_base
بافر داشته و همچنین یک iov_len
که اندازه بافر را مشخص میکند.
این روش مانند readv
و recmsg
، اولین از همه آرایه های iovec
را در سمت هسته کپی میکند و سپس بافرهای داخل آرایه را میخواند و در fd
مینویسد. پس اولین قدم بهرهبرداری این است که آرایه iovec
در فضای هسته کپی شود.
SYSCALL_DEFINE3(readv, unsigned long, fd, const struct iovec __user *, vec, unsigned long, vlen) SYSCALL_DEFINE3(writev, unsigned long, fd, const struct iovec __user *, vec, unsigned long, vlen)
ما میتوانیم بافرها را با استفاده از pipe
بنویسیم و بخوانیم، pipe
ساختاری است که به ما دو file descriptors میدهد، یکی برای خواندن و دیگری برای نوشتن. pipe
طولی بر حسب بایت دارد، زمانی که یک process در یک pipe
بیشتر از طول خود مینویسد، pipe
در آن process مسدود میشود و منتظر میماند تا process دیگری از آن pipe
خوانده شود.
pipe
یک کانال داده یک طرفه است که می تواند برای ارتباطات بین فرآیندی استفاده شود. ویژگی مسدود کردن pipe
پنجره زمانی قابل توجهی برای خراب کردن iovec
ساختار در فضای هسته به ما می دهد. هسته سعی میکند حافظه (برای یک ساختار) را با توجه به اندازه آن اختصاص دهد. به عنوان مثال وقتی ساختار binder_thread
را از حافظه آزاد کردیم، و پس از آن اگر ساختاری با اندازه ای مشابه binder_thread
داشته باشیم، شانس خوبی برای تخصیص در همان مکان آزاد شده binder_thread
دارد.
ابتدا یک iovecStack
(آرایه ای از ساختارهای iovec
) با اندازه ای مشابه ساختار binder_thread
ایجاد میکنیم. سپس binder_thread
را از حافظه آزاد می کنیم و در ادامه writev
را در این iovecStack
فراخوانی میکنیم. بخش اول این روش، iovecStack
را در فضای هسته کپی می کند. به احتمال زیاد iovecStack
ما در همان مکان آزاد شده binder_thread
تخصیص داده شده است و اگر iovec
کافی در iovecStack
داشته باشیم، حافظه در محل binder_thread
به شکل زیر است:
مشاهده میکنید که iovecStack[10].iov_base
و iovecStack[10].iov_len
و iovecStack[11].iov_base
در همان محل فیلد های wait.lock
، wait.head.next
و wait.head.prev
در binder_thread
خواهند بود. در بخش اول تشریح آسیب پذیری، ما دیدیم که خرابی (Crash) به دلیل دسترسی به wait
که بخشی از binder_thread
در طول فرآیند Unlinking Process که از لیست پیوند ها (linked list) یک مورد را حذف می نمود، رخ داد. در طول فرآیند لغو پیوند، wait.head
قطع میشود و فیلد های بعدی و قبلی آن به فیلد wait
در binder_thread
اشاره میکنند.
هنگامی که I/O بردار ساختار های iovec
ما را در حافظه هسته کپی کرد، سپس میبایست عملیات I/O مسدود شود تا ep_remove_wait_queue
بتواند از یک رشته مجزا اجرا شود. هنگامی که ep_remove_wait_queue
اجرا شود، عملیات list_del
را بر روی مقادیر افست 0xA8
و 0xB0
در نمودار ما انجام میدهد زیرا ep_remove_wait_queue
همچنان برقرار است و جریان اجرای برنامه تصور میکند این مقادیر حافظه بخشی از ساختار wait_queue_head_t
هستند.
entry
در حال ارسال به list_del
است و یک entry
در لیست صف انتظار است. ساختار wait_queue_head_t
آزاد شده حاوی سر لیست است که این ورودی تنها عضو آن است. قبل از عملیات list_del
، لیست مانند نمودار زیر است.
list_del
عملیات شبیه به نمودار زیر است. نشانگر های prev
و next
در list head
به گونه ای تنظیم شده اند که به سر فهرست اشاره کنند. این به این معنی است که iov_base
با یک آدرس هسته بازنویسی شده است و اکنون میتوانیم عملیات خواندن و نوشتن محدوده ای را از فضای هسته که از سر لیست شروع میشود انجام دهیم.
درز آدرس حافظه اشاره گر task_struct
task_struct
شامل بیشتر اطلاعات مهم در مورد یک فرآیند است. در این مورد، ما می خواهیم اشاره گر را به task_struct
برسانیم زیرا شامل محدودیت آدرس فرآیند است.برای ما قسمت next->prev = prev
در تابع del_list__
که در انتهای زنجیره تصویر قبل نشان داده شد، قرار دارد. این اساساً یک unlink است و یک اشاره گر از شی قبلی را در شی UAF ما مینویسد. این مفید است زیرا اگر یک شی هسته دیگر را در بالای شی UAF خود overlap کنیم، میتوانیم این leak kernel را برای خراب کردن داده ها در شیء همپوشانی (overlapped) شده به سلاح تبدیل کنیم.
چند ویژگی در ساختار iovec
وجود دارد که آن را به یک کاندیدای واقعاً خوب برای بهرهبرداری در اینجا تبدیل میکند.
- آنها کوچک هستند (اندازه 0x10 در ماشین های 64 بیتی) و شما میتوانید تمام فیلدها را با محدودیت های بسیار کمی کنترل کنید.
- میتوانید آنها را روی هم قرار دهید و بنابراین کنترل کنید که
iovec
پشته شما در کدام حافظه پنهان kmalloc قرار میگیرد. - آنها یک اشاره گر (
iov_base
) دارند که محل عالی برای خراب کردن با unlink است.
در شرایط عادی، iov_base
در هسته هر جایی که استفاده میشود بررسی میشود که آیا iov_base
قبل از پردازش اطمینان حاصل کند که یک اشاره گر در سمت کاربر است یا خیر، اما این موضوع با استفاده از unlink primitive که در مورد آن صحبت کردیم، میتواند این اعتبار سنجی را خراب کند و آن را با یک اشارهگر هسته بازنویسی کند، که منظور شیء prev
در فرآیند unlink است.
این به این معنی است که وقتی از توصیفگری (file descriptor) که روی آن نوشته شده بود iovec
را میخوانیم،در اصل داده هایی را میخوانیم که یک اشاره گر سمت هسته آن را به prev
مقدار داده است، که این اشاره گر میتواند در امر خواندن و نوشتن دلخواه ما و همچنین اجرای کد مورد استفاده قرار گیرد. مرحله دشوار این فرآیند این است که بفهمیم کدام iovec
در index خود با waitqueue
مطابقت دارد. این مهم است زیرا اگر mutex را به درستی جعل نکنیم، دستگاه hang کرده و ما نمیتوانیم از آن لذت ببریم 🙂
با نگاه کردن به تابعی که از فیلد waitqueue
در ساختمان binder_thread
استفاده میکند، میتوانیم به راحتی آدرس Offset را پیدا کنیم. یکی از این تابع ها binder_wakeup_thread_ilocked
است که با wake_up_interruptible_sync(&thread->wait)
تماس گرفته و زمانی که آدرس درست قبل از تماس در رجیستر X0
بارگیری شود، جریان کنترل اجرای برنامه رو به Offset ارجاع (Reference) داده خواهد داد.
در نسخه هسته 4.4.177، میتوانیم ببینیم که wait queue
در بایت 0xA0
در شی binder_thread
است. از آنجایی که iovec
اندازه 0x10
است، این بدان معناست که iovec
در شاخص 0xA
در آرایه با صف انتظار همخوانی دارد.
بنابراین چگونه میتوان یک آدرس iov_base
معتبر را ارسال کرد که اعتبارسنجی را پاس کند و در عین حال lock
را روی 0
نگه دارد تا از deadlock جلوگیری شود؟ از آنجایی که lock
فقط یک DWORD (4 بایت) است، و یک اشاره گر 64 بیتی را میتوان ارسال کرد، فقط باید از mmap
برای ترسیم یک آدرس کاربر که در آن 32 بیت پایین 0 است استفاده شود.
هنگامی که اکسپلویت اجرا میشود، iovec
در IOVEC_INDX_FOR_WQ
جای mutex
و همچنین نشانگر بعدی در لیست پیوند (linked list) را میگیرد. iovec
در IOVEC_INDX_FOR_WQ + 1
جای نشانگر قبلی را در لیست پیوند داده شده خواهد گرفت. این بدان معناست که فیلد iov_base
در IOVEC_INDX_FOR_WQ + 1
همان قسمتی است که با یک اشاره گر هسته بازنویسی میشود.
بهرهبرداری از درز آدرس
استراتژی بهرهبرداری میبایست به این صورت باشد که بعد از ایجاد یک Pipe و آزاد کردن شیء مربوط به ساختمان binder_thread
، یک ساختمان iovec
تعریف و برای overlap کردن آن روی ساختمان binder_thread
آماده سازیم. این عمل میبایست روی منطقه قدیمی حافظه رزور شده binder_thread
انجام شود و با استفاده از فراخوانی یک تماس سیستمی (Syscall) بر روی تابع writev
، یک Pipe را بازنویسی کنیم.
در ادامه ساختمان iovec
آماده است تا استفاده ی پس از آزادی حافظه (Use After Free) را با استفاده از عملیات unlink انجام داده و با استفاده از تماسی که با تابع read
میگیرد، آن را مبتنی بر Pipe قبلا ایجاد شده فراخوانی کند، که در ساختمان iovec
در آدرس IOVEC_INDX_FOR_WQ + 1
برای خواندن داده های هسته در بافر درز کرده استفاده کند و آدرس base هسته را کشف نماید.
از آنجایی که ما دو iovec
را با iov_len 0x1000
مقداردهی کردیم، در نهایت فراخوانی writv
دو صفحه داده را مینویسد. صفحه اول حاوی داده هایی از dummy_page
است که برای بهرهبرداری مفید نیست. صفحه دوم حاوی داده های هسته خواهد بود! مدیریت خواندن و نوشتن در دو Thread جداگانه آسان تر است.
مرحله اول آزاد سازی binder_thread
، دوم نوشتن پشته iovec
روی Pipe متصل به فرآیند فرزند (Child Process) و سوم اینکه پردازش ساخته شده overlap شود روی منقطه آزاد شده در ساختمان binder_thread
، اما برای پردازش فرزند (Child Process) مسئولیت خراب کردن iovec
با فعال کردن unlink از طریق حذف رویداد EPOLL
است و همچنین خواندن صفحه اول داده های dummy data.
ایجاد یک خواندن/نوشتن دلخواه اولیه
بنابراین ما یک اشاره گر هسته مفید را به بیرون درز کرده ایم، حالا چه؟ بیایید نگاهی به چند عضو اول task_info
بیندازیم، شیئی که آدرس آن فاش میشود و زمینه ی مورد علاقه ما است ، در اینجا addr_limit
است که فضای نوشتن دلخواه سمت هسته را فراهم میسازد. Macro های بسیار مهمی وجود دارند که از نظر امنیت به این زمینه اشاره میکنند. بیایید به یکی از آنها نگاه کنیم (access_ok
).
از نظر range_ok__
اساساً یک معادله محاسبه ای بهصورت (u65)addr + (u65)size <= current->addr_limit
وجود دارد، که این Macro تقریباً در همه جاهایی که هسته سعی میکند به یک اشاره گر ارائه شده توسط کاربر دسترسی پیدا کند استفاده میشود. برای اطمینان از اینکه اشاره گر ارائه شده واقعاً یک اشاره گر سمت کاربر است استفاده میشود یا خیر، و در صورت صحیح نبودن از اشاره به منطقه هتسه اشاره گر جلوگیری میکند. ببین با این کجا میرم؟ 🙂 هنگامی که این addr_limit
شکسته شود، میتوان آزادانه اشاره گر های سطح هسته را به جایی که اشاره گر های سمت کاربر مورد انتظار است منتقل کنیم و بدین صورت معادله access_ok
نقض نخواهد شد.
کنترل یک نویسنده حافظه
ما قبلاً نشان داده ایم که میتوان از unlink برای خواندن و افشای داده های هسته استفاده کنیم، برای افشای داده های هسته، به طور غیرمستقیم در یک توصیفگر فایل (file descriptor) با پشته ای از ساختار های iovec
ما قادر به نوشتن هستیم و یکی از آنها را با unlink خراب کنیم، تا بعداً با یک فراخوانی read
داده های مد نظر خود را نشت دهیم.
با فراخوانی recvmsg
با پشته ای از ساختارهای iovec
و خراب کردن آن به همان روش قبلی، میتوانیم داده هایی را که با استفاده از write
نوشته ایم مجبور کنیم روی ساختار های iovec
متوالی کپی شوند تا یک نوشتن دلخواه بدست آید. بیایید به پشته iovec
نگاهی کنیم که با recvmsg
چگونه میشو در شی UAF خود آن را قرار میدهیم.
تخریب unlink بواسطه IOVEC_INDX_FOR_WQ
ها و مقدار دهی iovec.iov_len
و IOVEC_INDEX_FOR_WQ + 1
که مقدار دهی iovec.iov_base
را با اشاره گر های هسته که مستقیماً به IOVEC_INDX_FOR_WQ
اشاره میکنند، iovec.iov_len
را خراب میکند. این اشاره گر هسته فقط به برخی از داده های تصادفی در جایی اشاره نمیکند بلکه دارد به IOVEC_INDX_FOR_WQ
به iovec.iov_len
اشاره می کند!
هنگامی که recvmsg
به این iovec
رسید، شروع به کپی کردن داده هایی که با write
نوشته ایم در این اشاره گر مربوط میکند، که به ما اجازه میدهد تا داده های دلخواه را در ساختار های iovec
زیر پس از اعتبارسنجی بنویسیم. این به ما امکان میدهد هر اشاره گری را که میخواهیم به iovec.iov_base
بعدی منتقل کنیم که در نهایت منجر به یک نوشتن دلخواه میشود. اما ما آنچه را که در این آدرس نوشته میشود را بهصورت QWORD از write
کنترل خواهیم کرد.
استراتژی حمله به شرح زیر است:
- یک جفت سوکت (socketpair) ایجاد کنید.
- ساختمان
binder_thread
را روی شیء آزاد کنید تا پشتهiovec
را بواسطهrecvmsg
بر رویbinder_thread
همپوشانی (overlap) شود. - بهصورت پیشگیرانه 1 بایت بنویسید تا اولین
iovec
را ساخته شود بدون آنکه تخریبی تولید کند. - ساختارهای
iovec
را از طریقrecvmsg
در حافظه قدیمیbinder_thread
بنویسید. - برای خراب کردن ساختار
iovec
، استفاده پس از رایگان (UAF) / قطع پیوند (Unlink) را فعال کنید. write
را روی socketpair فراخوانی کنید، که ازiovec
برای خراب کردنiovec
بعدی برای خراب کردن حافظه کنترل شده استفاده کند.
اما ماموریت های پردازش والد (Parent Thread)
- نوشتن پیشگیرانه 1 بایت داده برای برآورده کردن اولین درخواست
iovec
درrecvmsg
. - راهاندازی
free
درbinder_thread
. - نوشتن پشته iovec در سوکت و انتظار روی دادههایی که با درخواستهای iovec مطابقت دارند از طریق recvmsg()
ماموریت های خط پردازش فرزند (Child Thread) مسئول موارد زیر است:
- خراب کردن
iovec
با فعال کردن لغو پیوند (Unlink) از طریق حذفevent EPOLL
- وقتی فراخوانی
recvmsg
در پردازش والد (Parent Thread) ادامه یابد، نوشتن داده هایی که ساختارهایiovec
را خراب میکنند.
با کنار هم گذاشتن همه اینها، در نهایت به کد زیر میرسیم تا فرآیند والد addr_limit
را خراب کنیم.
خلاصه
شایان ذکر است که اکسپلویت به طور قابل توجهی پایدار است. خیلی به ندرت از کار می افتد، بنابراین فقط باید اکسپلویت را دوباره اجرا کنید و آماده کنید. به طور کلی، کشف خانوم Maddie Stone در خصوص آسیب پذیری ساختمان binder_thread
موجب بهرهبرداری آن توسط شرکت NSO در سیستم عامل های اندرویدی شد که در نسخه های بعدی هسته هم مثل اینکه بهرهبرداری شده است.
ناشناسی در مقابل دفاع (Defense Evasion)
جاسوس افزار Pegasus برای مخفی سازی وجود خود با استفاده از شبکه های سلولی به جای Wi-Fi سازمانی ارتباط خود را برقرار کند تا از سیستم های نظارت شبکه سازمانی عبور کند. همچنین ممکن است با استفاده از سایر پروتکل های غیبر مبتنی بر اینترنت مانند SMS، NFC یا بلوتوث برای دور زدن سیستم های نظارت شبکه ارتباط برقرار کند.
Pegasus قادر است از طریق مکانیسم ها و پروتکل های مختلف با زیرساخت های برنامه ریزی شده ی مهاجمان ارتباط برقرار کند. این ارتباط از طریق پیام کوتاه، از طریق HTTP، و از طریق پروتکل انتقال MQTT بوده و بخشهای زیر به جزئیات نحوه استفاده نسخه اندروید Pegasus از هر یک از این مکانیسمها و پروتکلها میپردازد.
- اولین قدم دریافت فرمانهایی که در تنظیمات اولیه گنجانده شده است.
- دومین قدم ارسال فرمان ها بواسطه SMS.
- ارسال و دریافت فرمان و داده مبتنی بر HTTP با سرور C&C مهاجم.
اکتشاف (Discovery)
اکتشاف شامل تکنیک هایی است که به مهاجم اجازه میدهد درباره ویژگی های سیستم عامل موبایل همراه و احتمالاً سایر سیستم های در شبکه اطلاعات کسب کنید. هنگامی که مهاجم به یک سیستم جدید دسترسی پیدا می کند، باید برای انجام مراحل بعد حمله خود، عملیات شناسایی داخلی سیستم عامل را انجام دهد. سیستم عامل ممکن است قابلیت هایی را ارائه دهد که به این مرحله جمعآوری اطلاعات پس از نفوذ کمک کند.
برنامه های هدف Pegasus
برنامه های مورد هدف شناسایی Pegasus قالبا برنامه شبکه های اجتماعی و سرویس دهنده های سمت کاربر ایمیل بوده است، لیست برنامه های مورد شناسایی به شرح زیر است:
- Skype
- Viber
- Kakao
- Gmail
- Android’s Native Browser or Chrome
- Android’s Native Email
- Calendar
تجزیه و تحلیل نشان داد که برای دستیابی به این اهداف، Pegasus ابتدا قبل از استفاده از دسترسی سطح هسته برای جستجو در برنامه ها و شناسایی آنها استفاده میکند، که آیا پایگاه های اطلاعاتی برنامه های هدف موجود هستند یا خیر. این شامل پیام های ایمیل، مکالمات چت، لینک های ارسال شده و محتوای های ذخیره شده میشود. Pegasus در مراحل بعدی مجوزهای خواندن، نوشتن و اجرای پایگاه های داده ای را که هدف آن است تغییر میدهد تا برای همه کاربران قابل دسترسی باشد. جالب اینجاست که پس از بازیابی اطلاعات با ارزش، از برخی پایگاه های داده، مجوز ها را به مقادیر اصلی خود دوباره تغییر میدهد.
روش کلی که Pegasus برای زدن Query به پایگاه داده ها استفاده میکند یکسان است. الگو کد های مرتبط با این موضوع را میتواند در تصاویر بالا مشاهده کنید. همچنین مشاهده میکنید که Pegasus برای جمعآوری اطلاعات قربانی تلاش میکند و ابتدا بررسی میکند که آیا Gmail یا برنامه سرویس دهنده بومی استفاده میشود یا خیر، سپس مجوز های فایل های مربوط را در بالاترین سطح تنظیم میکند تا قابل دسترسی باشد، بعد از جمعآوری اطلاعات خود مجوز های دسترسی را به حالت اولیه تنظیم می نماید.
مجموعه (Collection)
مجموعه شامل تکنیک هایی است که برای شناسایی و جمعآوری اطلاعات، مانند فایل های حساس، از یک شبکه هدف قبل از استخراج استفاده میشود. این دسته همچنین مکان هایی را در یک سیستم یا شبکه پوشش میدهد که مهاجم ممکن است به دنبال اطلاعاتی برای نفوذ باشد.
شنود صوتی زنده
مهاجمان ممکن است صدا را برای جمعآوری اطلاعات کاربر یک دستگاه تلفن همراه با استفاده از API های سیستم عامل استاندارد ضبط کنند، مهاجمان ممکن است اطلاعات صوتی مانند مکالمات کاربر، محیط اطراف، تماس های تلفنی یا سایر اطلاعات حساس را هدف قرار دهند.
اندروید و iOS به طور پیشفرض نیازمند این هستند که یک برنامه از کاربر درخواست دسترسی به دستگاه های میکروفون را داشته باشد. در اندروید، برنامه ها باید مجوز android.permission.RECORD_AUDIO
برای دسترسی به میکروفون و مجوز android.permission.CAPTURE_AUDIO_OUTPUT
برای دسترسی به خروجی صدا مانند بلندگوها را داشته باشند. اندروید به برنامه های شخص ثالث اجازه نمیدهد android.permission.CAPTURE_AUDIO_OUTPUT
را نگه دارند، بنابراین خروجی صدا را فقط میتوان با برنامه های دارای امتیاز (توزیع شده توسط Google یا فروشنده دستگاه) یا پس از یک حمله موفقیت آمیز افزایش سطح دسترسی دریافت کرد. در iOS، برنامه ها باید کلید NSMicrophoneUsageDescription
را در فایل Info.plist
خود داشته باشند.
Pegasus از شنود صوتی زنده پشتیبانی می کند. این عملکرد زمانی فعال می شود که تماسی از شماره مشخص شده مهاجم دریافت شود و که به محض رخداد این اتفاق مهاجم صدا را ضبط کرده و همزمان بهصورت زنده به آن گوش میدهد، در کنار این موضوع صدا های اطراف هم شامل این قاعده هستند و میتوان گفت صدای محیط هم رصد میشود. عملکرد شنود صوتی زنده فقط در شرایط خاصی که به موارد زیر نیاز دارد فعال می شود، شرایطی که باید رعایت شود:
- صفحه نمایش قفل شده باشد.
- صفحه نمایش خاموش باشد.
- انتقال تماس فعال نباشد.
- یک کاربر عملیات شنود زنده قبلی را قطع نکرده است.
- هیچ نقطه پایانی برای پذیرش تماس وجود نداشته باشد.
- شماره تماس باید با مقدار رمزگشایی ذخیره شده در تنظیمات مشترک با کلید
Skypi
مطابقت داشته باشد. - وضعیت تلفن در حال کار نباشد.
- میکروفون در حالت استفاده نباشد.
- قانون خاصی برای شارژ باتری اتخاذ نشده باشد.
- هدست سیمی متصل نباشد.
- یک دستگاه جانبی صوتی بلوتوث A2DP متصل نباشد.
- ارتباطات در حال استفاده از بلوتوث نباشد.
- موزیک فعال نباشد.
- اینترنت دستگاه بر حالت رومینگ نبوده یا Pegasus برای این مورد پیکربندی های لازمه را انجام داده باشد تا حتی اگر دستگاهی در حال رومینگ بود شنود زنده اتفاق افتد.
قابلیت Screenshot از دوربین
مهاجمان ممکن است از دوربین برای گرفتن اطلاعات در مورد کاربر، محیط اطراف یا سایر شناسه های فیزیکی استفاده کنند. مهاجمان ممکن است از دستگاه های دوربین فیزیکی موجود در دستگاه تلفن همراه برای ضبط تصاویر یا ویدیو استفاده کنند. به طور پیشفرض، در اندروید و iOS، یک برنامه باید مجوز دسترسی به یک دستگاه دوربین را درخواست کند که توسط کاربر از طریق یک درخواست اعطا میشود. در اندروید، برنامهها برای دسترسی به دوربین باید مجوز android.permission.CAMERA
را داشته باشند. در iOS، برنامه ها باید کلید NSCameraUsageDescription
را در فایل Info.plist
قرار دهند و در زمان اجرا باید درخواست دسترسی به دوربین را داشته باشند.
همانطور که در تصویر مشخص است توابعی برای ثبت Screenshot از دوربین عقب و همچنین جلو تعریف شده اند، از این روی مهاجم میتوانسته با استفاده از بدافزار خود با دقت بالایی عکس برداری کرده و وضعیت رخداد عکس برداری را متوجه شود، بهطوری که resolution و نوعه خروجی فایل هم میتوانسته مشخص شود، خط فرمان ارسالی به بدافزار در اولین قدم بررسی سطح دسترسی خود را به مجوز های مورد نیاز را انجام میدهد، ولیکن در صورت Root بودن سیستم عامل اجازه رخداد عملیات تابع مربوط را خواهد داد، این مورد راجعبه دریافت Screenshot هم صدق میکند و عملیات تصویر بالا نیازمند بررسی سطح دسترسی و اصطلاحا Root میباشد، که البته چون از قبل عملیات Privilege Escalation انجام شده است این مورد پاس خواهد شد.
قابلیت KeyLogger در بدافزار
مهاجمان ممکن است ورودی کاربر را برای به دست آوردن اعتبار یا سایر اطلاعات از کاربر از طریق روشهای مختلف دریافت کنند. بدافزار ممکن است به عنوان یک صفحه کلید شخص ثالث قانونی برای ضبط ضربه های کلید کاربر ظاهر شود. در اندروید و iOS، کاربران باید صریحاً اجازه استفاده از برنامه های صفحه کلید شخص ثالث را بدهند. باید به کاربران توصیه شود که قبل از اعطای این مجوز در صورت درخواست، بسیار احتیاط کنند.
در اندروید، بدافزار ممکن است با ثبت یک کلاس AccessibilityService
، نادیده گرفتن روش onAccessibilityEvent
و گوش دادن به نوع رویداد AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
، از ویژگیهای دسترسی برای ضبط ضربههای کلید سوء استفاده کند. شی رویداد ارسال شده به تابع حاوی داده هایی است که کاربر تایپ کرده است. در Pegasus دسترسی Root در دسترس است و همین موضوع میتواند روش های اضافی برای keylogging
را امکانپذیر میسازد.
Pegasus قادر است خود را به فرآیند صفحه کلید خود دستگاه تزریق کند و بعد از انجام این کار محتوایی را که قربانی وارد میکند ثبت کند. این قابلیت توسط libk
ارائه شده است که یک باینری در دایرکتوری res/raw/
است. در حین اجرا، Pegasus فایل libk
که یک فایل با فایل فرمت ELF است را در data/local/tmp/libuml.so/
مینویسد. داده های ثبت شده با کلید در ابتدا در data/local/tmp/ktmu/ulmndd.tmp/
نوشته میشوند.
فرمان و کنترل (Command and Control)
تاکتیک فرماندهی و کنترل نشان می دهد که چگونه مهاجمان با سیستم های تحت کنترل خود در یک شبکه هدف ارتباط برقرار کند. راه های زیادی وجود دارد که مهاجم بتواند با سطوح مختلف پنهانی، فرماندهی و کنترل را بر اساس پیکربندی سیستم و توپولوژی شبکه برقرار کند. با توجه به وسعت از تنوع موجود در سطح شبکه برای مهاجمان، تنها از رایج ترین عوامل برای توصیف تفاوت در فرماندهی و کنترل استفاده شد. هنوز تعداد زیادی تکنیک خاص در روش های مستند وجود دارد که عمدتاً به دلیل آسان بودن تعریف پروتکل های جدید و استفاده از پروتکل های موجود و قانونی و خدمات شبکه برای ارتباط است.
علاوه بر این، در محیط تلفن همراه، دستگاه های تلفن همراه اغلب به شبکه های خارج از کنترل سازمانی مانند شبکه های سلولی یا شبکه های Wi-Fi عمومی متصل می شوند. مهاجمان میتوانند با برقراری ارتباط در این شبکه ها و احتمالاً حتی با استفاده از مکانیسم های غیر پروتکل اینترنتی مانند سرویس پیام کوتاه (SMS) از شناسایی فرار کنند. با این حال، شبکههای سلولی اغلب دارای سرپوش داده ها و یا هزینه های داده اضافی هستند که میتواند پتانسیل شناسایی ارتباطات متخاصم را افزایش دهد.
تشریح برخی فرمان های تعریف شده در Pegasus در نسخه اندروید را تشریح میکنیم. اولین دستور dump
است که شامل دریافت اطلاعات زیر خواهد بود:
- SMS messages
- Phone call logs
- Contacts stored on the SIM card and device
- WhatsApp messages
- Facebook messages
- Twitter messages
- Browser history
- List of installed and running applications on the device
- Kako messages
- Viber mesages
- Skype chat logs
- Calendar entries
- Email stored on the device
دستور بعدی upgrade
است که از برنامه درخواست می کند تا یک ارتقاء را در قالب Package
دانلود و نصب کند از یک URL مشخص، دستور بعدی camCmd
از برنامه میخواهد با دوربین جلو یا عقب دستگاه یک Screenshot یا عکس بگیرد. همچنین دستور emailAttCmd
هم دستور دیگری است که میتواند باعث شود برنامه یک پیوست ایمیل خاص را بازیابی کند. دستورها اضافی برای تنظیم بیشتر گزینه های قابل تنظیم در برنامه موجود است. هر دستور شامل یک شناسه تایید است که مشتری برای تایید دریافت فرمان به سرور C2 ارسال می کند.
دریافت پیامک (Inbound SMS)
یکی از این روش های مورد استفاده Pegasus برای شناسایی نشدن این است که دستورها موجود خود را در متن پیام SMS که بنظر پیامی از طرف سرویس دهنده های گوگل است ارسال میکند، این استفاده از عنوان گوگل برای پنهان نگه داشتن مخرب بودن محتوای اصلی این پیامک بوده و در قالب پیامک احراز هویت گوگل طراحی شده است.
برنامه یک Content Observer داشته است که پوشش می دهد محتوای پیامک (SMS) ارسالی از خط فرمان را در پوشش “your google verification code” بررسی و پردازش میکند. دستورها پیامکی می توانند شامل شماره فرمان، شناسه تایید، آرگومان های فرمان و a
باشند امضا ساختار اصلی فرمان عبارت است از:
text:[Six Digits][Command Number]a=[Ack ID]&[Command Arguments]&s=[Message Signature]
در ساختار بالا میبینیم که امضای پیام یک هش MD5 بهصورت توکن است که در راهاندازی اولیه برنامه و پیام فرمان محتویات پیکربندی شده است. امضای موجود در پیام با امضای تولید شده در دستگاه بررسی می شود. این بررسی تضمین می کند که برنامه فقط به دستوراتی که از فرستنده ای که توکن دستگاه را می شناسد صادر می شود، پاسخ می دهد.
اعداد فرمان اندروید از 0 تا 8 متغیر است.
- فرمان
0
این دستورkill
باعث می شود که برنامه سعی کند خود را از دستگاه حذف کند. - فرمان
1
این دستور باعث می شود یک پیام خروجی از طریق HTTP یا SMS ارسال شود. - فرمان
2
این دستور گزینه های پیکربندی مکان و نرخ آگهی را تنظیم میکند و عملکرد نظارت بر مکان را تغییر میدهد. - فرمان
3
این دستور گزینه های پیکربندیWindowTargetSms
وSkypi
وNetworkWindowAddresess
را پیکربندی می کند (معمولاً برای پیکربندی اولیه استفاده می شود). - فرمان
4
این دستور با دوربین پشتی دستگاه عکس می گیرد. - فرمان
5
این دستور درخواست آپلود یک فایل دلخواه از سیستم فایل یا آپلود در فهرست دایرکتوری است. - فرمان
6
این دستور دستگاه را برای دریافت تماس نظارت صوتی همانطور که در بخش (Live Audio Surveillance) توضیح داده شده است، آماده می کند. - فرمان
7
این دستور ویژگی های ضبط تماس را با تنظیم گزینه (window canda configuration) تغییر می دهد. - فرمان
8
این دستور یک درخواست فوری به یکی از سرورهای پیکربندی شده C2 برای دریافت دستورها جدید از طریق HTTP ارسال میکند.
آرگومان های فرمان برای هر دستور متفاوت است، اما به گونه ای ساختار یافته اند که شبیه رشته پرس و جو هستند پارامترها در URL جعلی. برای همه دستورها به جز 0
، برنامه تصدیق می کند که یک فرمان توسط آن دریافت شده و برای ارسال درخواست به سرور HTTP C2 پیکربندی شده یا خیر، همچنین شناسه تایید دستور ارسال شده با SMS در پارامتر های فیلد در Header درخواست HTTP گنجانده می شود.
ارسال پیامک (Outbound SMS)
بدافزار پیام های SMS خروجی را به شماره WindowTargetSms
پیکربندی شده ارسال می کند. این رفتار می تواند در صورت تقاضا، از طریق دستوری که برای برنامه صادر می شود، یا تحت شرایط های خاص تغییر کند مانند عدم دسترسی به اینترنت یا تغییر سیم کارت دستگاه.
این پیام ها شامل زیر مجموعهای از اطلاعات موجود در فیلدهای Header است که در ارتباطات HTTP ارسال میشوند، اگر چه دادهها به صورت key/value جدا شده و از دو نقطه یا خط جدید قالب بندی شدهاند.
WAP Push Messages
بدفزار برای ارسال پیام های WAP ، تنظیمات WAP را فعال کرده و Push میکند، پیامهای فشار WAP پیامهای SMS حاوی داده های رمزگذاری شده ویژه هستند که میتوانند تحت شرایط خاصی باعث شوند دستگاه به طور خودکار لینکی را در مرورگر دستگاه باز کند. در صورتی که دستگاه و سیستم عامل از آن پشتیبانی کنند، می توان از آن برای بارگیری خودکار محتوا بدون دخالت کاربر استفاده کرد.
این تکنیک ممکن است برای استقرار در اندروید نیز استفاده شود زیرا برنامه تلاش میکند تا پیامهای WAP را هنگام راهاندازی در صورت روت بودن دستگاه حذف کند:
Message Queue Telemetry Transport (MQTT)
Pegasus برای اندروید علاوه بر امکان برقراری ارتباط از طریق HTTP و SMS نیز قادر به برقراری ارتباط با زیرساخت فرماندهی و کنترل از طریق پروتکل MQTT است. با این حال، چندین محدودیت مشخص شده توسط مهاجم در خصوص زمان وجود دارد که می تواند در صورت استفاده از MQTT اعمال شود. با در نظر گرفتن این موضوع، Pegasus بررسی میکند که آیا دستگاه روی Wi-Fi، شبکه تلفن همراه، یا رومینگ است یا خیر، و اینکه آیا برای استفاده از MQTT در این سناریوها پیکربندی ای انجام شده است یا خیر. اگر این پیکربندی اولیه انجام نشده باشد Pegasus از پروتکل MQTT استفاده نخواهد کرد.
با فرض اینکه مجاز به استفاده از MQTT در شبکه فعلی است، Pegasus تلاش خواهد کرد تا یک اتصال با فرمت زیر با مهاجم ایجاد کند <tcp://<MQTT_SERVER>:<MQTT_PORT
و متغیرهای پورت بر اساس اطلاعات پیکربندی بازیابی شده از آن پر می شوند، وجود مواردی مانند نام کاربری MQTT، رمز عبور و رمز شناسایی. توکن ارائه شده الزامی است و در صورت نبود ارتباط نا موفق خواهد بود.
Phone Calls
در برنامه شماره هایی برای تنظیم پیکربندی های رومینگ با شماره های *762646466
و *7626464633
قرار داده شده است. این شماره تلفن ها به ترتیب romingSetted
را روشن و خاموش می کنند. این ویژگی احتمالاً به این دلیل اجرا می شود که وقتی دستگاهی در حالت رومینگ است و romingSetted
غیرفعال است، برنامه دستورها HTTP معمولی را از کانال های ارتباطی SMS یا MQTT نمیپذیرد.
فیلترسازی (Exfiltration)
فرایند فیلترسازی همواره در خصوص ارتباطات با خط فرمان مهاجم صورت میگیرد تا علاوه بر شناسایی نشدن ارتباط بلکه مبهم سازی هم انجام شود، این مبهم سازی به این دلیل است که در صورت کشف ارتباط محتوای داده های مورد ارسال و دریافت قابیت خواندن نداشته باشد.
ساختار ارسال داده (Request)
در ساختار Header درخواست های (Requests) ارسال شده دو مقدار SessionId1
و SessionId2
قرار داره که اولین پارامتر یعنی SessionId1
دارای توکن ذخیره شده روی کاربر بوده و مقدار توکن توسط سرور برای تولید پاسخ های رمزگذاری شده و به احتمال زیاد برای شناسایی دستگاه کاربر استفاده می شود. اما پارامتر دوم یعنی SessionId2
که دارای یک آرایه مبتنی بر byte تولید میشه که بهصورت اتفاقی هم خواهد بود، اما در ادامه مقادیر پارامتر دوم، یک کلید AES است که برای رمزگذاری فایل های آپلود شده در دستگاه قربانی است و در پاسخ استفاده می شود. این مقدار دارای رمزگذاری AES و کدگذاری Base64 است.
طرح و مسیر مورد استفاده در ساخت URL برای C&C در برنامه رو میبینیم که کدگذاری شده است. هر قسمت از Body درخواست نوع محتوای multipart/form-data
است که کدگذاری شده است، همچنین با استفاده از gzip
فشرده شده و سپس AES
با استفاده از کلید SessionId2
رمزگذاری می شوند.
قالب فایل جمعآوری داده بسته به دادههای در حال انتقال متفاوت خواهد بود، اما فایلها با نوع محتوای XML هستند که حاوی داده های استخراج شده از دستگاه قربانی است و در پاسخ به فرمان صادر شده برای برنامه بدافزار بوده است.
ساختار پاسخ های داده شده از بدافزار
پاسخ ها (Response) نیز اسناد XML رمزگذاری شده AES هستند. کلید رمزگذاری با الگوریتم زیر تولید می شود:
MD5({0xB6, 0x27, 0xDB, 0x21, 0x5C, 0x7D, 0x35, 0xE4, token})
تجزیه پاسخ XML در داخل برنامه با نادیده گرفتن متدهای ()startElement
و ()endElement
و ()characters
و همچنین کلاس org.xml.sax.helpers.DefaultHandler
از کتابخانه SAX2 و ردیابی start/end
عناصر مختلف. این روش تجزیه برنامه کاربر این امکان را می دهد که بیش از یک ساختار پاسخ (Response) ممکن را بپذیرد، بنابراین نمی توان با اطمینان تعیین کرد که بخش سمت سرور برنامه چگونه همه دستورها خود را ساختار می دهد، فقط می توان تعیین کرد که برنامه به کدام دستورها پاسخ می دهد.