۰۶۲۴۹ ۳۶۸ ۰۲۶
[email protected]
Unk9vvN
  • راهکارها
    • شبیه سازی تهاجمی
    • عملیات تدافعی
    • شکارچی باگ
  • خدمات
    • تست نفوذ و ارزیابی امنیتی
    • تیم قرمز و مهندسی اجتماعی
    • امنیت سیستم های کنترل صنعتی
    • جرم شناسی دیجیتال و پاسخ به حادثه
    • تیم آبی و دفاع سایبری
    • بازبینی امنیتی و کشف آسیب پذیری
  • دوره ها
    • تست نفوذ
      • وب
      • موبایل
      • فضای ابری
      • شبکه
      • شبکه بی سیم
      • اینترنت اشیاء
    • تیم قرمز
    • امنیت صنعتی
    • جرم شناسی دیجیتال
    • تیم آبی
    • بازبینی امنیتی
  • منابع
    • وبلاگ ما
    • وبینار ها
    • تایید گواهی
  • درباره ما
  • تماس با ما
  • حساب من
  • فارسی
محصول به سبد خرید شما افزوده شد.

تحلیل تکنیکال جاسوس افزار پگاسوس (قسمت اول)

نوشته شده در 14 مرداد 1400
بدون دیدگاه

در این مقاله قصد داریم که تحلیل تکنیکال جاسوس افزار پگاسوس (Pegasus) که منتصب به شرکت NSO Group است را با هم داشته باشیم و ببینیم این جاسوس افزار نحوه عملکردش به چه صورت بوده و آسیب پذیری های باینری مورد استفاده آن چه مواردی بوده اند، همچنین تشریح فنی رفتار تاکتیکی و تکنیکی (Tactics, Techniques and Procedures) جاسوس افزار هم قدم به قدم مورد بررسی قرار خواهیم داد.

فهرست محتوا پنهان
1 معرفی شرکت NSO
2 رفتار تاکتیکی و تکنیکی (Android)
2.1 ایجاد دسترسی (Initial Access)
2.1.1 عملکرد مکانیزم PHAs
2.1.2 بدافزار Chrysaor در اندروید
2.1.3 استفاده از آسیب پذیری WhatsApp
2.1.3.1 بررسی وصله WhatsApp
2.2 اجرا (Execution)
2.3 ماندگاری (Persistence)
2.4 ارتقاء سطح دسترسی (Privilege Escalation)
2.4.1 ارتقاء سطح دسترسی در Chrysoar
2.4.1.1 استفاده از Framaroot
2.4.2 بهره‌برداری از آسیب پذیری روز صفر سطح هسته
2.4.2.1 اما ماهیت کارکرد درایور Binder IPC چیست؟
2.4.2.1.1 ارتباطات بین فرآیندی
2.5 شناسه و جزئیات دقیق تر آسیب پذیری
2.5.1 نسخه های آسیب پذیر به این شناسه موارد زیر هستند.
2.5.1.1 خلاصه
2.5.2 بهره‌برداری از آسیب پذیری UAF در نسخه 4.14.177
2.5.2.1 استفاده از Vectored I/O
2.5.2.2 درز آدرس حافظه اشاره گر task_struct
2.5.2.3 بهره‌برداری از درز آدرس
2.5.2.4 ایجاد یک خواندن/نوشتن دلخواه اولیه
2.5.2.5 کنترل یک نویسنده حافظه
2.5.3 خلاصه
2.6 ناشناسی در مقابل دفاع (Defense Evasion)
2.7 اکتشاف (Discovery)
2.7.1 برنامه های هدف Pegasus
2.8 مجموعه (Collection)
2.8.1 شنود صوتی زنده
2.8.2 قابلیت Screenshot از دوربین
2.8.3 قابلیت KeyLogger در بدافزار
2.9 فرمان و کنترل (Command and Control)
2.9.1 دریافت پیامک (Inbound SMS)
2.9.1.1 اعداد فرمان اندروید از 0 تا 8 متغیر است.
2.9.2 ارسال پیامک (Outbound SMS)
2.9.2.1 WAP Push Messages
2.9.2.2 Message Queue Telemetry Transport (MQTT)
2.9.2.3 Phone Calls
2.10 فیلترسازی (Exfiltration)
2.10.1 ساختار ارسال داده (Request)
2.10.2 ساختار پاسخ های داده شده از بدافزار
3 منابع

معرفی شرکت NSO

شرکت NSO یک شرکت اسرائیلی بوده است که بنیانگذاران آن Omri Lavie و Shalev Hulio هستند، شرکت NSO واقع در Herzliya در مناطق شمالی تل آویو بوده که ماموریت آن نفوذ به اهدافی خاص است، از این روی یکی از تهاجمی ترین جاسوس افزار های تلفن همراه را تا به امروز طراحی کرده است، و برای اولین بار بدست آزمایشگاه Lookout کشف و گزارش شده است. یکی از نقاط قوت این جاسوس افزار استفاده ی از آسیب پذیری های (روز صفر) سطح هسته سیستم عامل های iOS و Android بود، به همین دلیل به محض ایجاد دسترسی اولیه از سیستم عامل قربانی سعی بر ارتقاء سطح دسترسی (Privilege Escalation) میکند تا پردازش مختص به خودش را در سطح هسته سیستم عامل اجرا کند.

CO-Founder
CO-Founder
بنیانگذار شرکت NSO

اما هدف این جاسوس افزار چه کسانی بود؟ روزنامه نگاران، فعالین حقوق بشر و برخی از عوامل دولتی کشور پاکستان بوده است. بنیانگذاران NSO متنصب به واحد 8200 دستگاه اطلاعاتی کشور جعلی اسرائیل هستند که در آن بخش مامور شنود و جاسوسی در حوزه شبکه های سیگنالی و مخابراطی است. این جاسوس افزار بعد از هدف قرار دادن یک فعال حقوق بشر به نام احمد منصور در امارات متحده عربی بسیار رسانه ای شد و همین موضوع موجب کشف آن در آزمایشگاه Lookout شد، که با بررسی هایی که این آزمایشگاه انجام داد متوجه این جاسوس افزار شده و گزارش‌ها آن را منتشر کرده است.

شماتیک رویه ایجاد دسترسی اولیه
شماتیک رویه ایجاد دسترسی اولیه
رویه پیاده سازی SMS Phishing و مجبور کردن قربانی برای کلیک بر روی لینک اکسپلویت

در ژوئیه ۲۰۲۱ واشینگتن پست، گاردین، لو موند، و چهارده رسانه دیگر در گزارشی آشکار کردند که پگاسوس با آلوده‌ سازی تلفن‌ های اندروید و آیفون شماره تلفن ۵۰ هزار نفر را رهگیری میکرده‌ است. بیش از ۶۰۰ سیاستمدار (سه رئیس‌جمهور، ده نخست‌وزیر، و پادشاه مراکش)، ۱۸۹ روزنامه‌نگار، ۶۴ مدیر شرکت، ۸۵ فعال حقوق بشر، و اعضای خانواده کشورهای عربی از اهداف پگاسوس بوده‌ اند. روزنامه‌ نگارانی که هدف این بدافزار بودند در خبرگزاری فرانسه، سی‌ان‌ان، نیویورک تایمز، و الجزیره کار میکردند.

رفتار تاکتیکی و تکنیکی (Android)

TTPs Pegasus Android
TTPs Pegasus Android
رفتار تاکتیک و تکنیکی جاسوس افزار Pegasus در خصوص 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

محل درج تصدیق ورودی
محل درج تصدیق ورودی
محل درج تصدیق ورودی به‌صورت Disassemble شده

در تصویر بالا اشاره شده که یک تابع برای کنترل ورودی RTCP Handler پروتکل به چه صورت است، تابع مربوط بررسی میکند که آرگومان ورودی آیا طولی برابر با اندازه حداکثر 1480 بایت (0x5C8) بوده است یا خیر. جالبه که ماژول دارای آسیب پذیری که مامور ایجاد نشست ارتباط تماس صوتی واتس اپ مبتنی بر پروتکل RTCP است، قبل از برقراری ارتباط فراخوانی میشود، یعنی آسیب پذیری بدون برقراری ارتباط کامل صوتی میتواند اصطلاحا Trigger شود.

دو شرط کنترلی
دو شرط کنترلی
دو شرط کنترلی در خصوص آسیب پذیری Stack Overflow مبتنی بر پروتکل

در تصویر بالا مشاهده میشه که یک شرط اول وجود داره که طول فیلد پکت پروتکل اگر کمتر از طول ورودی باشه وارد بلاک شرط میشه، این اولین مرحله چک هستش اما در ادامه یکبار دیگر قبل از کپی مقادیر بواسطه تابع 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 رو انجام دهند.

تابع بررسی تاریخچه مرورگر
تابع بررسی تاریخچه مرورگر
تابع تخصیص داده شده برای بررسی Bookmark و History مرورگر

بعد از نصب نرم‌افزار، تا زمانی که یک بار دستگاه بازیابی مجدد یا Reboot نشود برنامه غیر فعال خواهد ماند و بعد از Boot شدن و باز شدن توسط کاربر یک android.intent.action.BOOT_COMPLETED که به‌صورت Broadcast ارسال میشود فعال شده و راه‌اندازی میگردد، برنامه در اولین مرحله راه‌اندازی سعی بر انجام یکسری پیکربندی داشته که آنها با استفاده از ارسال Query String به تاریخچه مرورگر و مقادیر بر URL انجام میدهد، همچنین سعی بر خواندن فایل های محلی کرده است. در ادامه کد میبینیم که شرطی وجود داره که میگه اگر مقدار Query String زده شده null نبوده و مساوی با رشته rU8IPXbn بود بیا و سرشماری url ها و ip address هارا پیدا کن. اما این رشته rU8IPXbn دارای معنی های مختلفی در کاراکتر های خود است.

  1. کاراکتر t معادل توکن مورد استفاده برای تولید امضا برای دستورها و شناسایی آنها در دستگاه کاربر است.
  2. کاراکتر c معادل یک فرمان و امضای رمزگذاری شده به‌صورت Base64 است و با همان فرمت دستورها ارسال شده از طریق پیامک دریافت خواهد شد.
  3. کاراکتر d که معادل گزینه پیکربندی userNetwork است که شامل کد کشور تلفن همراه ITU-T E.212 (MCC) است.
  4. کاراکتر b که معادل گزینه نصب پیکربندی ها به‌صورت Boolean خواهد بود.
  5. کاراکتر 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

دانلود و بارگذاری فایل libsgn.so
دانلود و بارگذاری فایل libsgn.so
این فایل یک فایلshared object است که ماموریت ارتقا سطح دسترسی را خواهد داشت

تجزیه و تحلیل این نمونه نشان داد که تنها هدف آن شروع اتصال به یک آدرس راه دور باشد، و در فایل 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
فرایند عملیات 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 شده از هم، میتوان این آسیب پذیری را بعضا در ساختمان های مورد استفاده ی درایور های دیگر، کشف نمود.

تاریخچه عملیات فازر syzkaller
تاریخچه عملیات فازر syzkaller
در این تصویر مشخص میشود آسیب پذیری UAF را فاز syzkaller پیدا کرده است

همونطور که در تصویر بالا مشاهده میکنید، لاگ عملیات فازر مشخص شده که تونسته آسیب پذیریه 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 آن را آزاد میسازد.

ساختمان binder_thread
ساختمان binder_thread
ساختمان binder_thread و ساختمان wait_queue_head که کاربر wait از آن ساخته شده است

یک کنترلر ورودی و خروجی درخواست ها به حافظه (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 با __remove_wait_queue
ارتباط تابع remove_wait_queue با __remove_wait_queue
در این تصویر تابع مورد استفاده تابع مادر به نمایش گذاشته شده است

در تصویر بالا میبینیم که تابع 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 آزاد میشود.

زنجیره بکارگیری دو اشاره گر
زنجیره به‌کارگیری دو اشاره گر
در این تصویر چرخه اتصال تابع epoll_create به ساختمان thread_binder مشخص میشود

زمانی که یک (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 تابع برای تأیید اعتبار استفاده می کنند که آیا آدرس ارسال شده واقعاً یک آدرس فضای کاربر است و قابل دسترسی است.

تابع access_ok
تابع 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 که اندازه بافر را مشخص میکند.

طریقه کار کرد Vectored I/O
طریقه کار کرد Vectored I/O
طریقه کار کرد Vectored I/O از توابع سمت کاربر به سمت هسته

این روش مانند 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 به شکل زیر است:

ساختمان iovec
ساختمان iovec
ساختمان iovec که میبایست در منطقه آزاد شده ساختمان 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 هستند.

زنجیره تماس های _remove_wait_queue
زنجیره تماس های _remove_wait_queue
در این تصویر چرخه تماس های _remove_wait_queue مشخص شده اند که در زمان unlinking از آن برای اشاره به منطقه حافظه آزاد شده استفاده شود

entry در حال ارسال به list_del است و یک entry در لیست صف انتظار است. ساختار wait_queue_head_t آزاد شده حاوی سر لیست است که این ورودی تنها عضو آن است. قبل از عملیات list_del، لیست مانند نمودار زیر است.

فرایند unlinking
فرایند unlinking
فرایند unlinking از linked list مربوط به list head

بعد از list_del عملیات شبیه به نمودار زیر است. نشانگر های prev و next در list head به گونه ای تنظیم شده اند که به سر فهرست اشاره کنند. این به این معنی است که iov_base با یک آدرس هسته بازنویسی شده است و اکنون میتوانیم عملیات خواندن و نوشتن محدوده‌ ای را از فضای هسته که از سر لیست شروع می‌شود انجام دهیم.

فرایند بازگشت به list head
فرایند بازگشت به list head
در بازنویسی منطقه حافظه binder_thread علاوه بر تعریف ساختمان ساختگی، بازگشت به ساختمان list head را انجام میدهد

درز آدرس حافظه اشاره گر task_struct

ما فرآیندی را که در بالا ذکر شد دنبال می کنیم تا از استفاده پس از آزاد شدن برای افشای نشانگر ساختار وظیفه استفاده کنیم. در هسته لینوکس و در نتیجه در هسته اندروید، task_struct شامل بیشتر اطلاعات مهم در مورد یک فرآیند است. در این مورد، ما می خواهیم اشاره گر را به task_struct برسانیم زیرا شامل محدودیت آدرس فرآیند است.

برای ما قسمت next->prev = prevدر تابع del_list__ که در انتهای زنجیره تصویر قبل نشان داده شد، قرار دارد. این اساساً یک unlink است و یک اشاره گر از شی قبلی را در شی UAF ما مینویسد. این مفید است زیرا اگر یک شی هسته دیگر را در بالای شی UAF خود overlap کنیم، میتوانیم این leak kernel را برای خراب کردن داده‌ ها در شیء همپوشانی (overlapped) شده به سلاح تبدیل کنیم.

چند ویژگی در ساختار iovec وجود دارد که آن را به یک کاندیدای واقعاً خوب برای بهره‌برداری در اینجا تبدیل میکند.

  1. آنها کوچک هستند (اندازه 0x10 در ماشین های 64 بیتی) و شما میتوانید تمام فیلدها را با محدودیت های بسیار کمی کنترل کنید.
  2. میتوانید آن‌ها را روی هم قرار دهید و بنابراین کنترل کنید که iovecپشته شما در کدام حافظه پنهان kmalloc قرار میگیرد.
  3. آنها یک اشاره گر ( 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) داده خواهد داد.

 

مقدایر تعریف شده برای کاور task_struct
مقدایر تعریف شده برای کاور task_struct
مقادیری که توضیح داده شد در خصوص موقعیت مقادیر آدرس offset در حافظه نسخه 4.4.177

در نسخه هسته 4.4.177، میتوانیم ببینیم که wait queue در بایت 0xA0 در شی binder_thread است. از آنجایی که iovec اندازه 0x10 است، این بدان معناست که iovec در شاخص 0xA در آرایه با صف انتظار همخوانی دارد.

بنابراین چگونه میتوان یک آدرس iov_base معتبر را ارسال کرد که اعتبارسنجی را پاس کند و در عین حال lock را روی 0 نگه دارد تا از deadlock جلوگیری شود؟ از آنجایی که lock فقط یک DWORD (4 بایت) است، و یک اشاره گر 64 بیتی را میتوان ارسال کرد، فقط باید از mmap برای ترسیم یک آدرس کاربر که در آن 32 بیت پایین 0 است استفاده شود.

فزایند بازنویسی اشاره گر هسته
فزایند بازنویسی اشاره گر هسته
در این قسمت اکسپلویت اشاره گر آسیب پذیر در ساختمان binder_thread بازنویسی میشود

 

هنگامی که اکسپلویت اجرا میشود، 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.

ایجاد یک خواندن/نوشتن دلخواه اولیه

ساختمان thread_info
ساختمان thread_info
فرایند محدود سازی در ساختمان thread_info و task_struct

بنابراین ما یک اشاره گر هسته مفید را به بیرون درز کرده ایم، حالا چه؟ بیایید نگاهی به چند عضو اول task_info بیندازیم، شیئی که آدرس آن فاش میشود و زمینه ی مورد علاقه ما است ، در اینجا addr_limit است که فضای نوشتن دلخواه سمت هسته را فراهم میسازد. Macro های بسیار مهمی وجود دارند که از نظر امنیت به این زمینه اشاره میکنند. بیایید به یکی از آنها نگاه کنیم (access_ok).

تابع access_ok
تابع 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 خود آن را قرار میدهیم.

 

ساختار iovec
ساختار iovec
ساختار iovec در خصوص خواندن داده های دلخواه

تخریب 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 کنترل خواهیم کرد.

 

محل نوشتن دوم دلخواه
محل نوشتن دوم دلخواه
محل نوشتن دوم دلخواه در ساختمان آزاد شده binder_thread

استراتژی حمله به شرح زیر است:

  1. یک جفت سوکت (socketpair) ایجاد کنید.
  2. ساختمان binder_thread را روی شیء آزاد کنید تا پشته iovec را بواسطه recvmsg بر روی binder_thread همپوشانی (overlap) شود.
  3. به‌صورت پیشگیرانه 1 بایت بنویسید تا اولین iovec را ساخته شود بدون آنکه تخریبی تولید کند.
  4. ساختارهای iovec را از طریق recvmsg در حافظه قدیمی binder_thread بنویسید.
  5. برای خراب کردن ساختار iovec، استفاده پس از رایگان (UAF) / قطع پیوند (Unlink) را فعال کنید.
  6. write را روی socketpair فراخوانی کنید، که از iovec برای خراب کردن iovec بعدی برای خراب کردن حافظه کنترل شده استفاده کند.

اما ماموریت های پردازش والد (Parent Thread)

  1. نوشتن پیشگیرانه 1 بایت داده برای برآورده کردن اولین درخواست iovec در recvmsg.
  2. راه‌اندازی free در binder_thread.
  3. نوشتن پشته iovec در سوکت و انتظار روی داده‌هایی که با درخواست‌های iovec مطابقت دارند از طریق recvmsg()

ماموریت های خط پردازش فرزند (Child Thread) مسئول موارد زیر است:

  1. خراب کردن iovec با فعال کردن لغو پیوند (Unlink) از طریق حذف event EPOLL
  2. وقتی فراخوانی recvmsg در پردازش والد (Parent Thread) ادامه یابد، نوشتن داده‌ هایی که ساختارهای iovec را خراب میکنند.

با کنار هم گذاشتن همه اینها، در نهایت به کد زیر میرسیم تا فرآیند والد addr_limit را خراب کنیم.

فرایند نوشتن دلخواه
فرایند نوشتن دلخواه
فرایند نوشتن دلخواه و دور زدن محدودیت های access_ok در خصوص اشاره گرها

خلاصه

شایان ذکر است که اکسپلویت به طور قابل توجهی پایدار است. خیلی به ندرت از کار می افتد، بنابراین فقط باید اکسپلویت را دوباره اجرا کنید و آماده کنید. به طور کلی، کشف خانوم Maddie Stone در خصوص آسیب پذیری ساختمان binder_thread موجب بهره‌برداری آن توسط شرکت NSO در سیستم عامل های اندرویدی شد که در نسخه های بعدی هسته هم مثل اینکه بهره‌برداری شده است.

ناشناسی در مقابل دفاع (Defense Evasion)

جاسوس افزار Pegasus برای مخفی سازی وجود خود با استفاده از شبکه های سلولی به جای Wi-Fi سازمانی ارتباط خود را برقرار کند تا از سیستم های نظارت شبکه سازمانی عبور کند. همچنین ممکن است با استفاده از سایر پروتکل های غیبر مبتنی بر اینترنت مانند SMS، NFC یا بلوتوث برای دور زدن سیستم های نظارت شبکه ارتباط برقرار کند.

ساختار URL فرمان C2
ساختار URL فرمان C2
فرایند و طریقه تعریف ساختار خط فرمان را مبتنی بر HTTP میبینیم

Pegasus قادر است از طریق مکانیسم ها و پروتکل های مختلف با زیرساخت های برنامه ریزی شده ی مهاجمان ارتباط برقرار کند. این ارتباط از طریق پیام کوتاه، از طریق HTTP، و از طریق پروتکل انتقال MQTT بوده و بخش‌های زیر به جزئیات نحوه استفاده نسخه اندروید Pegasus از هر یک از این مکانیسم‌ها و پروتکل‌ها می‌پردازد.

  1. اولین قدم دریافت فرمانهایی که در تنظیمات اولیه گنجانده شده است.
  2. دومین قدم ارسال فرمان ها بواسطه SMS.
  3. ارسال و دریافت فرمان و داده مبتنی بر HTTP با سرور C&C مهاجم.

اکتشاف (Discovery)

اکتشاف شامل تکنیک‌ هایی است که به مهاجم اجازه میدهد درباره ویژگی‌ های سیستم عامل موبایل همراه و احتمالاً سایر سیستم‌ های در شبکه‌ اطلاعات کسب کنید. هنگامی که مهاجم به یک سیستم جدید دسترسی پیدا می کند، باید برای انجام مراحل بعد حمله خود، عملیات شناسایی داخلی سیستم عامل را انجام دهد. سیستم عامل ممکن است قابلیت هایی را ارائه دهد که به این مرحله جمع‌آوری اطلاعات پس از نفوذ کمک کند.

برنامه های هدف Pegasus

سرشماری برنامه های شبکه اجتماعی
سرشماری برنامه های شبکه اجتماعی
بررسی و کاوش نرم‌افزار های نصب شده بر سیستم عامل قربانی

برنامه های مورد هدف شناسایی Pegasus قالبا برنامه شبکه های اجتماعی و سرویس دهنده های سمت کاربر ایمیل بوده است، لیست برنامه های مورد شناسایی به شرح زیر است:

  • WhatsApp
  • Skype
  • Facebook
  • Viber
  • Kakao
  • Twitter
  • 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 در بدافزار

تابع فعال سازی KeyLogger
تابع فعال سازی KeyLogger
تابع فعال سازی 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 که در بدافزار خود قرار داده است

تشریح برخی فرمان های تعریف شده در 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
ادامه خط فرمان و کنترل
ادامه خط فرمان و کنترل
ادامه خط فرمان و کنترل Pegasus که در بدافزار خود قرار داده است

دستور بعدی upgrade است که از برنامه درخواست می کند تا یک ارتقاء را در قالب Package دانلود و نصب کند از یک URL مشخص، دستور بعدی camCmd از برنامه می‌خواهد با دوربین جلو یا عقب دستگاه یک Screenshot یا عکس بگیرد. همچنین دستور emailAttCmd هم دستور دیگری است که میتواند باعث شود برنامه یک پیوست ایمیل خاص را بازیابی کند. دستورها اضافی برای تنظیم بیشتر گزینه های قابل تنظیم در برنامه موجود است. هر دستور شامل یک شناسه تایید است که مشتری برای تایید دریافت فرمان به سرور C2 ارسال می کند.

نمونه ای از پیامک فرمان
نمونه ای از پیامک فرمان
در این تصویر نمونه ای از ارسال فرمان به بدافزار در قالب پیام احراز شماره گوگل میباشد

دریافت پیامک (Inbound SMS)

یکی از این روش های مورد استفاده Pegasus برای شناسایی نشدن این است که دستورها موجود خود را در متن پیام SMS که بنظر پیامی از طرف سرویس دهنده های گوگل است ارسال میکند، این استفاده از عنوان گوگل برای پنهان نگه داشتن مخرب بودن محتوای اصلی این پیامک بوده و در قالب پیامک احراز هویت گوگل طراحی شده است.

توابع پارسر فرمان های SMS
توابع پارسر فرمان های 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 را هنگام راه‌اندازی در صورت روت بودن دستگاه حذف کند:

کلاس مربوط به حذف WAP
کلاس مربوط به حذف 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 است.

نمونه ترافیک رمز شده
نمونه ترافیک رمز شده
نمونه رمز شده قسمت Body در خصوص درخواست های ارسالی

طرح و مسیر مورد استفاده در ساخت URL برای C&C در برنامه رو میبینیم که کدگذاری شده است. هر قسمت از Body درخواست نوع محتوای multipart/form-data است که کدگذاری شده است، همچنین با استفاده از gzip فشرده شده و سپس AES با استفاده از کلید SessionId2 رمزگذاری می شوند.

نوع محتوای XML
نوع محتوای XML
محتوای XML که شامل اطلاعات قربانیان به C2 است

قالب فایل جمع‌آوری داده بسته به داده‌های در حال انتقال متفاوت خواهد بود، اما فایل‌ها با نوع محتوای XML هستند که حاوی داده‌ های استخراج شده از دستگاه قربانی است و در پاسخ به فرمان صادر شده برای برنامه بدافزار بوده است.

داده های استخراج شده
داده های استخراج شده
داده های استخراج شده مبتنی بر XML از دستگاه قربانی

ساختار پاسخ های داده شده از بدافزار

پاسخ ها (Response) نیز اسناد XML رمزگذاری شده AES هستند. کلید رمزگذاری با الگوریتم زیر تولید می شود:

MD5({0xB6, 0x27, 0xDB, 0x21, 0x5C, 0x7D, 0x35, 0xE4, token})

توابع پارسر نوع محتوای XML
توابع پارسر نوع محتوای XML
مجموعه توابعی که نوع محتوای XML رو برای ارسال مقادیر درخواست شده C2 استفاده شده است

تجزیه پاسخ XML در داخل برنامه با نادیده گرفتن متدهای ()startElement و ()endElement و ()characters و همچنین کلاس org.xml.sax.helpers.DefaultHandler از کتابخانه SAX2 و ردیابی start/end عناصر مختلف. این روش تجزیه برنامه کاربر این امکان را می دهد که بیش از یک ساختار پاسخ (Response) ممکن را بپذیرد، بنابراین نمی توان با اطمینان تعیین کرد که بخش سمت سرور برنامه چگونه همه دستورها خود را ساختار می دهد، فقط می توان تعیین کرد که برنامه به کدام دستورها پاسخ می دهد.

منابع

https://www.youtube.com/watch?v=U2qvK1hJ6zg
https://www.ft.com/content/4da1117e-756c-11e9-be7d-6d846537acab
https://github.com/maddiestone/ConPresentations/blob/master/OffensiveCon2020.BadBinder.pdf
https://googleprojectzero.blogspot.com/2019/11/bad-binder-android-in-wild-exploit.html
https://lookout.com/blog/pegasus-android

Post Views: 6,209
نوشتهٔ پیشین
دنیای آسیب پذیری XSS
نوشتهٔ بعدی
آسیب پذیری های روز صفر ایمیل سرور Zimbra

دیدگاهتان را بنویسید لغو پاسخ

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

این فیلد را پر کنید
این فیلد را پر کنید
لطفاً یک نشانی ایمیل معتبر بنویسید.
شما برای ادامه باید با شرایط موافقت کنید

نویسنده

Afshin Fakhim
CEO & Co-Founder at Unk9vvN

نوشته‌های تازه

  • آسیب پذیری های روز صفر ایمیل سرور Zimbra 14 مرداد 1400
  • تحلیل تکنیکال جاسوس افزار پگاسوس (قسمت اول) 14 مرداد 1400
  • دنیای آسیب پذیری XSS 14 مرداد 1400
  • مسمومیت حافظه پنهان وب 14 مرداد 1400
  • تزریق کد قالب سمت کاربر به AngularJS 14 مرداد 1400

دسته‌ها

  • وبلاگ – آسیب پذیری ها (4)
  • وبلاگ – جرم شناسی دیجیتال (1)
  • وبلاگ – فتح پرچم (1)

آخرین نوشته ها

آسیب پذیری های روز صفر ایمیل سرور Zimbra
13 دی در 6:24 pm
دنیای آسیب پذیری XSS
13 تیر 1400
مسمومیت حافظه پنهان وب
12 تیر 1400

ارتباط با ما

[email protected]
۰۶۲۴۹ ۳۶۸ ۰۲۶
استان البرز، کرج، فردیس، فلکه اول، برج نگین
Twitter
GitHub
Telegram
YouTube
LinkedIn
Instagram

تمامی حقوق این سایت متعلق به شرکت اکسین ایمن نیکراد است.

  • شرایط استفاده
  • سیاست حفظ حریم خصوصی