
استغلال الثغرة CVE-2022-20452، تصعيد الامتيازات على Android من تطبيق مثبت إلى تطبيق النظام (أو تطبيق آخر) عبر LazyValue باستخدام Parcel بعد recycle()
يُقدّم Android 13 العديد من التحسينات بهدف تعزيز آلية تسلسل Parcel.
إليك عرض تقديمي من فريق أمان وخصوصية Android حول التحسينات التي تم إجراؤها
هذا رائع، فهو بالتأكيد يزيل العديد من الثغرات أو يجعلها غير قابلة للاستغلال.
كما يصفون كسر استغلالي السابق، الذي يسمح للتطبيقات بتحميل شفرتها إلى تطبيقات أخرى (بما في ذلك تطبيقات النظام)
لكنني عدت الآن باستغلال جديد يحقق نفس الشيء، وإن كان بطريقة مختلفة.
يعتمد على الثغرات التالية التي تم تقديمها أثناء تقوية Parcel المذكورة أعلاه:
![لقطة شاشة لتطبيق يعرض نصًا. Title: LeakValue. Main text: Created 6 ValueLeaker-s. Locking ActivityTaskManagerService. Locked ActivityTaskManagerService. Unlocking ActivityTaskManagerService. Unlocked ActivityTaskManagerService. leakedBinders=[android.os.BinderProxy@f06702e]. Leaked interface: android.app.IApplicationThread. Requesting code execution. Shellcode has been executed in uid=1000 pid=6904 packageName=com.android.settings uid=1000(system) gid=1000(system) groups=1000(system),1007(log),1065(reserved_disk),1077(external_storage),3001(net_bt_admin),3002(net_bt),3003(inet),3007(net_bw_acct),9997(everybody) context=u:r:system_app:s0. في أسفل الشاشة زران: START و MANUAL TESTING](Screenshot_20220723-081920.png)
(أيضًا logcat من تنفيذ التطبيق، الاستغلال صاخب في السجلات)
Parcel و Parcelableفئة Parcel في Android هي أساس التواصل بين العمليات
يمكن للكائنات تنفيذ واجهة Parcelable للسماح بكتابتها إلى Parcel، على سبيل المثال (منسوخ من AOSP):```java
public class UsbAccessory implements Parcelable {
public static final Parcelable.Creator CREATOR =
new Parcelable.Creator() {
public UsbAccessory createFromParcel(Parcel in) {
String manufacturer = in.readString();
String model = in.readString();
String description = in.readString();
String version = in.readString();
String uri = in.readString();
IUsbSerialReader serialNumberReader = IUsbSerialReader.Stub.asInterface(
in.readStrongBinder());
return new UsbAccessory(manufacturer, model, description, version, uri,
serialNumberReader);
}
};
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeString(mManufacturer);
parcel.writeString(mModel);
parcel.writeString(mDescription);
parcel.writeString(mVersion);
parcel.writeString(mUri);
parcel.writeStrongBinder(mSerialNumberReader.asBinder());
} }
لاحظ أن `Parcel` يخزن داخليًا الموضع الذي يتم فيه الكتابة أو القراءة، وأن `readString()` يحلل البيانات إلى `String` بالإضافة إلى تقدم الموضع. يمكن الحصول على هذا الموضع أو تعيينه يدويًا من خلال [`dataPosition()`](https://developer.android.com/reference/android/os/Parcel#dataPosition())/[`setDataPosition()`](https://developer.android.com/reference/android/os/Parcel#setDataPosition(int)). يجب على تطبيقات واجهة `Parcelable` التأكد من أن `writeToParcel` و `createFromParcel` يكتبان/يقرآن نفس مقدار البيانات، وإلا ستحصل جميع القراءات اللاحقة على بيانات من إزاحات خاطئة.
يمكن أن يحتوي [`Bundle`](https://developer.android.com/reference/android/os/Bundle) (خريطة مفتاح-قيمة يمكن إرسالها عبر العمليات) على [مجموعة متنوعة من الكائنات التي يمكن كتابتها إلى `Parcel` عبر `writeValue()`](https://cs.android.com/android/platform/superproject/+/android-12.1.0_r8:frameworks/base/core/java/android/os/Parcel.java;l=1792-1937). عند قراءة محتويات `Bundle` من `Parcel`، يمكن قراءة أي فئة `Parcelable` متاحة في النظام هناك.
يؤجل `Bundle` التحليل الفعلي للمحتويات عن طريق كتابة طول البيانات المجمعة بالكامل في `Parcel` ثم [نسخ الجزء ذي الصلة من `Parcel` الأصلي إلى `Parcel` ثانوي مخزّن في `mParcelledData`](https://cs.android.com/android/platform/superproject/+/android-12.1.0_r8:frameworks/base/core/java/android/os/BaseBundle.java;l=1675-1683) (وهذا يسمح على سبيل المثال لـ [`Activity.onSaveInstanceState()`](https://developer.android.com/reference/android/app/Activity#onSaveInstanceState(android.os.Bundle)) بتوفير كائنات `Parcelable` غير متوفرة في `system_server`، ويتم تمرير `Bundle` بالكامل إلى `system_server` وإعادته كما هو دون تحليل المحتويات).
ولكن بمجرد الوصول إلى أي قيمة في `Bundle`، [تم إلغاء تجميع](https://cs.android.com/android/platform/superproject/+/android-12.1.0_r8:frameworks/base/core/java/android/os/BaseBundle.java;l=227-313) جميع القيم داخل `Bundle` و [تم تحليل كل زوج مفتاح-قيمة موجود](https://cs.android.com/android/platform/superproject/+/android-12.1.0_r8:frameworks/base/core/java/android/os/Parcel.java;l=3613-3632). إذا احتوت هذه الخريطة على `Parcelable` لديه طرق `writeToParcel` و `createFromParcel` غير متوازنة، ثم تم إعادة توجيه هذا `Bundle` لاحقًا إلى عملية أخرى، فقد ترى تلك العملية الأخرى محتويات مختلفة من `Bundle`. وهذا جعل جميع [حالات عدم التطابق هذه في الفئات المتاحة في النظام ثغرات أمنية](https://github.com/michalbednarski/ReparcelBug) حيث توجد [أماكن في النظام يتم فيها فحص `Bundle` ليكون آمنًا](https://cs.android.com/android/platform/superproject/+/android-12.1.0_r8:frameworks/base/services/core/java/com/android/server/accounts/AccountManagerService.java;l=5037-5046) ثم يتم إعادة توجيهه إلى عملية أخرى.
في هذه المذكرة، أسمي مثل هذا `Bundle` الذي يظهر محتويات واحدة ثم أخرى بعد إعادة التوجيه بـ `Bundle` ذاتي التغيير.
شيء مهم آخر هنا هو أنه بالإضافة إلى البايتات فقط (سلاسل، أرقام، كائنات مكونة مما سبق)، يمكن أن يحتوي `Parcel` أيضًا على واصفات ملفات و `Binder`s. `Binder`s هي كائنات يمكن من خلالها إجراء استدعاء RPC، أي أن عملية تنشئ كائن `Binder` وتتجاوز [`onTransact()` method](https://developer.android.com/reference/android/os/Binder#onTransact(int,%20android.os.Parcel,%20android.os.Parcel,%20int)). ثم يتم تمرير `Binder` إلى عملية أخرى، في الكود المثال أعلاه يمكنك رؤية استدعاءات `read`/`writeStrongBinder()` المستخدمة لقراءته وكتابته إلى `Parcel`. في العملية الأخرى، عند استخدام `readStrongBinder()` يتم إنشاء كائن `BinderProxy` (مخفي خلف [واجهة `IBinder`](https://developer.android.com/reference/android/os/IBinder)). ثم يمكن لتلك العملية الأخرى استدعاء [`transact()`](https://developer.android.com/reference/android/os/IBinder#transact(int,%20android.os.Parcel,%20android.os.Parcel,%20int)) على ذلك الكائن وفي الكائن الأصلي سيتم تنفيذ `onTransact()`. عادةً، لا يقوم الشخص بكتابة `transact()`/`onTransact()` يدويًا بل [يستخدم AIDL بدلاً من ذلك](https://developer.android.com/guide/components/aidl).
# دخول `LazyValue`، نهاية `Bundle` ذاتي التغيير
نظرًا لوجود العديد من حالات الفئات ذات عدم التطابق في `writeToParcel`/`createFromParcel` في الماضي، يحل Android 13 مشكلة وجود أي فئة من هذا القبيل في أي مكان في النظام مما يسمح ببناء `Bundle` ذاتي التغيير من خلال [تقديم `LazyValue`](https://android.googlesource.com/platform/frameworks/base/+/9ca6a5e21a1987fd3800a899c1384b22d23b6dee%5E%21/)