مقدمة
في منتصف أبريل ،
نشرنا أخبارًا عن
Android.InfectionAds.1 Trojan ، والتي استغلت العديد من نقاط الضعف الحرجة في نظام التشغيل Android. واحد منهم - CVE-2017-13156 (المعروف أيضًا باسم
Janus ) - يسمح لبرنامج ضار بإصابة ملفات APK دون الإضرار بالتوقيع الرقمي.
الآخر هو CVE-2017-13315. إنه يعطي امتيازات طروادة المتقدمة ، ويمكنه تثبيت التطبيقات وإلغاء تثبيتها بشكل مستقل. يتوفر تحليل مفصل لنظام
Android.InfectionAds.1 في مكتبة الفيروسات الخاصة بنا ، ويمكن العثور عليه
هنا . سنناقش مدى الضعف CVE-2017-13315 بمزيد من التفصيل ونرى ما هو عليه.
ينتمي CVE-2017-13315 إلى مجموعة الثغرات الأمنية التي حصلت على الاسم العام EvilParcel. تم العثور عليها في فئات النظام المختلفة لنظام التشغيل أندرويد. بسبب أخطاء في الأخير عند تبادل البيانات بين التطبيقات والنظام ، يصبح من الممكن استبدال هذه البيانات. تتلقى البرامج الضارة التي تستغل نقاط الضعف في EvilParcel امتيازات أعلى ويمكنها القيام بما يلي بمساعدتها:
- تثبيت وإلغاء تثبيت التطبيقات مع أي أذونات دون تأكيد المستخدم ؛
- عند استخدامها مع نقاط الضعف الأخرى ، تصيب البرامج المثبتة على الجهاز واستبدال النسخ الأصلية "النظيفة" بنسخ مصابة ؛
- إعادة تعيين رمز قفل الشاشة لجهاز Android
- إعادة تعيين رقم التعريف الشخصي لشاشة قفل جهاز Android.
يوجد حاليًا 7 نقاط ضعف معروفة من هذا النوع:
- CVE-2017-0806 (خطأ في فئة GateKeeperResponse) ، نُشر في أكتوبر 2017 ؛
- CVE-2017-13286 (خطأ في الفصل OutputConfiguration ، نُشر في أبريل 2018 ؛
- CVE-2017-13287 (خطأ في فئة VerifyCredentialResponse) ، نُشر في أبريل 2018 ؛
- CVE-2017-13288 (خطأ في فئة PeriodicAdvertizingReport) ، نُشر في أبريل 2018 ؛
- CVE-2017-13289 (خطأ في فئة ParcelableRttResults) ، نُشر في أبريل 2018 ؛
- CVE-2017-13311 (خطأ في فئة SparseMappingTable) ، نُشر في مايو 2018 ؛
- CVE-2017-13315 (خطأ في فئة DcParamObject) ، نُشر في مايو 2018.
جميعها تهدد الأجهزة التي تعمل على إصدارات Android OS 5.0 - 8.1 التي لا تحتوي على تثبيت مايو 2018 والتحديثات الأمنية الأحدث.
المتطلبات الأساسية لضعف EvilParcel
دعونا نرى كيف تنشأ نقاط الضعف EvilParcel. بادئ ذي بدء ، سننظر في بعض ميزات عمل تطبيقات Android. في نظام التشغيل Android OS ، تتفاعل جميع البرامج مع بعضها البعض ، وكذلك مع نظام التشغيل نفسه ، عن طريق إرسال واستقبال كائنات من النوع Intent. يمكن أن تحتوي هذه الكائنات على عدد اعتباطي من أزواج قيمة المفتاح داخل كائن من النوع Bundle.
عند إرسال Intent ، يتم تحويل كائن Bundle (متسلسل) إلى صفيف بايت ملفوف في Parcel ، وعند قراءة المفاتيح والقيم من Bundle التسلسلي ، يتم إلغاء تسلسلها تلقائيًا.
في Bundle ، السلسلة هي المفتاح ، ويمكن أن تكون القيمة أي شيء تقريبًا. على سبيل المثال ، نوع بدائي أو سلسلة أو حاوية تحتوي على أنواع أو سلاسل بدائية. بالإضافة إلى ذلك ، يمكن أن يكون كائن من نوع الطرود.
وبالتالي ، في Bundle ، يمكنك وضع كائن من أي نوع يقوم بتنفيذ واجهة Parcelable. للقيام بذلك ، سوف تحتاج إلى تطبيق أساليب WriteToParcel () وإنشاءFromFromParcel () لتسلسل الكائن وإلغاء تسلسله.
كمثال جيد ، لنقم بإنشاء حزمة متسلسلة بسيطة. دعنا نكتب شفرة تضع ثلاثة أزواج ذات قيمة في الحزمة وتسلسلها:
عرض الحزمة = حزمة جديدة () ؛
demo.putString ("String" ، "Hello ، World!") ؛
demo.putInt ("Integer"، 42)؛
demo.putByteArray ("ByteArray" ، البايت الجديد [] {1 ، 2 ، 3 ، 4 ، 5 ، 6 ، 7 ، 8}) ؛
طرد لا يتجزأ = Parcel.obtain () ؛
parcel.writeBundle (تجريبي) ؛
بعد تنفيذ هذا الرمز ، نحصل على حزمة من النموذج التالي:
الشكل 1. هيكل كائن حزمة متسلسلة.
لنلاحظ الميزات التالية لتسلسل الحزمة:
- تتم كتابة جميع أزواج القيمة الرئيسية واحدة تلو الأخرى ؛
- قبل كل قيمة ، تتم الإشارة إلى نوعه (13 لصفيف بايت ، 1 لعدد صحيح ، 0 لسلسلة ، وهكذا) ؛
- قبل البيانات ذات الطول المتغير ، يشار إلى حجمها (طول السلسلة ، عدد البايت للصفيف) ؛
- تتم كتابة جميع القيم مع محاذاة 4 بايت.
نظرًا لحقيقة أن جميع المفاتيح والقيم في Bundle تتم كتابتها بالتسلسل ، عند الوصول إلى مفتاح أو قيمة كائن Bundle التسلسلي ، يتم إلغاء تسلسل الأخير بشكل كامل ، بما في ذلك تهيئة جميع كائنات Parcelable الموجودة فيه.
يبدو ، ما يمكن أن يكون مشكلة؟ وفي بعض فئات النظام التي تنفذ Parcelable ، قد تواجه أساليب createFromParcel () و writeToParcel () أخطاء. في هذه الفئات ، سيكون عدد البايتات المقروءة في أسلوب createFromParcel () مختلفًا عن عدد البايتات المكتوبة في أسلوب WriteToParcel (). إذا وضعت كائنًا من هذه الفئة داخل Bundle ، فستتغير حدود الكائن داخل Bundle بعد إعادة التسلسل. وهذا هو المكان الذي يتم فيه إنشاء شروط استغلال مشكلة عدم حصانة EvilParcel.
فيما يلي مثال لفئة بها خطأ مشابه:
class Demo implements Parcelable { byte[] data; public Demo() { this.data = new byte[0]; } protected Demo(Parcel in) { int length = in.readInt(); data = new byte[length]; if (length > 0) { in.readByteArray(data); } } public static final Creator<Demo> CREATOR = new Creator<Demo>() { @Override public Demo createFromParcel(Parcel in) { return new Demo(in); } }; @Override public void writeToParcel(Parcel parcel, int i) { parcel.writeInt(data.length); parcel.writeByteArray(data); } }
إذا كان حجم صفيف البيانات هو 0 ، فعند إنشاء كائن في createFromParcel () ستتم قراءة int (4 بايت) ، وسيتم كتابة اثنين int (8 بايت) في writeToParcel (). سيتم كتابة int الأول في مكالمة واضحة إلى writeInt. سيتم كتابة int الثاني عند استدعاء writeByteArray () ، لأن طول المصفوفة يتم كتابته دائمًا إلى Parcel قبله (انظر الشكل 1).
الحالات التي يكون فيها حجم صفيف البيانات 0 نادرًا. ولكن حتى عند حدوث ذلك ، يستمر البرنامج في العمل إذا تم إرسال كائن واحد فقط في شكل متسلسل في وقت واحد (في المثال الخاص بنا ، كائن العرض التوضيحي). لذلك ، مثل هذه الأخطاء ، كقاعدة عامة ، تمر مرور الكرام.
الآن دعونا نحاول وضع كائن Demo بطول صفيف صفري في Bundle:
الشكل 2. نتيجة إضافة كائن تجريبي بطول صفيف صفري إلى الحزمة.
نحن تسلسل الكائن:
الشكل 3. كائن حزمة بعد التسلسل.
دعنا نحاول إلغاء تسلسلها:
الشكل 4. بعد إلغاء تسلسل الكائن حزمة.
ما هي النتيجة؟ النظر في مقتطف لا يتجزأ:
الشكل 5. هيكل الطرود بعد إلغاء تسلسل الحزمة.
من الشكلين 4 و 5 ، نرى أنه خلال عملية إلغاء التسلسل ، تمت قراءة int في طريقة createFromParcel بدلاً من اثنين مكتوبين سابقًا. لذلك ، لم يتم قراءة جميع القيم اللاحقة من Bundle بشكل صحيح. تمت قراءة القيمة 0x0 في العنوان 0x60 على أنها طول المفتاح التالي. وقراءة القيمة 0x1 في العنوان 0x64 كمفتاح. في هذه الحالة ، تمت قراءة القيمة 0x31 في العنوان 0x68 كنوع القيمة. لا توجد قيم في الطرود التي يكون نوعها 0x31 ، لذا فإن readFromParcel () أبلغ عن خطأ (استثناء) بأمانة.
كيف يمكن استخدام هذا في الممارسة وتصبح نقطة ضعف؟ لنرى! يسمح لك الخطأ الموصوف أعلاه في فئات نظام Parcelable ببناء Bundle ، والتي قد تختلف خلال التخفيضات الأولى والمتكررة. لشرح ذلك ، قم بتعديل المثال السابق:
Parcel data = Parcel.obtain(); data.writeInt(3);
ينشئ هذا الرمز حزمة متسلسلة تحتوي على فئة مستضعفة. دعونا نلقي نظرة على نتيجة تنفيذ هذا الرمز:
الشكل 6. إنشاء حزمة مع فئة الضعيفة.
بعد إلغاء التسلسل الأول ، ستحتوي هذه الحزمة على المفاتيح التالية:
الشكل 7. نتيجة إلغاء تسلسل حزمة مع فئة ضعيفة.
الآن قم بتسلسل الحزمة الناتجة مرة أخرى ، ثم قم بإلغاء تسلسلها مرة أخرى وإلقاء نظرة على قائمة المفاتيح:
الشكل 8. نتيجة لإعادة التسلسل وإلغاء تسلسل حزمة مع فئة ضعيفة.
ماذا نرى؟ يظهر المفتاح المخفي (مع قيمة السلسلة "Hi there!") في Bundle ، التي لم تكن موجودة من قبل. فكر في مقتطف Parcel من هذه الباقة لفهم سبب حدوث ذلك:
الشكل 9. هيكل الطرود للكائن Bundle مع الفئة الضعيفة بعد دورتين من التسلسل التسلسل.
هنا يصبح جوهر نقاط الضعف EvilParcel أكثر وضوحًا. من الممكن إنشاء حزمة مكونة خصيصًا تحتوي على فئة ضعيفة. يتيح لك تغيير حدود هذه الفئة وضع أي كائن في هذه الحزمة - على سبيل المثال ، النية ، والتي لن تظهر في الباقة إلا بعد إلغاء التسلسل الثاني. هذا سيجعل من الممكن إخفاء القصد من آليات الحماية لنظام التشغيل.
عملية EvilParcel
Android.InfectionAds.1 باستخدام CVE-2017-13315 تثبيت البرامج وإلغاء تثبيتها من تلقاء نفسها دون تدخل مالك الجهاز المصاب. ولكن كيف الحال؟
في عام 2013 ، تم اكتشاف الخطأ
7699048 أيضًا ، والمعروف أيضًا باسم Launch AnyWhere. لقد سمح لأي تطبيق تابع لجهة أخرى بتشغيل أنشطة عشوائية نيابة عن نظام المستخدم الأكثر امتيازًا. يوضح الرسم البياني أدناه آلية عمله:
الشكل 10. مخطط الخطأ 7699048.
باستخدام مشكلة عدم الحصانة هذه ، يمكن لتطبيق الاستغلال تطبيق خدمة AccountAuthenticator ، المصممة لإضافة حسابات جديدة إلى نظام التشغيل. بفضل الخطأ 7699048 ، يمكن للاستغلال تشغيل النشاط لتثبيت التطبيقات وإلغاء تثبيتها واستبدالها وإعادة تعيين PIN أو Pattern Lock والقيام بأشياء غير سارة أخرى.
لقد أصلحت Google هذه الفجوة عن طريق حظر إطلاق نشاط تعسفي من AccountManager. الآن AccountManager يسمح فقط إطلاق الأنشطة القادمة من نفس التطبيق. للقيام بذلك ، يقوم بفحص ومقارنة التوقيع الرقمي للبرنامج الذي بدأ بداية النشاط بتوقيع التطبيق الذي يوجد به النشاط الذي تم إطلاقه. يبدو مثل هذا:
if (result != null && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) { int authenticatorUid = Binder.getCallingUid(); long bid = Binder.clearCallingIdentity(); try { PackageManager pm = mContext.getPackageManager(); ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId); int targetUid = resolveInfo.activityInfo.applicationInfo.uid; if (PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authenticatorUid, targetUid)) { throw new SecurityException( "Activity to be started with KEY_INTENT must " + "share Authenticator's signatures"); } } finally { Binder.restoreCallingIdentity(bid); } }
يبدو أن المشكلة قد تم حلها ، ولكن ليس كل شيء هنا سلس. اتضح أنه يمكن التحايل على هذا الإصلاح باستخدام الثغرة الأمنية المعروفة EvilParcel CVE-2017-13315! كما نعلم بالفعل ، بعد إصلاح Launch AnyWhere ، يتحقق النظام من التوقيع الرقمي للتطبيق. إذا نجح هذا الاختبار ، يتم تمرير الحزمة إلى IAccountManagerResponse.onResult (). في الوقت نفسه ، يتم استدعاء onResult () من خلال آلية IPC ، لذلك يتم إجراء تسلسل Bundle مرة أخرى. في تطبيق onResult () ، يحدث ما يلي:
private class Response extends IAccountManagerResponse.Stub { public void onResult(Bundle bundle) { Intent intent = bundle.getParcelable(KEY_INTENT); if (intent != null && mActivity != null) {
بعد ذلك ، يتم استخراج الحزمة من مفتاح النوايا ويتم إطلاق النشاط بدون شيكات. نتيجة لذلك ، لبدء نشاط تعسفي مع حقوق النظام ، يكفي إنشاء Bundle بحيث يتم إخفاء حقل النوايا عند إلغاء التسلسل الأول ، ويظهر في عملية إلغاء التسلسل الثانية. وكما رأينا بالفعل ، فإن هذه الثغرات هي بالضبط تلك المهمة التي تفي بها نقاط الضعف في EvilParcel.
في الوقت الحالي ، يتم إصلاح جميع الثغرات المعروفة من هذا النوع عن طريق إصلاحات في فئات Parcelable الضعيفة نفسها. ومع ذلك ، لا يمكن استبعاد ظهور الطبقات الضعيفة في المستقبل. لا يزال تطبيق الحزمة وآلية إضافة حسابات جديدة كما كانت من قبل. لا يزالون يسمحون لك بإنشاء نفس الاستغلال بالضبط عندما تكتشف (أو جديدة) فئات Parcelable الضعيفة. علاوة على ذلك ، لا يزال تنفيذ هذه الفئات يتم يدويًا ، ويجب أن يراقب المبرمج الطول الثابت للكائن المتسلسل Parcelable. وهذا عامل بشري مع كل العواقب. ومع ذلك ، نأمل أن تكون هذه الأخطاء قليلة قدر الإمكان ، وأن ثغرات EvilParcel لن تزعج مستخدمي أجهزة Android.
يمكنك التحقق من جهازك المحمول بحثًا عن ثغرات EvilParcel باستخدام برنامج مكافحة الفيروسات
Dr.Web Security Space . سيقوم "مدقق الأمن" المدمج في الإبلاغ عن المشكلات المحددة وتقديم توصيات لحلها.