پروژه مهندسی معکوس کافه بازار یکی از جذاب ترین و پیچیده ترین پروژه های من بود. در 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 که خیلی شفاف خواسته اش رو بیان میکرد و جوابی رو از سرور میگرفت، اینطوری شاید حتی نیاز نبود سورس برنامه رو بررسی کنم، اما رکوئستی که من باهاش مواجه شدم اینجوری نبود...
این مشخصا یک 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) یک ابزار آنالیز داینامیک هست که قدرت فوق العاده ای به آنالیزور برای بررسی و دستکاری اپ موقع اجرا (run time) میده. با اجرای سرور فریدا روی اندروید و اتصالش به سیستم خودم میتونستم اسکریپت هایی رو در قالب جاوا اسکریپت (مار از پونه بدش میاد، در لونش سبز میشه) اجرا کنم که بهم اجازه نگاهی عمیق تر به روند اجرای برنامه رو میداد. یکی از کار های مهم این وسط، هوک (hook) کردن بعضی ابزار هایی بود که کافه بازار ازش استفاده میکرد و با اینکار من میتونستم ورودی و خروجی بعضی متد ها رو قبل از انجامشون بخونم و گاهی تغییر بدم. تا اون لحظه من هیچوقت جدی با فریدا کار نکرده بودم چون تا این حد به چالش کشیده نشده بودم، پس چند ساعتی (خیلی ساعت) رو هم درگیر یادگیری و نوشتن دست و پا شکسته یک اسکریپت فریدا شدم.
به این روش من سعی داشتم مقادیری که در داخل کلاینت ساخته و بعد رمزنگاری میشن و خروجی اون هارو بخونم، نتایج امید بخشی حاصل شد:
این کار قبل از هر چیز به من کمک کرد تا ترتیب مراحل ساخته شدن و رمزنگاری اجزای رکوئست هارو بفهمم، اما باید متوجه میشدم رکوئست ها قبل از انکریپت (رمزنگاری) شدن چه شکلی هستن و کلید و iv ساخته شده چه تفاوت هایی با رکوئست نهایی کلاینت به سرور های کافه بازار دارن. بعد از ده ها آزمایش مختلف و هوک کردن انواع و اقسام اینترنال های کافه بازار و کال های رمزنگاری، اولین موفقیت اساسی حاصل شد، رکوئست خام و مقدار انکریپت نشده پارامتر 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 کلیدی بوده که سرور پاسخش رو باهاش انکریپت میکرده و به کلاینت میفرستاده...
نتیجتا کافه بازار از سیستم بسیار عجیب و غریب چند مرحله ای رمزنگاری خودساخته اینچنینی استفاده میکرد:
با مشخص شدن فرمول ساخت یک رکوئست سالم، این فرایند در یک اسکریپت که نسخه اولیش رو در همون لینک گیت هاب اول پست قرار دادم، اتوماتیک شد و کل اپ های قابل دسترس کافه بازار به ویروس توتال جهت اسکن ارسال شد (که به دلیل محدودیت api ویروس توتال و خالی بودن جیب من ماه ها طول کشید).
این یکی از فوقالعاده ترین و سنگین ترین پروژه های من در اون زمان بود که بی اندازه باعث افزایش دانشم و کاهش غرورم شد. نتایج تحقیق در کنفرانس شبگرد ارائه شد که ظاهرا مورد اقبال قرار نگرفت به دلایلی...
امیدوارم این پست براتون مفید واقع شده باشه، اگر سوالی بود در کامنت ها یا توییتر من مطرح کنید، ممنون که مطالعه کردید ;)