Mohsen Tahmasebi
Mohsen Tahmasebi
خواندن ۸ دقیقه·۳ سال پیش

از آشوب تا API: مهندسی معکوس کافه بازار


پروژه مهندسی معکوس کافه بازار یکی از جذاب ترین و پیچیده ترین پروژه های من بود. در https://vrgl.ir/cLnN9 و https://vrgl.ir/QFfBe مفصلا درباره نتایجش صحبت شد و اسکریپت ها و اطلاعاتش در https://github.com/moh53n/bazz منتشر شد، اما در این پست میخوام به فرایند فنی مهندسی معکوس اپ و API کافه بازار بپردازم، پس کمی فنی تر میشیم :)

اوایل تیر سال ۹۸ و بعد از خوندن مقاله خنده دار ژاپنی درباره امنیت داخل پلتفرم کافه بازار تصمیم گرفتم یک پروژه برای اسکن کل کافه بازار رو راه بندازم...

دیکامپایل کردن کافه بازار

اپ های اندرویدی و در واقع جاوا، به دلیل کامپایل شدن به Java ByteCode تا حد بسیار زیادی قابل Decompile شدن هستن، به این معنی که خروجی کامپایلر جاوا رو میشه تبدیل به سورس اصلی جاوا کرد (تا حد مناسبی). این باعث میشه تقریبا هر اپلیکیشن اندرویدی رو بشه تبدیل به سورس اصلیش کرد و خب، این خوب نیست؛ نه از نظر امنیت نه از نظر اینکه ممکنه اپ شما فردا با اندکی تغییر توسط یکی دیگه منتشر بشه. نتیجتا پای روش ها و ابزار های مبهم ساز یا Obfuscation میاد وسط. این ابزار ها و روش ها (که محدود به جاوا نیستن) با بهم ریختن سورس کد و خروجی کامپایلر باعث میشن بعد از دیکامپایل کردن (یا حتی حین دیکامپایل) ابزار های بررسی سورس کد و محققی که سورس رو بررسی میکنه به مشکل بخورن. چطوری؟ اینطوری:

بخش مربوط به ساخت رکوئست برای ارسال به سرور
بخش مربوط به ساخت رکوئست برای ارسال به سرور

همونطور که میبینید، اسم خیلی چیز ها (فانکشن ها کلاس ها ...) به a و b و... تبدیل شده و اینطوری در شرایطی که شما با ده ها (یا صد ها) کلاس و متد مختلف که با هم ارتباط دارن کار میکنید، خودتون و ابزار های کمکی مثل آنالیزور کد، بعضی چیز هارو گم میکنید و وارد یک دنیای گیجی می شید.

وقتی نسخه ۷.۳۱.۱ کافه بازار رو اولین بار با jadx دیکامپایل کردم و سورس رو دیدم، تقریبا مطمئن شدم این کار پیچیده خواهد بود...

مواجهه با آشوب و ابهام

هدف نهایی من فهمیدن نحوه ساخته شدن رکوئست های کلاینت برای نصب یک برنامه (دادن لینک دانلود) و کار کردن با جواب سرور بود، بنابراین اولین کاری که پس از گم شدن توی سورس کد و ناامیدی از استاتیک آنالیز (خوندن و بررسی کد) انجام دادم، رفتن سراغ داینامیک آنالیز (بررسی برنامه موقع اجرا) بود تا ارتباط کلاینت و سرور کافه بازار رو بررسی کنم. در حالت ساده ای که انتظارش رو داشتم، بعد از دور زدن ارتباط استاندارد https (که در واقع با نصب گواهی ساخته شده توسط خودم روی اندروید برای مهیا شدن امکان خوندن ارتباط ssl/tls به صورت رمزنگاری نشده بود) باید با چنین چیزی مواجه میشدم:

{"id":1,"method":"getAppDownloadInfo","params":["com.example.android",27]}

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

well...
well...

این مشخصا یک ssl pinning ساده نبود و من تقریبا هیچ تصوری نداشتم چه اتفاقی داره میفته. بعد از کمی تفکر این موارد روشن شد:

۱. با نگاه به iv میشد فهمید p و iv مربوط به الگوریتم رمزنگاری AES هستن و p کلید هست، ولی اینجا دو تا p بود!

۲. پارامتر method که خوشبختانه مشخص بود و متد API مورد نظر ما رو مشخص میکرد.

۳. بعد از ده بیست بار تست این نوع رکوئست (و فکر کنم انواع دیگه رکوئست) id همیشه ۱ بود پس عملا مهم نبود.

۴. با کمی حدس میشد فهمید enc_resp مخفف encrypted response بود، به معنی درخواست ریسپانس رمزنگاری شده از سرور.

۵. با همون حدس non_enc_params هم پارامتر های رمزنگاری نشده رکوئست ما بود که عموما متادیتا مربوط به دستگاه کاربر بود.

۶. پارامتر hash هم طبق حدس باید هش درخواست میبود برای اطمینان از دستکاری نشدن رکوئست.

۷. پارامتر params در رکوئستی که من میخواستم خالی بود پس هیچ.

۸. و نهایتا پارامتر معظم packed که مشخصا رمزنگاری شده بود و قادعتا حاوی جزئیات رکوئست...

بعد از بار ها تلاش برای ساختن یک رکوئست AES شده با iv و p نمونه، موفقیتی حاصل نشد؛ چاره ای جز برگشت به استاتیک آنالیز اون جهنم نبود.

غلبه بر آشوب و ابهام

همه سر نخ ها در همون عکس اول بود:

عکس تکراری
عکس تکراری

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

در جستجوی نحوه رمزنگاری
در جستجوی نحوه رمزنگاری

و نهایتا گم شدن:

ها؟ (مسیر غلط)
ها؟ (مسیر غلط)


بعد از ساعت های بیخوابی مشخص بود در بخش packed با AES/CBC/PKCS5Padding طرف بودم و البته یک جایی هم با RSA/None/OAEPPADDING که این مورد رو ندیده بودم و حالا فهمیدم با چند مرحله رمزنگاری طرف هستم، اگر فقط یکی از پارامتر ها درست و با ترتیب مورد انتظار رمزنگاری نمیشد سرور جواب درستی نمیداد، و من هنوز دقیقا نمیدونستم بخش packed در متدی که میخوام قبل از انکریپت شدن، دقیقا چه شکلی هست...

کار با Frida

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

فریدا (frida) یک ابزار آنالیز داینامیک هست که قدرت فوق العاده ای به آنالیزور برای بررسی و دستکاری اپ موقع اجرا (run time) میده. با اجرای سرور فریدا روی اندروید و اتصالش به سیستم خودم میتونستم اسکریپت هایی رو در قالب جاوا اسکریپت (مار از پونه بدش میاد، در لونش سبز میشه) اجرا کنم که بهم اجازه نگاهی عمیق تر به روند اجرای برنامه رو میداد. یکی از کار های مهم این وسط، هوک (hook) کردن بعضی ابزار هایی بود که کافه بازار ازش استفاده میکرد و با اینکار من میتونستم ورودی و خروجی بعضی متد ها رو قبل از انجامشون بخونم و گاهی تغییر بدم. تا اون لحظه من هیچوقت جدی با فریدا کار نکرده بودم چون تا این حد به چالش کشیده نشده بودم، پس چند ساعتی (خیلی ساعت) رو هم درگیر یادگیری و نوشتن دست و پا شکسته یک اسکریپت فریدا شدم.

اسکریپت فریدا
اسکریپت فریدا

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

خروجی اسکریپت های اولیه فریدا
خروجی اسکریپت های اولیه فریدا

این کار قبل از هر چیز به من کمک کرد تا ترتیب مراحل ساخته شدن و رمزنگاری اجزای رکوئست هارو بفهمم، اما باید متوجه میشدم رکوئست ها قبل از انکریپت (رمزنگاری) شدن چه شکلی هستن و کلید و iv ساخته شده چه تفاوت هایی با رکوئست نهایی کلاینت به سرور های کافه بازار دارن. بعد از ده ها آزمایش مختلف و هوک کردن انواع و اقسام اینترنال های کافه بازار و کال های رمزنگاری، اولین موفقیت اساسی حاصل شد، رکوئست خام و مقدار انکریپت نشده پارامتر packed:

مقدار اصلی پارامتر packed برای رکوئست جزئیات اپ
مقدار اصلی پارامتر packed برای رکوئست جزئیات اپ

به همین روش مقدار اصلی پارامتر packed برای گرفتن لینک دانلود اپ (getAppDownloadInfo) رو هم گرفتم، اما هش sha1 این مقدار با هش رکوئست درست مطابقت نداشت، بعد از کمی هوک بیشتر و گشتن در سورس متوجه شدم کافه بازار در واقع مقدار پیلود اصلی رو هش نمیکنه، بلکه یک رشته خاص همراه با یک uuid ثابت میسازه و بعد هش میکنه!:

{"7cc78271-e338-4edc-849c-b105c5d51ba5":["getAppDownloadInfo","com.example.android",27]}

حین بازی با فریدا و داغون کردن روند طبیعی برنامه در استفاده از ابزار های رمزنگاریش متوجه استفاده از RSA شدم، روی iv و p ها. پس دلیل تفاوت و کار نکردن p و iv بدست اومده من با خروجی نهایی در رکوئست هم مشخص شد. از طریق هوک کردن اپ پابلیک کی RSA رو هم بدست آوردم (که بعدتر مشخص شد به صورت serialize شده در resource های اپ وجود داشته و من کور بودم).

نهایتا یک مسئله باقی مونده بود، چرا دو تا p و در واقع دو تا کلید AES وجود داشت؟ بررسی ها با فریدا نشون میداد یکی از کلید ها اصلا در رمزنگاری نقشی رو ایفا نمیکنه (p2)، پس من هم یک کلید استفاده نشده AES ساختم و به عنوان p2 همراه رکوئست نهایی فرستادم، و بعد از شصت هفتاد ساعت اولین پاسخ درست به رکوئست ساختگیم رو از سرور گرفتم! در بررسی های بعدی مشخص شد احتمالا p2 کلیدی بوده که سرور پاسخش رو باهاش انکریپت میکرده و به کلاینت میفرستاده...

پاسخ کامل و درست سرور به رکوئست ساخته شده
پاسخ کامل و درست سرور به رکوئست ساخته شده

نتیجتا کافه بازار از سیستم بسیار عجیب و غریب چند مرحله ای رمزنگاری خودساخته اینچنینی استفاده میکرد:

  1. ساخت یک آبجکت json و قرار دادن متد مورد نظر و پارامتر لازم (در اینجا package name اپ خواسته شده)
  2. ساخت یک iv و دو کلید AES
  3. انکریپت (رمزنگاری) خروجی مرحله ۱ با iv و کلید اول توسط الگوریتم AES/CBC/PKCS5Padding
  4. انکریپت iv و دو کلید AES توسط public key کافه بازار (درون اپلیکیشن) با الگوریتم RSA/OAEPPADDING
  5. هش کردن پارامتر و متد مرحله ۱ در یک قالب خاص در کنار یک uuid ثابت با الگوریتم SHA1
  6. ساختن یک آبجکت json جدید و قرار دادن متد مورد نظر به همراه خروجی مرحله ۳ در پارامتر packed، قرار دادن سه خروجی مرحله ۴ شامل iv و دو کلید انکریپت شده، هش مرحله ۵ و اندکی (کمی بیشتر از اندکی) متادیتای رمزنگاری نشده.
اسکریپت اولیه ساخت یک رکوئست دانلود اپ
اسکریپت اولیه ساخت یک رکوئست دانلود اپ


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

سخن پایانی

این یکی از فوقالعاده ترین و سنگین ترین پروژه های من در اون زمان بود که بی اندازه باعث افزایش دانشم و کاهش غرورم شد. نتایج تحقیق در کنفرانس شبگرد ارائه شد که ظاهرا مورد اقبال قرار نگرفت به دلایلی...

امیدوارم این پست براتون مفید واقع شده باشه، اگر سوالی بود در کامنت ها یا توییتر من مطرح کنید، ممنون که مطالعه کردید ;)

نکات

  1. این مطلب به API قدیمی کافه بازار پرداخت که گرچه هنوز کار میکنه، اما API جدیدتر و ظاهرا بسیار ساده تری در حال استفاده هست.
  2. بعضی از تصاویر و سمپل های این پست مربوط به زمان انجام تحقیق و بعضی مربوط به زمان نگارش این پست هستن.
  3. مبهم سازی یا obfuscation که توسط ابزار هایی مثل proguard صورت میگیره لزوما برای مبهم سازی و سخت تر کردن مهندسی معکوس استفاده نمیشه و کاربرد بهینه سازی هم داره.
  4. هیچگونه تلاشی برای "هک" کافه بازار یا اخلال در عملکرد اون صورت نگرفت و این مطلب و تحقیق همونطور که واضحه، صرفا فرمول ارتباط کلاینت و سرور کافه بازار رو مورد نظر داشت.
  5. به طرز بسیار جالبی در طی انجام تحقیق و ارسال ده ها هزار فایل از سرور های کافه بازار به ویروس توتال، API کافه بازار متوجه ساختگی بودن این رکوئست ها (بر اساس متادیتا) نشد و خللی در روند اسکن صورت نگرفت!

منابع برای مطالعه بیشتر

  1. Frida: https://frida.re/docs/home/
  2. AES: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
  3. RSA: https://en.wikipedia.org/wiki/RSA_(cryptosystem)
  4. jadx: https://github.com/skylot/jadx
  5. Android obfuscation: https://developer.android.com/studio/build/shrink-code
  6. Java Bytecode: https://en.wikipedia.org/wiki/Java_bytecode
کافه بازارفریدااندرویدمهندسی معکوسبدافزار
بلاگ انگلیسی: https://moh53n.medium.com
شاید از این پست‌ها خوشتان بیاید