
CVE-2024-34740 के लिए राइटअप और एक्सप्लॉइट — Android के BinaryXmlSerializer में इंटीजर ओवरफ्लो से system_server फ़ाइल राइट और फिर सामान्य इंस्टॉल किए गए ऐप से system_server कोड निष्पादन

यहाँ वर्णित समस्या के फिक्स CVE-2024-34740 / A-307288067 के अंतर्गत सामने आए:
Android system_server के अंदर, कई सेवाएँ रिबूट्स के बीच अपनी स्थिति को 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 में उस प्रारूप का नया बाइनरी संस्करण पेश किया गया, जिसमें कहा गया कि `system_server` द्वारा बिताए गए कुल समय का 1.5% इन 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 फ़ाइलों में से किसी को पढ़ता है, तो वह [Binary XML फ़ाइल के लिए पार्सर या नियमित XML पार्सर चुनने के लिए फ़ाइल में `"ABX\0"` मैजिक वैल्यू का उपयोग करता है](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/util/Xml.java;l=188-192;drc=97a370a95275e79c69e79d7ead11aa38934a5575)। जब भी इन फ़ाइलों को Binary 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) होता है और डिफ़ॉल्ट रूप से सक्षम होता है
जब Binary XML फ़ाइलें उपयोग में होती हैं, तो आप उनकी सामग्री पढ़ सकते हैं, उदाहरण के लिए `adb shell su 0 abx2xml /data/system/packages.xml -` के माध्यम से
इस बाइनरी प्रारूप की एक खासियत यह है कि यह टाइप किए गए एक्सेसर्स प्रदान करता है, इसलिए सीरियलाइज़र `attributeInt(String namespace, String name, int value)` विधि प्रदान करता है, जो मान को बाइनरी इंटीजर के रूप में लिखता है, जिससे String के माध्यम से राउंड-ट्रिप से बचा जा सकता है जो नया आवंटन और उसके बाद गारबेज कलेक्शन के लिए ऑब्जेक्ट होता
एक अन्य प्रकार जिसे सीधे सीरियलाइज़ किया जा सकता है वह बाइट ऐरे है```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 टूल द्वारा बाइट ऐरे को उपयुक्त String प्रस्तुति में बदलने के लिए किया जाता है।
mOut FastDataOutput का उदाहरण है, जो Java के 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 में संग्रहीत की जाती है। इंस्टॉलर ऐप उदाहरण के लिए अपने PackageInstaller.Session के लिए Package Manager Service द्वारा बनाई गई अस्थायी निर्देशिका में बड़े APK का आधा हिस्सा डाउनलोड कर सकता है, फिर रीबूट के बाद डाउनलोड फिर से शुरू कर सकता है, शेष आधा लिख सकता है और इंस्टॉलेशन commit कर सकता है।
एक संभावना install_sessions.xml में डेटा लिखना है ताकि सत्र को staged के रूप में चिह्नित किया जा सके, जिसका अर्थ है कि यह अगले बूट के बाद इंस्टॉल होगा।
दूसरी संभावना, जो यहाँ प्रस्तुत की गई है, अस्थायी निर्देशिका का पथ बदलना है जिसमें इंस्टॉलेशन फ़ाइलें तैयार की जाती हैं, क्योंकि openWrite()/openRead() किसी भी मान्य फ़ाइलनाम को स्वीकार करते हैं जब तक कि पथ ट्रैवर्सल न हो और उस फ़ाइल को stageDir फ़ील्ड द्वारा इंगित निर्देशिका में रखते हैं, जो XML से पढ़ा जाता है
अब हमें वास्तव में अपने नियंत्रित बाइट ऐरे को attributeBytesBase64() में पहुँचाना होगा।
PackageInstaller.Session setChecksums() विधि प्रदान करता है।
system_server की ओर, प्रदान किए गए Checksum-s को कॉलर द्वारा प्रदान किए गए हस्ताक्षर के विरुद्ध वैकल्पिक रूप से सत्यापित किया जाता है और फिर mChecksums में रखा जाता है
जब install_sessions.xml लिखा जाता है, checksum.getValue() को writeByteArrayAttribute में पारित किया जाता है, जो बदले में इसे attributeBytesBase64() में पारित करता है
कुछ ऐसी घटनाएँ हैं जो install_sessions.xml के लेखन को ट्रिगर करती हैं, जिनमें से एक नए Session का निर्माण है, इसलिए यह शोषण एक सत्र पर Checksum सेट करने के बाद यह सुनिश्चित करने के लिए नया Session बनाता है कि पहला Session फ़ाइल में सहेजा गया है।
अब हम 65536 लंबाई वाला बाइट ऐरे लिखते हैं, फिर जब इसे पढ़ा जाता है तो इसका आकार शून्य माना जाता है और उस ऐरे की सामग्री कच्चा डेटा बन जाती है जिसे BinaryXmlPullParser पार्स करता है।
कोई विशेषता गिनती निर्दिष्ट नहीं है, प्रत्येक प्रविष्टि में एक टैग बाइट होता है जिसमें token होता है। निचले निबल में XmlPullParser में परिभाषित इवेंट प्रकारों में से एक होता है, जैसे START_TAG, END_TAG या END_DOCUMENT। इन प्रकारों के अतिरिक्त, एक विशेष ATTRIBUTE प्रकार भी है, जिसे next() के माध्यम से रिपोर्ट नहीं किया जाता है बल्कि START_TAG टोकन देखने के बाद, पार्सर अगले टोकन तब तक देखता रहता है जब तक उसे गैर-ATTRIBUTE टोकन नहीं मिल जाता
चूंकि कोई विशेषता गिनती निर्दिष्ट नहीं है, हम END_TAG टोकन के माध्यम से तुरंत वर्तमान तत्व को बंद करने के लिए आगे बढ़ सकते हैं। फिर हम </session> भी बंद कर देते हैं क्योंकि सभी दिलचस्प तत्वों की विशेषताएँ <session> प्रारंभिक टैग में हैं लेकिन हम उस बिंदु से आगे हैं, हालाँकि अब हम नया <session> तत्व खोल सकते हैं और उन्हें वहाँ सेट कर सकते हैं।
जैसा कि ऊपर बताया गया है FastDataInput Java के DataInputStream के साथ संगत है, सिवाय एक अतिरिक्त readInternedUTF() विधि के, जो पिछले Strings को संदर्भित कर सकती है। चूंकि हम नहीं जानते कि पहले कौन से Strings interned किए गए थे, हम हमेशा निर्दिष्ट करते हैं कि पहले से अनदेखा String लिखा गया था। यह नए पढ़े गए strings को पूल में भी जोड़ता है, जो हमारे इंजेक्शन बिंदु के बाद लिखे गए डेटा को पढ़ने में समस्या पैदा कर सकता है, हालाँकि इंजेक्शन के भाग के रूप में मैं सभी समाप्ति टैग और END_DOCUMENT टोकन डालता हूँ, इसलिए मेरे इंजेक्शन के बाद उस फ़ाइल से कुछ और नहीं पढ़ा जाएगा।
stageDir के साथ PackageInstaller.Session का उपयोगएक बार जब सिस्टम संशोधित install_sessions.xml पढ़ लेता है, तो हमें PackageInstallerSession ऑब्जेक्ट मिलता है जिसमें stageDir हमारे नियंत्रित मान पर सेट होता है।
मेरा पहला विचार stageDir को /proc/self पर सेट करना था, फिर maps पढ़ना और mem लिखना, हालाँकि ये काम नहीं आए।
जब मैंने /proc/self/maps खोलने के लिए openRead() का उपयोग करने का प्रयास किया, तो system_server ने फ़ाइल को सफलतापूर्वक खोल लिया, हालाँकि उस फ़ाइल को Binder पर untrusted_app को भेजना SELinux द्वारा अवरुद्ध कर दिया गया था।
हालाँकि लेखन कच्चे फ़ाइल डिस्क्रिप्टर को किसी अन्य प्रक्रिया में भेजकर नहीं किया जाता है, बल्कि system_server के माध्यम से प्रॉक्सी किया जाता है, क्योंकि सत्र commit होने के बाद 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 फ़ाइल मौजूद नहीं होती है और हम stageDir को /data/system पर सेट करके गढ़े गए PackageInstallerSession का उपयोग करके एक बना सकते हैं।
साथ ही system_server को /data/system/packages.xml का केवल-पठनीय फ़ाइल डिस्क्रिप्टर भेजने की अनुमति है जब मैं openRead() का उपयोग करता हूँ, इसलिए मैं पिछली सामग्री को दूषित किए बिना केवल अपने संशोधनों वाली पैच की गई फ़ाइल आसानी से बना सकता हूँ।
sharedUserId="android.uid.system" पहुँच प्रदान करनाpackages.xml में मेरे पास इंस्टॉल किए गए एप्लिकेशन की परिभाषाएँ पंजीकृत हैं, जैसे:
<package name="com.android.settings" codePath="/system_ext/priv-app/Settings" ... sharedUserId="1000" ...>
<sigs count="1" schemeVersion="3">
<cert index="3" key="308204..." />
</sigs>
<proper-signing-keyset identifier="2" />
</package>
```
क्या हम `/data/app` में कहीं नया APK लिख सकते हैं (किसी अन्य `PackageInstallerSession` का उपयोग करके) और `packages.xml` में नया `<package>` तत्व जोड़ सकते हैं और इसे उस तरह इंस्टॉल करवा सकते हैं?
हाँ, हालाँकि हमें `<cert>` में अपने नए इंस्टॉल किए गए APK के मान्य हस्ताक्षर प्रदान करने होंगे और सिस्टम बूट के दौरान इसे APK फ़ाइल के विरुद्ध जाँचेगा
क्या हम `userId` विशेषता को अपनी इच्छित मान पर सेट कर सकते हैं (`sharedUserId` के बजाय, जो `AndroidManifest.xml` में `<manifest android:sharedUserId>` विशेषता के बिना APK को इंगित करता है)?
हाँ, हालाँकि हमें ऐसा मान उपयोग नहीं करना चाहिए जो पहले से किसी अन्य पैकेज या `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` से आते हैं, विशेष रूप से जब हमारे पास `<cert>` के साथ `<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) में नई प्रविष्टियाँ जोड़ने के लिए `<sigs>` के अंतर्गत `<pastSigs>` तत्व जोड़ सकते हैं
अंत में, हमारा छेड़छाड़ किया गया `<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>` के अंतर्गत दो बार डाला जाता है क्योंकि [अंतिम पिछला हस्ताक्षर वर्तमान माना जाता है और इसलिए उस पर विचार नहीं किया जाता](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/content/pm/SigningDetails.java;l=698-700;drc=97a370a95275e79c69e79d7ead11aa38934a5575)
[`flags="2"` का अर्थ है कि प्रमाणपत्र `sharedUserId` के लिए अनुमत है](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/content/pm/SigningDetails.java;l=102-103;drc=97a370a95275e79c69e79d7ead11aa38934a5575)
इसके अलावा, `<package sharedUserId="1000">` पंजीकरण उस ऐप पर लागू किया जाना चाहिए जो manifest में `android:sharedUserId="android.uid.system"` घोषित करता है, इसलिए यह उस APK से अलग होना चाहिए जो शोषण करता है
# नया इंस्टॉल किया गया system-uid ऐप शुरू होने में विफल रहता है
हालाँकि मैं `android:sharedUserId="android.uid.system"` के लिए विश्वसनीय नया प्रमाणपत्र पंजीकृत करने में सक्षम था, सामान्यतः उस प्रमाणपत्र के साथ हस्ताक्षरित और manifest में केवल `sharedUserId` घोषित करने वाला ऐप शुरू नहीं हो पाएगा। इसे लॉन्च करने पर हमें `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'
```
ऐसा इसलिए है क्योंकि [`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` श्रृंखला है:
* [`IAlarmManager.set()` AIDL विधि `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` बिना प्रकार तर्क के deprecated `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`, जो दिए गए `Parcel` पर `writeInt(0)` कॉल करता है](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/os/PooledStringWriter.java;l=55;drc=782d49826862cbdc9d020fc9d85f8a6f64675dcb) निर्दिष्ट करता हूँ
* वह `writeInt()` कॉल `Parcel` पर किया गया था जो [`onTransact()`](https://developer.android.com/reference/android/os/Binder#onTransact(int,%20android.os.Parcel,%20android.os.Parcel,%20int)) के `data` तर्क के रूप में प्राप्त हुआ था, जो `/dev/binder` से `mmap`-ed रीड-ओनली मेमोरी द्वारा समर्थित है। उस पर लिखने से `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) का उपयोग करके यूज़रस्पेस रीबूट से बचेगा और उसके बाद बैकग्राउंड में प्रतीक्षा करेगा
2. नया `PackageInstaller.Session` आवंटित किया जाता है और उसमें नया `Checksum` ऑब्जेक्ट जोड़ा जाता है। उस `Checksum` ऑब्जेक्ट में बाइट ऐरे होता है जिसका आकार सीरियलाइज़ेशन के दौरान इंटीजर ओवरफ्लो का कारण बनेगा और एक बार उसका डेटा वापस डी-सीरियलाइज़ हो जाने पर सिस्टम को ऐसे `PackageInstaller.Session`-s दिखेंगे जिनका डेटा पहले `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`-s का उपयोग किया जा सकता है
6. `RebootBackgroundRunner` यूज़रस्पेस रीबूट के दौरान बैकग्राउंड में प्रतीक्षा कर रहा होता है और जैसे ही उसे पता चलता है कि सिस्टम वापस चालू और तैयार है, यह अगले चरण करता है
7. एक `PackageInstaller.Session` का उपयोग करके, नया APK assets से निकाला जाता है और `/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` के भीतर चलता है क्योंकि `AndroidManifest.xml` में `<manifest android:sharedUserId="android.uid.system">` और `<application android:process="system">` है
# `utils/` में स्क्रिप्ट्स
PoC ऐप के साथ `utils` निर्देशिका भी है जिसमें कुछ स्क्रिप्ट्स हैं
* `moveapk.sh` संकलित APK को ड्रॉपर के `assets` में डालने के लिए ले जाता है, इसे `gradle :droppedapk:assembleRelease` के बाद चलाया जाना है
* `peeksessions.sh` `install_sessions.xml` की वर्तमान सामग्री देखने की अनुमति देता है (Android के `eng`/`userdebug` बिल्ड की आवश्यकता है)
* `wipesessions.sh` मौजूद किसी भी `PackageInstaller.Session`-s को हटाता है और सिस्टम को पुनरारंभ करता है (Android के `eng`/`userdebug` बिल्ड की आवश्यकता है)
# ट्रिविया
निश्चित नहीं है कि यह संबंधित है, लेकिन संभावित ABX-संबंधित बगों के इतिहास (`cd frameworks/base ; git log -S ABX`) को देखते हुए मुझे ["Stop processing on IOException" कमिट](https://android.googlesource.com/platform/frameworks/base/+/5112cfef2a2023a2629a426154547444593e9f9b%5E!/) मिला, जिसमें **ट्रंकेटेड ABX फ़ाइल के साथ यूनिट टेस्ट का जोड़ शामिल है**। वह कमिट ["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)