Skip to content
KitploitKITPLOIT
工具漏洞利用博客
Log in
提交
工具漏洞利用博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

订阅源联系隐私© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
ResourcePoison — CVE-2025-22441 的 Writeup 与漏洞利用:Android 上因将不受信任的 ApplicationInfo 传递给 LoadedApk,导致从已安装应用提权至 SystemUI 进程 | Kitploit
工具/GitHubGitHub/michalbednarski/resourcepoison
Android安全权限提升漏洞分析漏洞利用移动安全论文与研究
GitHubmichalbednarski/resourcepoison

ResourcePoison

CVE-2025-22441 的 Writeup 与漏洞利用:Android 上因将不受信任的 ApplicationInfo 传递给 LoadedApk,导致从已安装应用提权至 SystemUI 进程

查看仓库
10829630年前Kitploit 审核通过

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

此问题的修复已作为 CVE-2025-22441 发布:公告 补丁 后续修复

传递 ApplicationInfo

ApplicationInfo 是定义已安装应用各种信息(最显著的是加载资源和代码的 apk 文件路径)的结构体

通常它由系统传递给应用,但有时也存在非系统调用方可以提供自己的 ApplicationInfo 的情况。例如,过去 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);
}

让我们来讨论一下这些方法中发生了什么

首先,我们调用三参数的 checkAndUpdateApkPaths,其中 cacheWithCode 分别被设置为 true 和 false。LoadedApk 对象可以以两种模式构造,要么 mIncludeCode 为 true,要么为 false,在 SDK 层面这对应着 Context 是否设置了 CONTEXT_INCLUDE_CODE 标志

该方法首先会使用 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()

到目前为止展示的代码仅用于 RemoteViews(小部件、通知等),但现在我们将进入 LoadedApk.updateApplicationInfo(),该方法也用于在安装新的 split 之后更新正在运行的应用进程(例如在使用 Play Feature Delivery 按需交付时)

现在,来自 RemoteViews 的不可信 ApplicationInfo 对象将被传递给 updateApplicationInfo(),让我们看看该方法做了什么 (代码片段来源)```java public void updateApplicationInfo(@NonNull ApplicationInfo aInfo, @Nullable List oldPaths) { if (!setApplicationInfo(aInfo)) { return; }

我们来看一下这个 `setApplicationInfo()` [(代码片段来源)](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/app/LoadedApk.java;l=392-422;drc=5916ee589c4880e2d8a1a9ad6dc852108e4c44c1)```java
private boolean setApplicationInfo(ApplicationInfo aInfo) {
    if (mApplicationInfo != null && mApplicationInfo.createTimestamp > aInfo.createTimestamp) {
        Slog.w(TAG, "New application info for package " + aInfo.packageName
                + " is out of date with TS " + aInfo.createTimestamp + " < the current TS "
                + mApplicationInfo.createTimestamp);
        return false;
    }
    // Snip: assign fields such as mAppDir and mResDir on this object from aInfo
    return true;
}

createTimestamp 字段通常被设置为 SystemClock.uptimeMillis(),但由于该对象来自攻击者,这意味着攻击者可以提供未来的值,从而阻止后续的 updateApplicationInfo() 调用执行

下载工具