
CVE-2025-22441 के लिए राइटअप और एक्सप्लॉइट: एंड्रॉइड पर इंस्टॉल किए गए ऐप से SystemUI प्रोसेस तक विशेषाधिकार वृद्धि, जो LoadedApk को अविश्वसनीय ApplicationInfo पास करने के कारण होती है
इस समस्या का समाधान CVE-2025-22441 के रूप में सामने आया है: बुलेटिन पैच अनुवर्ती
ApplicationInfo को इधर-उधर पास करनाApplicationInfo एक संरचना है जो इंस्टॉल किए गए ऐप के बारे में विभिन्न जानकारी परिभाषित करती है, विशेष रूप से apk फ़ाइल का पथ जिससे संसाधन और कोड लोड किए जाते हैं
आमतौर पर इसे सिस्टम से एप्लिकेशन तक पास किया जाता है, हालांकि कभी-कभी ऐसे मामले होते हैं जहां गैर-सिस्टम कॉलर अपना स्वयं का प्रदान कर सकता है। उदाहरण के लिए, अतीत में bindBackupAgent() विधि में यह भेद्यता थी, जहां हमलावर पैरामीटर में अपना स्वयं का ApplicationInfo ऑब्जेक्ट uid और sourceDir मानों के साथ पास कर सकता था और उन्हें सिस्टम में वास्तव में इंस्टॉल किए गए ऐप्स के विरुद्ध जांचा नहीं जाता था, क्योंकि उस विधि को system_server द्वारा आंतरिक रूप से कॉल किए जाने का इरादा था, लेकिन यह adb shell के लिए उजागर थी
इस बार, हालांकि, मैंने RemoteViews के भीतर ApplicationInfo फ़ील्ड को बारीकी से देखा
RemoteViews एक ऑब्जेक्ट है जो उस दृश्य का वर्णन करता है जो किसी अन्य प्रक्रिया से आ सकता है। यह विशेष रूप से होम स्क्रीन विजेट्स के लिए उपयोग किया जाता है, जहां विजेट प्रदान करने वाला ऐप RemoteViews बनाता है और फिर इसे होम स्क्रीन प्रक्रिया के भीतर "लागू" किया जाता है
अन्य स्थान जहां RemoteViews का उपयोग किया जाता है वे हैं सूचनाएं (SystemUI प्रक्रिया द्वारा लागू) और ऑटोफिल संवाद (ऑटोफिल सेवा द्वारा प्रदान किए गए, system_server द्वारा लागू)
RemoteViews.mApplication फ़ील्ड Parcel के माध्यम से क्रमबद्ध है और इसलिए दूरस्थ प्रक्रियाओं से आ सकता है और जब भी RemoteViews लागू होते हैं तो इसका उपयोग निम्नलिखित विधि द्वारा किया जाता है (स्निपेट स्रोत):```java
private Context getContextForResourcesEnsuringCorrectCachedApkPaths(Context context) {
if (mApplication != null) {
if (context.getUserId() == UserHandle.getUserId(mApplication.uid)
&& context.getPackageName().equals(mApplication.packageName)) {
return context;
}
try {
LoadedApk.checkAndUpdateApkPaths(mApplication);
return context.createApplicationContext(mApplication,
Context.CONTEXT_RESTRICTED);
} catch (NameNotFoundException e) {
Log.e(LOG_TAG, "Package name " + mApplication.packageName + " not found");
}
}
return context;
}
यहाँ सबसे दिलचस्प `LoadedApk.checkAndUpdateApkPaths()` कॉल है, क्योंकि यह एक स्टैटिक मेथड है और कुछ ग्लोबल स्टेट को संशोधित करेगा [(सोर्स स्निपेट)](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/app/LoadedApk.java;l=2275-2302;drc=5916ee589c4880e2d8a1a9ad6dc852108e4c44c1)```java
public static void checkAndUpdateApkPaths(ApplicationInfo expectedAppInfo) {
// Get the LoadedApk from the cache
ActivityThread activityThread = ActivityThread.currentActivityThread();
if (activityThread == null) {
Log.e(TAG, "Cannot find activity thread");
return;
}
checkAndUpdateApkPaths(activityThread, expectedAppInfo, /* cacheWithCode */ true);
checkAndUpdateApkPaths(activityThread, expectedAppInfo, /* cacheWithCode */ false);
}
private static void checkAndUpdateApkPaths(ActivityThread activityThread,
ApplicationInfo expectedAppInfo, boolean cacheWithCode) {
String expectedCodePath = expectedAppInfo.getCodePath();
LoadedApk loadedApk = activityThread.peekPackageInfo(
expectedAppInfo.packageName, /* includeCode= */ cacheWithCode);
// If there is load apk cached, or if the cache is valid, don't do anything.
if (loadedApk == null || loadedApk.getApplicationInfo() == null
|| loadedApk.getApplicationInfo().getCodePath().equals(expectedCodePath)) {
return;
}
// Duplicate framework logic
List<String> oldPaths = new ArrayList<>();
LoadedApk.makePaths(activityThread, expectedAppInfo, oldPaths);
// Force update the LoadedApk instance, which should update the reference in the cache
loadedApk.updateApplicationInfo(expectedAppInfo, oldPaths);
}
आइए इन मेथड्स में क्या हो रहा है, इस पर चर्चा करें
पहले हम 3-पैरामीटर वाले checkAndUpdateApkPaths को कॉल कर रहे हैं, जिसमें cacheWithCode को true और false दोनों पर सेट किया गया है। LoadedApk ऑब्जेक्ट को दो मोड में बनाया जा सकता है, या तो mIncludeCode के true या false होने के साथ, जो SDK के दृष्टिकोण से CONTEXT_INCLUDE_CODE फ्लैग के सेट होने या न होने के साथ Context से मेल खाता है
वह मेथड पहले ActivityThread.peekPackageInfo() का उपयोग करेगी, जो पहले से कैश किया गया LoadedApk इंस्टेंस लौटाएगी, इसलिए यदि ऐप ने पहले मेल खाते packageName और includeCode के साथ LoadedApk नहीं बनाया है, तो peekPackageInfo() null लौटाएगी और checkAndUpdateApkPaths() कुछ नहीं करेगी
RemoteViews.getContextForResourcesEnsuringCorrectCachedApkPaths() पर वापस देखते हुए, हमारे पास वहाँ context.createApplicationContext(mApplication, Context.CONTEXT_RESTRICTED) है, CONTEXT_INCLUDE_CODE फ्लैग निर्दिष्ट नहीं किया गया था (उस मेथड को तर्क में पारित किए गए कॉन्टेक्स्ट के मामले में भी ऐसा ही है) और इसलिए वह मेथड हमेशा mIncludeCode=false के साथ LoadedApk का उपयोग करेगी
इसलिए, RemoteViews के लिए कोड के साथ वर्जन अपडेट करना अनावश्यक है, क्योंकि RemoteViews हमेशा बिना कोड के Context का उपयोग करते हैं। कमिट संदेश केवल यह कहता है कि दो स्थान हैं जहाँ ApplicationInfo कैश किया जा सकता है और मुझे लगता है कि कोड के साथ वर्जन अपडेट करने को हटाने से यहाँ समस्याएँ नहीं होंगी (क्योंकि checkAndUpdateApkPaths() केवल AppWidgetHostView और RemoteViews द्वारा उपयोग किया जाता है)। हालाँकि इसका प्रतिवाद यह है कि विभिन्न कैश को सिंक से बाहर होने की संभावना से बचा जाए और जबकि cacheWithCode=true के साथ कॉल को हटाना इस बग के सबसे गंभीर प्रभाव को समाप्त करता है, यह समस्या को पूरी तरह से ठीक नहीं करता है क्योंकि केवल संसाधनों (कोड के बजाय) का संशोधन अभी भी हमलावर के लिए मूल्यवान हो सकता है
LoadedApk.updateApplicationInfo()