Skip to content
KitploitKITPLOIT
도구익스플로잇블로그
Log in
제출
도구익스플로잇블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

피드문의개인정보© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
ResourcePoison — CVE-2025-22441에 대한 분석 및 익스플로잇: Android에서 신뢰할 수 없는 ApplicationInfo가 LoadedApk로 전달되어 설치된 앱에서 SystemUI 프로세스로 권한 상승이 발생함 | Kitploit
도구/GitHubGitHub/michalbednarski/resourcepoison
Android SecurityPrivilege EscalationVulnerability AnalysisExploitationMobile SecurityPapers & Research
GitHubmichalbednarski/resourcepoison

ResourcePoison

CVE-2025-22441에 대한 분석 및 익스플로잇: Android에서 신뢰할 수 없는 ApplicationInfo가 LoadedApk로 전달되어 설치된 앱에서 SystemUI 프로세스로 권한 상승이 발생함

저장소 보기
10829630년 전Kitploit 검토 완료

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

이 문제에 대한 수정 사항이 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);
}

이 메서드들에서 무슨 일이 일어나고 있는지 논의해 보겠습니다.

먼저 cacheWithCode를 true와 false 모두로 설정하면서 3-파라미터 checkAndUpdateApkPaths를 호출하고 있습니다. 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는 항상 코드가 없는 Context를 사용하므로, RemoteViews에 대해 코드와 함께 버전을 업데이트하는 것은 불필요합니다. 커밋 메시지에는 ApplicationInfo가 캐시될 수 있는 두 곳이 있다고만 언급되어 있으며, 코드와 함께 버전을 업데이트하는 것을 제거해도 여기서는 문제가 발생하지 않을 것이라고 생각합니다(checkAndUpdateApkPaths()는 AppWidgetHostView와 RemoteViews에서만 사용되기 때문입니다). 그러나 이에 대한 반론은 다양한 캐시가 서로 동기화되지 않을 가능성을 피하는 것이며, cacheWithCode=true로 호출을 제거하면 이 버그의 가장 심각한 영향을 제거할 수 있지만, 코드 대신 리소스만 수정하는 것이 여전히 공격자에게 가치가 있을 수 있으므로 문제를 완전히 해결하지는 못한다는 점입니다.

LoadedApk.updateApplicationInfo()

지금까지 제시된 코드는 RemoteViews(위젯, 알림 등)에만 사용되지만, 이제 새 스플릿이 설치된 후 실행 중인 앱 프로세스를 업데이트하는 데에도 사용되는 LoadedApk.updateApplicationInfo()를 살펴보겠습니다(예: Play Feature Delivery 주문형 전달 사용 시).

도구 다운로드