Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
ResourcePoison — Writeup and exploit for CVE-2025-22441: Privilege escalation from installed app to SystemUI process on Android due to pass of untrusted ApplicationInfo to LoadedApk | Kitploit
Tools/GitHubGitHub/michalbednarski/resourcepoison
Android SecurityPrivilege EscalationVulnerability AnalysisExploitationMobile SecurityPapers & Research
GitHubmichalbednarski/resourcepoison

ResourcePoison

Writeup and exploit for CVE-2025-22441: Privilege escalation from installed app to SystemUI process on Android due to pass of untrusted ApplicationInfo to LoadedApk

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share
View Repository
10829630 years agoReviewed by Kitploit

Fix for this issue has appeared as CVE-2025-22441: bulletin patch follow up

Passing ApplicationInfo around

ApplicationInfo is structure defining various information about installed app, most notably path to apk file from which resources and code are loaded

Usually it is passed from system to applications, however sometimes there are cases where non-system caller could provide own one. For example in the past that was vulnerability in bindBackupAgent() method, where attacker could pass in parameter own ApplicationInfo object with uid and sourceDir values and they weren't checked against what apps are really installed in system, because that method was meant to be called internally by system_server, but was exposed to adb shell

This time though, I've looked closely at ApplicationInfo field within RemoteViews

RemoteViews is object describing view that can come from another process. This most notably is used for home screen widgets, where app providing widget builds RemoteViews and then it is "applied" within home screen process

Other places where RemoteViews are used are notifications (applied by SystemUI process) and autofill dialogs (provided by autofill service, applied by system_server)

RemoteViews.mApplication field is serialized through Parcel and therefore may come from remote processes and whenever RemoteViews are applied it is used by following method (snippet source):

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;
}

Most interesting here is LoadedApk.checkAndUpdateApkPaths() call, as this is static method and will modify some global state (snippet source)

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);
}

Lets discuss what is going on in these methods

First we're calling 3-parameter checkAndUpdateApkPaths with cacheWithCode being set to both true and false. The LoadedApk object can be constructed in two modes, either with mIncludeCode being true or false, which SDK-wise maps to Context with CONTEXT_INCLUDE_CODE flag being set or not

That method will first use ActivityThread.peekPackageInfo(), which will return already cached LoadedApk instance, therefore if app didn't previously construct LoadedApk with matching packageName and includeCode, peekPackageInfo() will return null and checkAndUpdateApkPaths() won't do anything

Looking back at RemoteViews.getContextForResourcesEnsuringCorrectCachedApkPaths(), we have context.createApplicationContext(mApplication, Context.CONTEXT_RESTRICTED) there, the CONTEXT_INCLUDE_CODE flag was not specified (same is the case with contexts passed in argument to that method) and therefore that method will always using LoadedApk with mIncludeCode=false

Therefore, for RemoteViews updating version with code is unnecessary, as RemoteViews are always using Context without code. Commit message just says that there are two places where ApplicationInfo may be cached and I think removing updating version with code wouldn't cause issues here (as checkAndUpdateApkPaths() is only used by AppWidgetHostView and RemoteViews). Counterargument to that though is avoiding possibility of getting various caches out of sync and that in itself while removing call with cacheWithCode=true removes most severe impact of this bug, it doesn't completely fix issue as modification of just resources (instead of code) still might be valuable to attacker

LoadedApk.updateApplicationInfo()

Download Tool