ویرگول
ورودثبت نام
neo vortex
neo vortex
neo vortex
neo vortex
خواندن ۱۱ دقیقه·۱ سال پیش

دوست داشتنی ولی سختگیر!


تو این مقاله قصد دارم درباره اجرای جدید ترین نسخه بلو بانک روی گوشی های قفل شکسته (rooted) اندروید صحبت کنم.

مقدمه

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

من از اولین سال هایی که گوشی اندروید داشتم همیشه علاقه مند بودم که تنظیمات سیستمی رو عوض کنم و خلاصه کلی با دل و روده سیستم عامل ور برم! مهم ترین لازمه این کار توی سیستم عامل اندروید داشتن دسترسی سیستمی یا همون روت هستش. اون موقع ها اینقدر راهکار های روت پیچیده و متفاوت نبود. کلا یه برنامه supersu بود که فلش میزدی و تمام! اما این روزا اوضاع کمی فرق داره از طرفی راهکار های مدیریت روت خیلی پیشرفته تر شدن و امکانات زیادی رو در اختیار کاربراشون میزارن از جمله نصب module های مختلف و غیره و از طرف دگ خیلی از برنامه ها و بازی ها به دلایل امنیتی خودشون برای جلوگیری از ضرر های مالی و تقلب تمایلی ندارن که روی دستگاه های روت شده اجرا بشن. اگرچه برخی از این نگرانی ها رو درک میکنم ولی حس میکنم بخصوص در سال های اخیر این نگرانی ها پارانویدی شدن!. به هرحال متقابلا راه حل های مدیریت روت هم به دنبال ارایه راهکار هایی برای پنهان سازی خودشون از دست این دسته از برنامه ها هستن. قابلیت هایی مثل Enforce Denylist و HideMagisk توی برنامه مجیسک فقط مثال هایی از این تلاش هاست. ولی خب همونطور که می تونید حدس بزنید این بازی بی شباهت به بازی موش و گربه نیست و هر دو هر روزه در حال پیشرفت هستن.

یکی از مهم ترین دسته بندی برنامه هایی که شناسایی دستگاه های قفل شکسته براشون اهمیت داره موسسه های مالی و به خصوص بانک ها هستن. توی ایران هم چند سالیه که چنین قابلیت هایی توی موبایل بانک های ایرانی پیاده میشه و توی چند بانکی که من حساب دارم از جمله پاسارگاد و ملی همگی چنین قابلیت رو با شدت کم تر و زیاد تر دارن. اما همیشه راهکار هایی که من برای پنهان سازی روت از دید این برنامه ها داشتم پیشرفته تر از متد های شناسی اون ها بوده و هست. غیر از یک برنامه و اونم بلو بانک متعلق به بانک سامان !

بلو بانک همیشه برام چالش اصلی بوده و مجبور بودم ترفند های به روزتری رو اعمال کنم که بتونم ازش استفاده کنم. استفاده از Magisk Alpha , استفاده از Module به اسم Shamiko , نصب lsposed و HMA ,همچنین استفاده از ماژول هایی برای پاس کردن تست های Play Integrity و SafetyNet.

همیشه بعد انجام این همه عملیات بالاخره بلو بانک تسلیم می شد و اجازه استفاده می داد!

تا اینکه چند روز پیش تصمیم گرفتم برای هزارمین بار ROM‌ گوشیمو عوض کنم و طبعا بعضی از نرم افزار ها نیاز به نصب مجدد داشتن و از جمله اونا برنامه بلو بانک بود که اخرین نسخش ورژن ۳٫۱٫۱٫۰ بود.

اما در کمال تعجب دیدم که هر ترفند بروزی که برای اجرای برنامه استفاده میکنم بی نتیجس و اجازه ورود نمیده!

چند تا گزینه داشتم

  • استفاده از نسخه وب
  • نصب روی یک دستگاه بدون روت
  • کاهش ورژن (Downgrade)
  • رفتن سراغ راه حل های روت مختلف مثل KernelSu
  • بالازدن آستین و اجرای جدید ترین نسخه برنامه در محیط روت و Magisk!

(طبعا گزینه اخر رو انتخاب کردم وگرنه شما الان در حال خوندن این مقاله نبودید!)

یه نکته قبل از ادامه مقاله قابل ذکره و اون اینکه به دلیل اینکه در طول پروسه توی ذهنم نبود که بعدا مقاله بنویسم برای اینکار. بعضی از عکسا رو به ناچار فوتوشاپ کردم!

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

بررسی رفتار سامانه و برآورد نقشه راه

برای ترسیم تقشه راه و رویکرد مناسب برخورد با موضوع اول نیاز بود که رفتار سامانه رو بررسی کنم.

مورد هایی مثل :

  • ایا از کتابخانه اماده ای برای تشخیص دستکاری سیستم استفاده می شه ؟
  • ایا غیر از روت از پارامتر های دگ هم داخل سیستم استفاده می شود؟
  • ایا از API هایی که Google در اختیار برنامه نویسان برای تشخیص دستکاری در سیستم گذاشته هم استفاده شه ؟
  • ایا تشخیص صرفا on-device انجام میشه یا اینکه سرور هم در این کار دخیله ؟

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

حذف روت!

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

قبل از حذف روت به دلیل استفاده از ماژول های مربوطه Play Integrity و SafetyNet همه تست های مربوطه (غیر از یکی که بعدا دربارش صحبت میکنیم ) پاس می شه.

پس احتمالی که اینجا به ذهن میرسه اینه که ممکنه اخرین تست مربوط به Play Integrity کار رو خراب کرده باشه. با بررسی بیشتر در مورد تست اخر به این نتیجه رسیدم که پاس کردنش بصورت نرم افزاری خیلی دشواره و اکثرا فقط با بستن بوتلودر پاس میشه.

پس تصمیم گرفتم روت رو حذف کنم و رفتار برنامه رو دوباره بررسی کنم.

برای حذف روت از گزینه Uninstall Magisk داخل خودبرنامه Magisk استفاده کردم.

بعد از راه اندازی مجدد دستگاه همونطور که انتظار میرفت تست های SafetyNet و Play Integrity دگ پاس نمیشن.

اما در کمال تعجب blu کار میکنه !

به همین راحتی حذف روت جواب خیلی از سوالاتمون رو داد!

  • تاثیر SafetyNet روی تشخیص دستکاری بلو صفره!
  • تاثیر Play Integrity رو تشخیص دستکاری بلو صفره!
  • (از نتیجه ۲ این هم نتیجه میشه که پس STRONG_INTEGRITY هم تاثیری در ماجرا نداره. پس اصلا نیازی نیست بریم دنبال پاس کردن اون!)
  • ای دی دستگاه توی سرور BlackList نمیشه


بررسی ترافیک شبکه

خب میشه گفت که قسمت راحت ماجرا تموم شده و از الان وارد قسمت های سخت میشیم!

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

بنابراین برنامه HTTP Toolkit رو باز کردم و شروع کردم به گوش دادن به ترافیک ها.

قاعدتا قرار نبود همه چیز با یه تغییر پروکسی ساده کار بکنه. از اونجایی که دسترسی روت داشتم مجوز HTTP Toolkit رو به عنوان مجوز مجاز سیستمی (system certificate) و نه (user certificate) به سیستم تزریق کردم ولی نتیجه اون چیزی نبود که میخواستم:

ظاهرا شرایط سخت تر از چیزیه که فکر می کردم .

بلو بانک یه سیستم Certificate Pinning پیاده کرده که حتی به مجوز های سیستمی هم اعتماد نمیکنه و تنها به مجوز اصلی خودش اعتماد میکنه.

تصمیم گرفتم بالاخره چاقوی سوییسی مهندس معکوس اندروید رو اجرا کنم. برنامه frida یه برنامه خیلی کاربردی و چند کارس که به مهندسی معکوس روی پلتفرم های مختلف از جمله اندروید خیلی کمک میکنه.

درواقع میتونید این برنامه رو شبیه یه debugger ببینید که بهتون اجازه میده هنگام اجرای برنامه ها تویcontextشون کد خودتون رو ران کنید.

در ادامه خیلی بیشتر در مورد کار با frida توضیح میدم ولی فعلا در این اندازه بدونید که برای دور زدن certificate pinning ازش استفاده کردم و نتیجه موثری داشت. میتونید لینک مقاله ای که ازش استفاده کردم رو اینجا ببینید.

بعد از دور زدن certificate pinning حالا میتونستم ترافیک رو ببینم.


ترافیک هارو میتونم توی این برنامه به دو دسته تقسیم کنم.

  • ترافیک های چند رسانه ای
  • ترافیک های API

دسته اول برای انتقال همین ویدیو و عکس های خوشگل بلو استفاده میشه !

اما دسته دوم که برای ما اهمیت داره رو بیشتر بررسی میکنیم.

برای ورود به برنامه مجموع دوتا API کال میشه و بعد از این دوتا کال هست که ما میریم تو دیوار و اجازه ورود پیدا نمیکنیم!

https://openapi.sdb247.com/openapi/v0/session/key-exchange
https://openapi.sdb247.com/openapi/v0/session/login

نکته جالبی که وجود داشت این بود که پروتکل ارتباطی بر پایه ی Protobuf بود!


من خودم از طرفدارای protobuf هستم و معتقدم علاوه برعملکرد بهتر. ساختار کد هم منسجم تر میکنه.

اما توی مهندسی معکوس Protobuf چیز دردناکیه!

چیزی که از دیتا ها بصورت خام دیده میشه اینه که یه سری از اطلاعات مربوط به دستگاه همراه با درخواست ورود به سرور ارسال میشه.


بدون داشتن contract schema نمیشه خیلی متوجه شد که هر فیلد بیانگر چه چیزی هست و مقدار داخلش دقیقا چیه.

با بررسی بیشتر متوجه شدم که بعد از تبادل کلید (که احتمالا زیاد برای کار ما اهمیت نداره) در مرحله لاگین بعد از ارسال اطلاعات ( که هنوز خیلی دربارش نمیدونم) سرور خطای ۵۰۰ میده با یه پیام جالب !

نتیجه گیری از این بخش:

  • برنامه یک سری از اطلاعات دستگاه رو به سرور ارسال می کنه
  • این سروره که تصمیم میگیره با وجود دستکاری در سیستم اجازه ورود بده یا نه!


حدسی که الان توی ذهنم شکل گرفته اینه که احتمالا توی همون اطلاعات مرموز . جزییاتی در مورد دستکاری سیستم هم به سرور ارسال میشه و بعد سرور تصمیم میگیره که دسترسی نده.

بررسی کد منبع برنامه

با توجه به اینکه تمام سوالتی که توی ذهنم بود رو نگرفته بود و هنوز نمیدونستم دقیقا با چی طرف هستم. تصمیم گرفتم سعی کنم نگاهی به سورس کد بندازم.

برای decompile کردن برنامه های اندروید ابزار های زیادی هست ولی من خودم jadx رو ترجیح می دم. هم رابط کاربری خوبی داره هم امکانات مناسب. نتایج هم معمولا قابل قبول هستن.

سورس کد رو که باز کردم متوجه شدم کد تا اندازه ای Obfuscate شده.

ازاونجایی که قبلا سایقه کار با کد های Obfuscated رو داشتم و لول Obfuscate توی این سورس کد زیاد نبود خیلی مشکلی برای خوندن کد ها نداشتم.

بعد از گشت و گذار طولانی مدت به یک سری کلاس جالب توجه رسیدم که مهمترینش کلاس SessionLoginRequest بود.


مهمترین قسمت این کلاس فیلد Device بود که همونطور که از اسمش بر میاد مربوط به خصوصیت های دستگاه مورد استفاده هست.

نکته مهم در مورد این کلاس این بود که ساختارش خیلی شبیه همون اطلاعاتی نامفهموم اولیه بود که تویHTTP Toolkit تونسته بودم از برنامه ببینم.

بعد از بررسی فیلد های این کلاس متوجه شدم مهمترین فیلد این کلاس DeviceFlags هست.

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

نکته جالب توجه وجود فیلدی به اسم rooted هستش که نوع boolean داره!

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


Called blu.proto.protomodels.DeviceFlags.toString
Result of blu.proto.protomodels.DeviceFlags.toString: DeviceFlags(rooted=true, secureEnclaveCapable=true, biometricAuthenticationCapable=true, biometricAuthenticationActivated=true, nfcCapable=false, nfcPaymentCapable=true, touchInputCapable=false, pushNotificationAccessDenied=false, unknownFields={}) RETURN TYPE : java.lang.String
Argument 0: DeviceFlags(rooted=true, secureEnclaveCapable=true, biometricAuthenticationCapable=true, biometricAuthenticationActivated=true, nfcCapable=false, nfcPaymentCapable=true, touchInputCapable=false, pushNotificationAccessDenied=false, unknownFields={})
Result of blu.proto.protomodels.DeviceFlags$Companion$descriptor$2$1$2.get: true RETURN TYPE : java.lang.Object
Boolean return type detected. Returning false.
Called blu.proto.protomodels.DeviceFlags$Companion$descriptor$2$1$4.get
Called blu.proto.protomodels.DeviceFlags.toString
Result of blu.proto.protomodels.DeviceFlags.toString: DeviceFlags(rooted=true, secureEnclaveCapable=true, biometricAuthenticationCapable=true, biometricAuthenticationActivated=true, nfcCapable=false, nfcPaymentCapable=true, touchInputCapable=false, pushNotificationAccessDenied=false, unknownFields={}) RETURN TYPE : java.lang.String
Argument 0: DeviceFlags(rooted=true, secureEnclaveCapable=true, biometricAuthenticationCapable=true, biometricAuthenticationActivated=true, nfcCapable=false, nfcPaymentCapable=true, touchInputCapable=false, pushNotificationAccessDenied=false, unknownFields={})
Result of blu.proto.protomodels.DeviceFlags$Companion$descriptor$2$1$4.get: true RETURN TYPE : java.lang.Object
Boolean return type detected. Returning false.
Called blu.proto.protomodels.DeviceFlags$Companion$descriptor$2$1$6.get
Called blu.proto.protomodels.DeviceFlags.toString
Result of blu.proto.protomodels.DeviceFlags.toString: DeviceFlags(rooted=true, secureEnclaveCapable=true, biometricAuthenticationCapable=true, biometricAuthenticationActivated=true, nfcCapable=false, nfcPaymentCapable=true, touchInputCapable=false, pushNotificationAccessDenied=false, unknownFields={}) RETURN TYPE : java.lang.String
Argument 0: DeviceFlags(rooted=true, secureEnclaveCapable=true, biometricAuthenticationCapable=true, biometricAuthenticationActivated=true, nfcCapable=false, nfcPaymentCapable=true, touchInputCapable=false, pushNotificationAccessDenied=false, unknownFields={})
Result of blu.proto.protomodels.DeviceFlags$Companion$descriptor$2$1$6.get: true RETURN TYPE : java.lang.Object

الان دو تا راه داریم

  • بررسی ساز و کار ست شدن این فیلد و مکانیزم تشخیص دستکاری در سیستم عامل.
  • انجام mask برای این فیلد توی Runtime

از اونجایی که روش اول خیلی سخت تره و ممکنه مدت ها طول بکشه و درنهایت نیاز به اقدامات ثانویه برای رفع اثرات دستکاری در سیستم عامل باشه ترجیح دادم راه حل دوم رو انتخاب کنم.

حمله اخر!

برای mask کردن مقدار rooted ابزار محبوب frida رو دوباره ران میکنیم و این اسکریپت رو براش می نویسیم:

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

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

نکته مهم توی این کد اینه که من دقیقا نمیدونم ساز و کار تشخیص دستکاری چیه و حتی دقیق نمیدونم کدوم تابع تقش بررسی این موضوع رو داره ولی از یه تکنیک به اسم blind fuzzing استفاده کردم که بر اساس اون هر فیلدی که توی DeviceFlags باشه و نوع boolean داشته باشه با مقدار false جایگزین میشه .

حالا این میتونه قابلیت nfc باشه یا هر فیلد دگ که توی DeviceFlags هست. مهم اینه که در نهایت rooted هم برابر false خواهد شد.

خب وقت اجرای برنامس

neo@neo-PC ~/P/p/frida [1]> frida -U -l main.js -f com.samanpr.blu

و خروجی را نظاره میکنیم:

و تمام ! وارد شدیم:



سخن پایانی

در حاشیه این کار به یه مورد جالب هم بر خوردم و برام جالب بود که یه بانک چطوری api key یکی از سرویس هایی که استفاده میکنه رو بصورت هاردکد داخل برنامش گذاشته باشه!

توی این مثال api key مربوط به map.ir رو بصورت هاردکد توی برنامه قرار داده بودن. گرچه تاریخ توکن مال دو سال پیشه و scope ش basic هست ولی به هرحال اصلا کاری خوبی نیست اینکار!

خالی از لطفه اگه نگیم Obfuscation کد بعضی جاها خوب عمل کرده بود!

با اینکه سعی کردم تمام مراحلی که خودم طی کردم رو اینجا بنویسم ولی خیلی از قسمت ها رو حذف کردم که متن بیشتر از این طولانی نشه!

در کل کاش موسسه های مالی و بانک ها در مورد دسترسی روت اینقدر سخت گیری نکنن و دست از این بازی موش و گربه بردارن!

این متن و کاری که انجام دادم برای اهداف اموزشی و تفریح بوده . هست و خواهد بود :)














سیستم عاملاندرویدامنیتنرم افزار
۳
۰
neo vortex
neo vortex
شاید از این پست‌ها خوشتان بیاید