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

ظهرت الإصلاحات الخاصة بالمشكلة الموصوفة هنا تحت CVE-2024-34740 / A-307288067:
داخل 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
تاريخيًا كانت هذه الملفات عبارة عن ملفات 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، وبعد ذلك ستتم كتابة محتويات المصفوفة الفعلية
لاستغلال هذا عدم التطابق، سنحتاج إلى اختيار ملف معين نتمكن من خلاله من حقن مصفوفة بايت تعسفية في attributeBytesBase64 أو attributeBytesHex، بالإضافة إلى أن تعديل ذلك الملف سيكون ذا قيمة للمهاجم
توفر فئة PackageInstaller إمكانية تجهيز حزمة للتثبيت. دون الحاجة إلى أي أذونات، يمكن لأي تطبيق كتابة APK جديد ليتم تثبيته في دليل مؤقت. بمجرد كتابة كل ما هو ضروري للتثبيت، يمكن للتطبيق المثبّت استدعاء commit() الخاص بـ PackageInstaller.Session، مما يعني أنه لن يتمكن من إجراء أي تغييرات إضافية على ملفات التثبيت، وتكون Session جاهزة إما لموافقة المستخدم أو للتثبيت الفعلي
يتم تخزين حالة هذه العمليات في /data/system/install_sessions.xml. يمكن لتطبيق المثبّت على سبيل المثال تنزيل نصف APK كبير إلى الدليل المؤقت الذي أنشأته خدمة إدارة الحزم لجلستها PackageInstaller.Session، ثم بعد إعادة التشغيل استئناف التنزيل وكتابة النصف المتبقي والالتزام بالتثبيت
أحد الاحتمالات هو كتابة بيانات في install_sessions.xml لتحديد الجلسة على أنها مؤجلة (staged)، مما يعني أنها ستُثبَّت بعد إعادة التشغيل التالية