Skip to content
KitploitKITPLOIT
أدواتالمدونة
إرسال
أدواتالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

··الخلاصات·اتصال·الخصوصية·© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
AbxOverflow — تقرير واستغلال لثغرة CVE-2024-34740، فيض عدد صحيح في BinaryXmlSerializer في أندرويد يؤدي إلى كتابة ملف إلى system_server ومن ثم إلى تنفيذ كود داخل system_server من تطبيق مثبت عادي | Kitploit
أدوات/GitHubGitHub/michalbednarski/abxoverflow
أمان أندرويدتصعيد الامتيازاتتحليل الثغرات الأمنيةتحليل الكودالاستغلالالتعلم والتعليمتطوير الحمولاتاستغلال الملفات الثنائية
GitHubmichalbednarski/abxoverflow

AbxOverflow

تقرير واستغلال لثغرة CVE-2024-34740، فيض عدد صحيح في BinaryXmlSerializer في أندرويد يؤدي إلى كتابة ملف إلى system_server ومن ثم إلى تنفيذ كود داخل system_server من تطبيق مثبت عادي

عرض المستودع
6825منذ 10 أشهرتمت المراجعة من قبل Kitploit

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة

لقطة شاشة لتطبيق Android بعنوان AbxDroppedApk والكثير من النص الذي يصف أنه يعمل داخل system_server

ظهرت الإصلاحات الخاصة بالمشكلة الموصوفة هنا تحت CVE-2024-34740 / A-307288067:

  • النشرة
  • التصحيح المرتبط بالنشرة
  • تصحيحان آخران: 1 2

Android Binary XML

داخل system_server في Android، تخزّن العديد من الخدمات حالتها عبر عمليات إعادة التشغيل في ملفات XML``` $ adb shell su 0 find /data/system -name '*.xml' | sort /data/system/appops_accesses.xml /data/system/cachequota.xml /data/system/device_policies.xml /data/system/device_policy_state.xml /data/system/display-manager-state.xml /data/system/input-manager-state.xml /data/system/inputmethod/subtypes.xml /data/system/install_sessions.xml /data/system/job/jobs_1000.xml /data/system/job/jobs_10131.xml /data/system/log-files.xml /data/system/netpolicy.xml /data/system/notification_policy.xml /data/system/overlays.xml /data/system/packages.xml /data/system/package-watchdog.xml /data/system/sensor_privacy_impl.xml /data/system/sensor_privacy.xml /data/system/shortcut_service.xml /data/system/users/0/app_idle_stats.xml /data/system/users/0/appwidgets.xml /data/system/users/0/package-restrictions.xml /data/system/users/0/settings_global.xml /data/system/users/0/settings_secure.xml /data/system/users/0/settings_system.xml /data/system/users/0/wallpaper_info.xml /data/system/users/0.xml /data/system/users/userlist.xml /data/system/watchlist_settings.xml

root@kitploit:~
تاريخيًا كانت هذه الملفات عبارة عن ملفات XML نصية عادية ذات مسافات بادئة، مما سمح للمطورين بقراءتها بسهولة، ومع ذلك [في Android 12 تم تقديم نسخة ثنائية جديدة من هذا التنسيق، مستشهدين بأن 1.5% من إجمالي الوقت الذي يقضيه `system_server` كان يُنفق على عمليات XML هذه](https://android.googlesource.com/platform/frameworks/base/+/4ccea8796991d678ead4399130ec31edf63ff4fa%5E%21/)

وتجدر الإشارة إلى أن هذا التنسيق يُستخدم داخليًا فقط من قِبل النظام، وله ملفات ذات قيمة سحرية `"ABX\x00"`. وهو مختلف عن [التنسيق المستخدم داخل حزم APK](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/libs/androidfw/ResourceTypes.cpp;l=1770;drc=d4e49e63519397789d284a03aea5fafc119cb1b0) لـ `AndroidManifest.xml`، و`res/xml/*.xml`، و`res/layout/*.xml`، وما إلى ذلك، والذي لا يحتوي على "قيمة سحرية" صريحة، لكنه يبدأ عادةً بـ `0300 0800` (وهو [الترويسة](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/libs/androidfw/include/androidfw/ResourceTypes.h;l=608;drc=d4e49e63519397789d284a03aea5fafc119cb1b0) مع `type=RES_XML_TYPE` و`headerSize=8`)

عندما يقرأ النظام أحد ملفات الحالة الداخلية هذه بصيغة XML، فإنه [يستخدم القيمة السحرية `"ABX\0"` في الملف لاختيار إما محلل ملفات XML الثنائية أو محلل XML العادي](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/util/Xml.java;l=188-192;drc=97a370a95275e79c69e79d7ead11aa38934a5575). ومتى يتم حفظ هذه الملفات بصيغة XML الثنائية [يُتحكَّم فيه بواسطة خاصية النظام](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/util/Xml.java;drc=97a370a95275e79c69e79d7ead11aa38934a5575;l=74?q=Xml.java) وهو مفعّل افتراضيًا

عند استخدام ملفات XML الثنائية، يمكنك قراءة محتوياتها على سبيل المثال عبر `adb shell su 0 abx2xml /data/system/packages.xml -`

من الأمور التي يقوم بها هذا التنسيق الثنائي هو توفير موصِّلات ذات أنواع (typed accessors)، إذ يوفر المُسلسِل (serializer) دالة `attributeInt(String namespace, String name, int value)` التي تكتب القيمة كعدد صحيح ثنائي، متجنبًا الرحلة ذهابًا وإيابًا عبر `String` والتي كانت ستتطلب تخصيصًا جديدًا وكائنًا لاحقًا لجمع القمامة (Garbage Collection)

نوع آخر يمكن تسلسله مباشرةً هو مصفوفة البايتات```java
@Override
public XmlSerializer attributeBytesBase64(String namespace, String name, byte[] value)
        throws IOException {
    if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
    mOut.writeByte(ATTRIBUTE | TYPE_BYTES_BASE64);
    mOut.writeInternedUTF(name);
    mOut.writeShort(value.length);
    mOut.write(value);
    return this;
}

هناك أيضًا طريقة مشابهة تسمى attributeBytesHex ولا تختلف إلا في وسم TYPE_* الذي يُكتب. يُستخدم ذلك الوسم بواسطة أداة abx2xml لتحويل مصفوفة البايت إلى تمثيل نصي مناسب

mOut هو نسخة من FastDataOutput، والتي توفر دوال مشابهة لدالة DataOutputStream في جافا. تستخدم writeByte/writeShort/writeInt/writeUTF/write نفس التنسيق المستخدم في DataOutputStream القياسية

وبالمثل إلى Parcel، إذا حدث أي عدم تطابق أثناء الكتابة/القراءة، فسيتم قراءة البيانات اللاحقة من إزاحات خاطئة، لكن على عكس Parcel، فإن الأخطاء في استخدام BinaryXmlSerializer/BinaryXmlPullParser لا تمنح المهاجم القدرة على العبث ببيانات القراءة بشكل تعسفي (لا يمكن للمهاجم في هذه الحالة إدخال أسماء/قيم وسوم أو خصائص جديدة)

لكن الأخطاء داخل كلاس BinaryXmlSerializer نفسه أو في FastDataOutput تمنحه تلك القدرة

في الطريقة أعلاه إذا حاولنا كتابة مصفوفة بايت بطول 65536، فسنكتب الطول باستخدام writeShort()، والتي ستكتب فعليًا 0، وبعد ذلك ستتم كتابة محتويات المصفوفة الفعلية

اختيار هدف حقن ABX

لاستغلال هذا عدم التطابق، سنحتاج إلى اختيار ملف معين نتمكن من خلاله من حقن مصفوفة بايت تعسفية في attributeBytesBase64 أو attributeBytesHex، بالإضافة إلى أن تعديل ذلك الملف سيكون ذا قيمة للمهاجم

توفر فئة PackageInstaller إمكانية تجهيز حزمة للتثبيت. دون الحاجة إلى أي أذونات، يمكن لأي تطبيق كتابة APK جديد ليتم تثبيته في دليل مؤقت. بمجرد كتابة كل ما هو ضروري للتثبيت، يمكن للتطبيق المثبّت استدعاء commit() الخاص بـ PackageInstaller.Session، مما يعني أنه لن يتمكن من إجراء أي تغييرات إضافية على ملفات التثبيت، وتكون Session جاهزة إما لموافقة المستخدم أو للتثبيت الفعلي

يتم تخزين حالة هذه العمليات في /data/system/install_sessions.xml. يمكن لتطبيق المثبّت على سبيل المثال تنزيل نصف APK كبير إلى الدليل المؤقت الذي أنشأته خدمة إدارة الحزم لجلستها PackageInstaller.Session، ثم بعد إعادة التشغيل استئناف التنزيل وكتابة النصف المتبقي والالتزام بالتثبيت

أحد الاحتمالات هو كتابة بيانات في install_sessions.xml لتحديد الجلسة على أنها مؤجلة (staged)، مما يعني أنها ستُثبَّت بعد إعادة التشغيل التالية

احتمال آخر، معروض هنا، هو تغيير المسار إلى الدليل المؤقت الذي تُجهَّز فيه ملفات التثبيت، حيث أن openWrite()/openRead() تقبل أي اسم ملف صالح طالما لا يوجد اجتياز للمسار وتضع ذلك الملف في الدليل المشار إليه بواسطة حقل stageDir، والذي يُقرأ من XML

استغلال حقن ABX

الآن نحتاج فعليًا إلى إدخال مصفوفة البايت الخاضعة لسيطرتنا في attributeBytesBase64()

توفر PackageInstaller.Session طريقة setChecksums()

على جانب system_server، يتم التحقق اختياريًا من Checksum-s المقدمة مقابل التوقيع المقدم من المتصل ثم وضعها في mChecksums

عند كتابة install_sessions.xml، يتم تمرير checksum.getValue() إلى writeByteArrayAttribute، والتي بدورها تمرره إلى attributeBytesBase64()

هناك عدة أحداث تؤدي إلى كتابة install_sessions.xml، أحدها هو إنشاء Session جديدة، لذلك بعد تعيين Checksum في إحدى الجلسات، يقوم هذا الاستغلال بإنشاء Session جديدة لضمان حفظ الجلسة الأولى في الملف

الآن نكتب مصفوفة بايت بطول 65536، وبعد قراءتها يتم تفسير حجمها على أنه صفر وتصبح محتويات تلك المصفوفة بيانات خام يقوم BinaryXmlPullParser بتحليلها

لا يوجد عدد محدد للخصائص، كل إدخال يحتوي على بايت وسم يحتوي على token. في النصف السفلي (low nibble) يوجد أحد أنواع الأحداث المعرفة في XmlPullParser، مثل START_TAG أو END_TAG أو END_DOCUMENT. بالإضافة إلى هذه الأنواع، يوجد نوع خاص ATTRIBUTE، لا يتم الإبلاغ عنه عبر next() ولكن بدلاً من ذلك بعد رؤية رمز START_TAG، يطلع المحلل اللغوي على الرموز التالية حتى يرى رمزًا غير ATTRIBUTE

نظرًا لعدم وجود عدد محدد للخصائص، يمكننا المتابعة فورًا لإغلاق العنصر الحالي عبر رمز END_TAG. ثم نغلق أيضًا </session> لأن جميع خصائص العناصر المهمة موجودة في وسم افتتاح <session> ولكننا تجاوزنا تلك النقطة، ومع ذلك يمكننا الآن فتح عنصر <session> جديد وتعيينها هناك

كما ذُكر أعلاه، FastDataInput متوافق مع DataInputStream في جافا، باستثناء وجود طريقة إضافية readInternedUTF()، والتي يمكن أن تشير إلى سلاسل نصية سابقة. نظرًا لأننا لا نعرف السلاسل النصية التي تم إدخالها سابقًا في التجمع، فإننا نحدد دائمًا أن سلسلة نصية لم تُرَ من قبل قد كُتبت. يؤدي هذا أيضًا إلى إضافة السلاسل النصية المقروءة حديثًا إلى التجمع، مما قد يسبب مشكلة في قراءة البيانات المكتوبة بعد نقطة الحقن، ولكن كجزء من الحقن أقوم بإدراج جميع وسوم الإغلاق ورمز END_DOCUMENT، لذا بعد حقني لن يتم قراءة أي شيء آخر من ذلك الملف

استخدام PackageInstaller.Session مع stageDir معدل

بمجرد قراءة النظام لملف install_sessions.xml المعدل، نحصل على كائن PackageInstallerSession مع تعيين stageDir على قيمة نتحكم بها

كانت فكرتي الأولى هي تعيين stageDir إلى /proc/self، ثم قراءة maps وكتابة mem، لكن هذه لم تنجح

عندما حاولت استخدام openRead() لفتح /proc/self/maps، نجح system_server في فتح الملف، ولكن تمرير ذلك الملف إلى untrusted_app عبر Binder تم حظره بواسطة SELinux

لكن عمليات الكتابة لا تتم بتمرير واصف الملف الخام إلى عملية أخرى، بل تتم بواسطة وساطة عبر system_server، حيث يجب أن يكون system_server قادرًا على إلغاء صلاحية الكتابة بمجرد الالتزام بالجلسة. هل يعني ذلك أننا نستطيع الكتابة إلى /proc/self/mem؟ اتضح أنه بينما يمكن لـ system_server فتح هذا الملف، فإنه قبل كتابة أي شيء يستدعي Os.chmod() على هذا الملف، وهو ما لا يمكنه فعله على /proc/self/mem. لذا لا يمكننا استخدام ذلك للاستغلال هنا، على الرغم من أن system_server قادر فيما عدا ذلك على فتح هذا الملف وإجراء عمليات كتابة عند إزاحات نحددها نحن، وهذا الملف يسمح بالكتابة فوق صفحات الكود، مما يمنحنا تنفيذ الكود مباشرة

نظرًا لأن هذا الخيار غير متاح، جربت الفكرة التالية: استبدال محتويات /data/system/packages.xml. هذا هو الملف الذي يحتوي على حالة PackageManagerService، وأهم ما فيه هو التطبيقات المثبتة ومعرفات المستخدمين (uids) المخصصة لها

يبدو أن system_server غير مسموح له بالكتابة مباشرة إلى هذا الملف: بدلاً من ذلك، كلما كتب النظام هذا الملف، يكتب أولاً إلى ملف مؤقت ثم يستبدل packages.xml بذلك الملف المؤقت ويُفعّل الحماية عليه

ومع ذلك عند قراءة /data/system/packages.xml، سيقوم النظام أولاً بالتحقق مما إذا كان ملف /data/system/packages-backup.xml موجودًا، وإذا كان موجودًا فسيعتبر packages.xml الأساسي تالفًا وسيقرأ النسخة الاحتياطية بدلاً منه. أثناء التشغيل العادي، ملف /data/system/packages-backup.xml غير موجود ويمكننا إنشاؤه باستخدام PackageInstallerSession مصمم بعناية مع تعيين stageDir إلى /data/system

أيضًا، يُسمح لـ system_server بإرسال واصف ملف للقراءة فقط لملف /data/system/packages.xml عندما أستخدم openRead()، لذا يمكنني بسهولة إنشاء ملف مصحح يحتوي على تعديلاتي فقط دون إفساد المحتويات السابقة

منح وصول sharedUserId="android.uid.system"

في packages.xml لدي تعريفات التطبيقات المثبتة المسجلة، مثل:```xml <package name="com.android.settings" codePath="/system_ext/priv-app/Settings" ... sharedUserId="1000" ...>

root@kitploit:~
هل يمكننا كتابة APK جديدة في مكان ما داخل `/data/app` (باستخدام `PackageInstallerSession` آخر) وإضافة عنصر `<package>` جديد إلى `packages.xml` بحيث يتم تثبيتها بهذه الطريقة؟

نعم، ولكن يجب علينا توفير توقيع صحيح لملف APK المثبّت حديثًا داخل `<cert>`، وسيقوم النظام بالتحقق منه مقابل ملف APK أثناء الإقلاع.

هل يمكننا تعيين سمة `userId` (بدلاً من `sharedUserId` للإشارة إلى APK بدون سمة `<manifest android:sharedUserId>` في `AndroidManifest.xml`) إلى القيمة التي نريدها؟

نعم، ولكن يجب ألا نستخدم قيمة مستخدمة بالفعل من قبل حزمة أخرى أو `sharedUserId`.

هل يمكننا ضبط `sharedUserId="1000"` لتطبيقنا؟

إذا قمنا بذلك، فسيقوم النظام أثناء الإقلاع بالتحقق من هذا الإعداد عبر [`canJoinSharedUserId()`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java;l=658-750;drc=7ec13b04c3bbaeac99cbbc4db9f9f80492c508fe)

على وجه الخصوص، ستستخدم هذه الطريقة [`checkCapability()`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/content/pm/SigningDetails.java;l=613-637;drc=97a370a95275e79c69e79d7ead11aa38934a5575) للتحقق مما إذا كانت التوقيعات متطابقة تمامًا أو أن التوقيع على أحد الجانبين يطابق أحد التوقيعات السابقة على الجانب الآخر.

هذه "التوقيعات السابقة" تأتي من `packages.xml`، وتحديدًا عندما يكون لدينا عنصر `<sigs>` يحتوي على `<cert>`، يمكننا إضافة عنصر `<pastSigs>` تحت `<sigs>` لإضافة إدخالات جديدة إلى [`SigningDetails.mPastSigningCertificates`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/content/pm/SigningDetails.java;l=74-87;drc=97a370a95275e79c69e79d7ead11aa38934a5575)

في النهاية، سيبدو عنصر `<shared-user>` الذي تم العبث به هكذا:```xml
<shared-user name="android.uid.system" userId="1000">
  <sigs count="1" schemeVersion="3">
    <cert index="3" />
    <pastSigs count="2" schemeVersion="3">
      <cert index="19" flags="2" />
      <cert index="19" flags="2" />
    </pastSigs>
  </sigs>
</shared-user>

عنصر <cert> ضمن <pastSigs> يُدرج مرتين لأن التوقيع السابق الأخير يُعتبر توقيعًا حاليًا وبالتالي لا يُؤخذ في الاعتبار

flags="2" تعني أن الشهادة مسموح بها لـ sharedUserId

أيضًا، يجب تطبيق تسجيل <package sharedUserId="1000"> على التطبيق الذي يصرّح بـ android:sharedUserId="android.uid.system" في البيان (manifest)، لذلك يجب أن يكون APK منفصلاً عن التطبيق الذي ينفّذ الاستغلال.

التطبيق المثبّت حديثًا بمعرّف مستخدم النظام (system-uid) يفشل في البدء

بينما تمكّنت من تسجيل شهادة جديدة موثوقة لـ android:sharedUserId="android.uid.system"، فإن التطبيق الموقّع بتلك الشهادة والذي يصرّح فقط بـ sharedUserId في البيان (manifest) لن يكون قادرًا على البدء عادةً. عند تشغيله سنرى الرسالة التالية في logcat:``` signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr -------- Abort message: 'JNI FatalError called: (com.example.abxoverflow.droppedapk) frameworks/base/core/jni/com_android_internal_os_Zygote.cpp:1976: selinux_android_setcontext(1000, 0, "default:privapp:targetSdkVersion=33:complete", "com.example.abxoverflow.droppedapk") failed'

root@kitploit:~
هذا لأن أياً من التعريفات في ملف [`seapp_contexts`](https://cs.android.com/android/platform/superproject/main/+/main:system/sepolicy/private/seapp_contexts) لم يطابق

قاعدة `user=` في ذلك الملف هي [مُعرَّفة من `uid`](https://cs.android.com/android/platform/superproject/main/+/main:external/selinux/libselinux/src/android/android_seapp.c;l=819-833;drc=530165a996d8ca5ab5959c33bc040c78951bcb59) (الوسيط الأول لـ `selinux_android_setcontext()`)، وفي حالتنا ستكون `user=system`، وبالنسبة للتطبيقات العادية تكون `user=_app`

الأمر الآخر الذي يجب مطابقته هو قاعدة `seinfo=`، وهي مأخوذة من الوسيط الثالث لـ `selinux_android_setcontext()` حتى أول نقطتين. في الأصل تأتي تلك القيمة من مقارنة توقيع التطبيق المُطلق مع التوقيعات المعرّفة في `/system/etc/selinux/plat_mac_permissions.xml`

في النهاية يحاول تطبيقنا مطابقة `user=system seinfo=default` ولا يوجد مثل هذه القاعدة في `seapp_contexts`

ومع ذلك، بينما لا يمكن بدء عملية تطبيقنا الجديد الذي يملك `android:sharedUserId="android.uid.system"`، لا يزال بالإمكان تحميل التطبيق في عملية قائمة إذا تم تحديد ذلك عبر [خاصية `android:process`](https://developer.android.com/guide/topics/manifest/application-element#proc). على وجه الخصوص، يمكن للتطبيقات التي تعمل تحت `android.uid.system` تحديد `android:process="system"` ليتم تحميلها في `system_server`

# تعطيل النظام

بشكل عام [اعتبار التطبيق الذي يسبب تعطيل `system_server` خطأً بتأثير أمني مهمل](https://bughunters.google.com/learn/invalid-reports/android-platform/5148417640366080/bugs-with-negligible-security-impact#triggering-a-local-temporary-denial-of-service) وهنا لا يُذكر الأمر إلا لأنه جزء من سلسلة استغلال تتطلب إعادة تشغيل `system_server` مرتين

على أي حال، لدينا سلسلة `Parcelable`:

* [طريقة AIDL `IAlarmManager.set()` تقبل `AlarmManager.AlarmClockInfo`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl;l=32-35;drc=ca41ed611ac9c6584c6d5c38ae8428b8e4f3b135)
* [`AlarmClockInfo` يستدعي `readParcelable()` المُهمل بدون وسيط نوع](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/apex/jobscheduler/framework/java/android/app/AlarmManager.java;l=1598;drc=04bf84e220ade9d7ad8ef0b2f7e6ce6ec72841c8) (لأنه في وحدة apex وهذه لم تُحوَّل إلى الطرق الجديدة)
* أحدد [`android.content.pm.PackageParser$Activity`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/content/pm/PackageParser.java;l=8240;drc=7d3ffbae618e9e728644a96647ed709bf39ae759) كصف `Parcelable`
* قراءة ذلك تؤدي إلى [استدعاء أي مُنشئ عام يقبل وسيط `Parcel` واحد](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/content/pm/PackageParser.java;l=7789-7795;drc=7d3ffbae618e9e728644a96647ed709bf39ae759)
* أحدد [`android.os.PooledStringWriter`، والذي يستدعي `writeInt(0)` على `Parcel` المُمرَّر](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/os/PooledStringWriter.java;l=55;drc=782d49826862cbdc9d020fc9d85f8a6f64675dcb)
* تم استدعاء `writeInt()` هذا على `Parcel` المستلم كوسيط `data` في [`onTransact()`](https://developer.android.com/reference/android/os/Binder#onTransact(int,%20android.os.Parcel,%20android.os.Parcel,%20int))، وهو مدعوم بذاكرة للقراءة فقط معرّفة عبر `mmap` من `/dev/binder`. الكتابة إلى ذلك تسبب `SIGSEGV`

جدير بالذكر أيضاً أنني [استخدمت توليفة `PackageParser`+`PooledStringWriter` كجزء من تقارير سابقة، على سبيل المثال مع CVE-2023-21098](https://github.com/michalbednarski/TheLastBundleMismatch)

# التدفق الكامل

هذا ما يحدث بمجرد الضغط على زر "Do everything" داخل التطبيق

1. يتم تشغيل `RebootBackgroundRunner` كعملية منفصلة، والتي ستستخدم الآن فقط [`setsid()`](https://man7.org/linux/man-pages/man2/setsid.2.html) للبقاء بعد إعادة تشغيل userspace ومن ثم تنتظر في الخلفية
2. يتم تخصيص `PackageInstaller.Session` جديد ويُضاف إليه كائن `Checksum` جديد. يحتوي كائن `Checksum` هذا على مصفوفة بايتات بحجم سيسبب تجاوز عدد صحيح أثناء التسلسل، وبمجرد إلغاء تسلسل بياناتها سيرى النظام جلسات `PackageInstaller.Session` التي كانت بياناتها سابقاً حمولة `Checksum`. على وجه الخصوص، هناك جلستان محقونتان
    * واحدة بـ `sessionStageDir="/data/system"` و `prepared="true"` (بمعنى أن دليل المرحلة جاهز بالفعل ولا يحتاج إلى إنشاء)
    * واحدة بـ `sessionStageDir="/data/app/dropped_apk"` و `prepared="false"` (بمعنى أنه سيتم إنشاء الدليل عند أول `Session.openWrite()`)
3. يتم تخصيص `PackageInstaller.Session` جديد ثم تدميره فوراً. يؤدي هذا إلى قيام النظام بكتابة المحتويات المحدثة إلى `install_sessions.xml`
4. بعد تأخير بسيط، يتم تشغيل تعطل `system_server`
5. أثناء بدء تشغيل `system_server` التالي، تتم قراءة ملف `install_sessions.xml` والآن يمكن استخدام جلسات `PackageInstaller.Session` التي حقنّاها
6. كان `RebootBackgroundRunner` ينتظر في الخلفية أثناء إعادة تشغيل userspace وبمجرد أن يلاحظ أن النظام عاد وجاهز ينفذ الخطوات التالية
7. باستخدام `PackageInstaller.Session` واحد، يتم استخراج APK جديد من الأصول وكتابته في `/data/app/dropped_apk/base.apk`
8. تُستخدم الجلسة الأخرى لقراءة `/data/system/packages.xml`، ويتم تصحيح ذلك الملف ليدّعي أن APK المُسقط قد تم تثبيته بالفعل وأن الشهادة المستخدمة له كانت مستخدمة سابقاً لـ `android:sharedUserId="android.uid.system"` ولا تزال موثوقة لهذا الغرض. تتم كتابة الملف المعدَّل كـ `/data/system/packages-backup.xml`
9. يتم تشغيل تعطل آخر لـ `system_server`
10. عندما يرى `system_server` أثناء بدء التشغيل `packages-backup.xml`، يعتبر أن `packages.xml` الأصلي تالف ويستخدم النسخة الاحتياطية بدلاً منه
11. بما أن النظام قرأ `packages.xml` المعدَّل، فإن التطبيق المُسقط للتو موجود ويُطلق نفسه من [`ACTION_BOOT_COMPLETED`](https://developer.android.com/reference/android/content/Intent#ACTION_BOOT_COMPLETED). يعمل هذا التطبيق الجديد داخل `system_server` لأنه يملك `<manifest android:sharedUserId="android.uid.system">` و `<application android:process="system">` في `AndroidManifest.xml`

# السكربتات في `utils/`

إلى جانب تطبيق PoC يوجد دليل `utils` يحتوي على بعض السكربتات

* `moveapk.sh` ينقل APK المُجمَّع ليُسقط في أصول `assets` الخاصة بالـ dropper، ويُشغَّل بعد `gradle :droppedapk:assembleRelease`
* `peeksessions.sh` يتيح عرض المحتويات الحالية لـ `install_sessions.xml` (يتطلب بناءً من نوع `eng`/`userdebug` لنظام Android)
* `wipesessions.sh` يمسح أي جلسات `PackageInstaller.Session` موجودة ويعيد تشغيل النظام (يتطلب بناءً من نوع `eng`/`userdebug` لنظام Android)

# معلومات جانبية

لست متأكداً مما إذا كان هذا مرتبطاً، لكن بالنظر إلى التاريخ بحثاً عن أخطاء متعلقة بـ ABX (`cd frameworks/base ; git log -S ABX`) وجدت [commit باسم "Stop processing on IOException"](https://android.googlesource.com/platform/frameworks/base/+/5112cfef2a2023a2629a426154547444593e9f9b%5E!/)، والذي **يتضمن إضافة اختبار وحدة بملف ABX مقطوع**. كان ذلك الـ commit متابعةً لـ ["Ignore malformed shortcuts"](https://android.googlesource.com/platform/frameworks/base/+/d5122bfaf18f1503e73c1a3a177a56d0f604a008%5E%21/) والذي [تم وصفه في النشرة الأمنية كـ DoS](https://source.android.com/docs/security/bulletin/2022-12-01#framework)
تنزيل الأداة