Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

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

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

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

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
LSPromise — Android 완전한 익스플로잇 체인으로, 로컬의 신뢰할 수 없는 앱에서 루트/커널로 권한 상승을 가능하게 하며, CVE-2026-49881과 CVE-2026-43284를 결합한 것입니다. | Kitploit
도구/GitHubGitHub/lsposed/lspromise
Android SecurityPrivilege EscalationExploit FrameworksVulnerability AnalysisExploitationPost-ExploitationPayload Development
GitHublsposed/lspromise

LSPromise

Android 완전한 익스플로잇 체인으로, 로컬의 신뢰할 수 없는 앱에서 루트/커널로 권한 상승을 가능하게 하며, CVE-2026-49881과 CVE-2026-43284를 결합한 것입니다.

저장소 보기
51913시간 37분 전Kitploit 검토 완료

인기

모두 보기 →

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

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

LSPromise

로컬의 신뢰할 수 없는 앱에서 root/커널로 권한 상승을 가능하게 하는 완전한 익스플로잇 체인입니다. 메모리 손상이나 경쟁 조건을 포함하지 않으므로, 공격자는 KASLR, MTE, CFI와 같은 메모리 손상 취약점에 대한 복잡한 힙 스프레이를 수행하거나 완화 조치를 우회할 필요가 없으며, 이 익스플로잇 체인은 취약한 기기에서 100% 성공률을 보장합니다.

초기 Android 17 공식 릴리스를 실행하는 Pixel 10에서 테스트되었습니다. Pixel 6a에서는 작동하지 않으며, 이 문제는 6.1.xxx-android14 커널 트리를 실행하는 다른 기기에서도 해당 커널의 또 다른 버그로 인해 발생할 수 있습니다.

사용법: KernelSU 앱을 설치하고, 이 앱을 연 다음 "Run userspace exploit"을 클릭하고 "Run kernel exploit and load KernelSU"를 클릭하세요. 성공적인 익스플로잇 후 KernelSU가 활성화되며, 이를 사용하여 다른 앱에 root 액세스 권한을 부여할 수 있습니다. 알려진 문제: 커널 익스플로잇을 이미 실행한 후 다시 실행하려면 기기를 재부팅해야 합니다.

화면 녹화: 여기를 클릭

분석 문서

이 체인은 두 가지 별개의 취약점으로 구성됩니다. 하나는 Telecom 서비스의 0-day이고, 다른 하나는 3개월 전에 공개된 커널 1-day입니다. 그러나 AOSP 및 Pixel 기기(베타 QPR 버전을 실행하는 기기 제외)는 작성 시점 현재 여전히 취약합니다.

system_server 진입

이 체인의 첫 번째 취약점은 Android 17에서 도입된 단순한 로직 버그입니다. 이는 InCallController.java에 다음 코드를 추가하는 말도 안 되는 변경에서 비롯됩니다:

root@kitploit:~
        PackageManager packageManager = mContext.getPackageManager();
        Context userContext = mContext.createContextAsUser(userHandle,
                0 /* flags */);
        PackageManager userPackageManager = userContext != null ?
                userContext.getPackageManager() : packageManager;

        List<ResolveInfo> entries;
        entries = userPackageManager.queryIntentServices(
                serviceIntent,
                PackageManager.GET_META_DATA | PackageManager.MATCH_DISABLED_COMPONENTS);
        for (ResolveInfo entry : entries) {
            ServiceInfo serviceInfo = entry.serviceInfo;

            if (serviceInfo != null) {
                boolean isMetaFlag = serviceInfo.metaData != null &&
                        serviceInfo.metaData.getBoolean(
                                "android.telecom.CLASS_EXISTENCE_CHECK", false);
                if (isMetaFlag && !serviceClassExists(serviceInfo, userHandle)) {
                    continue;
                }
            }
        }

관련된 serviceClassExists() 메서드는 다음과 같이 정의됩니다:

root@kitploit:~
    /**
     * Verifies that the class for a given ServiceInfo exists within its package.
     * This prevents a system crash if a service is declared in the manifest but its
     * class was not included in the compiled code.
     * @param serviceInfo The ServiceInfo of the service to check.
     * @param userHandle The user under which to check for the service.
     * @return {@code true} if the class exists, {@code false} otherwise.
     */
    private boolean serviceClassExists(ServiceInfo serviceInfo, UserHandle userHandle) {
        Log.i(this, "serviceClassExists check");
        try {
            Context packageContext = mContext.createPackageContextAsUser(
                    serviceInfo.packageName,
                    Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY, userHandle);
            ClassLoader classLoader = packageContext.getClassLoader();
            Class.forName(serviceInfo.name, false, classLoader);
            return true;
        } catch (NameNotFoundException | ClassNotFoundException e) {
            Log.w(this, "Skipping InCallService: class not found for " + serviceInfo.name);
            return false;
        } catch (Exception e) {
            Log.e(this, e, "Error checking for existence of " + serviceInfo.name);
            return false;
        }
    }

이것은 제가 본 것 중 가장 믿기 어려운 취약점입니다. 이 코드는 Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY를 사용하여 임의의 앱에서 코드를 로드합니다. 클래스 초기화를 방지하기 위해 Class.forName()에 false를 전달하는 등 임의 코드 실행을 방지하기 위한 일부 조치가 있는 것처럼 보이지만, 앱은 getClassLoader()가 호출될 때 호출되는 사용자 정의 AppComponentFactory를 선언할 수 있습니다.

한편, 이 버그는 com.android.phone이 아닌 com.android.server.telecom 패키지의 일부인 InCallController.java에 존재합니다. 이 패키지는 AndroidManifest.xml에서 android:sharedUserId="android.uid.system" 및 android:process="system"을 선언하므로 system_server 프로세스에서 실행된다는 점에 주목할 가치가 있습니다. system_server는 Android에서 가장 권한이 높은 사용자 공간 프로세스 중 하나입니다. 따라서 이제 우리는 system_server 내부에서 임의의 Java 코드를 실행할 수 있는 능력을 갖게 되었습니다.

AI 시대에 Google 엔지니어조차 이렇게 큰 실수를 할 수 있다는 것이 놀랍습니다. 우리는 2026년 7월 23일에 이를 발견하여 Android 보안 팀에 보고했습니다. 그들은 중복 신고라고 말했습니다. Google은 월간 보안 게시판을 분기별 릴리스로 전환했으며, 이는 Android 17 출시 3개월 후에도 취약점이 수정되지 않은 이유를 설명할 수 있습니다.

이 취약점은 CVE-2026-49881로 지정되었으며, 2026년 9월에 보안 취약점 해결을 위한 serviceClassExists 로직 제거를 통해 수정되었습니다.

네트워크 스택 진입

첫 번째 버그를 통해 system으로 권한을 상승시킬 수 있지만, 여전히 root와는 거리가 멉니다. 완전한 root 권한을 얻으려면 최소한 UID 0이 필요하고 SELinux의 제한을 받지 않아야 합니다.

이제 커널 1-day를 소개합니다: DirtyFrag 취약점입니다. 기본 원리는 여기서 자세히 설명하지 않겠습니다. 원래 보고자의 분석 문서를 참조하세요. 두 가지 변형이 있습니다: CVE-2026-43500은 Android Generic Kernel에서 비활성화된 RxRPC가 필요합니다. CVE-2026-43284는 xfrm-ESP가 필요하며 Android에서 악용 가능합니다. 그러나 SELinux는 신뢰할 수 없는 앱이 해당 기능을 사용하는 것을 금지합니다:

root@kitploit:~
# Privileged netlink socket interfaces.
neverallow { appdomain -network_stack }
    domain:{
        netlink_tcpdiag_socket
        netlink_nflog_socket
        netlink_xfrm_socket
        netlink_audit_socket
        netlink_dnrt_socket
    } *;

허용된 도메인은 system_server, network_stack 및 netd뿐입니다. DirtyFrag를 악용하려면 공격자는 먼저 허용 목록에 포함된 권한 있는 프로세스 중 하나를 손상시켜야 합니다.

두 버그를 결합합니다. 사용자 공간 버그를 통해 system_server 내부에서 Java 코드를 실행할 수 있지만, SELinux는 system_server가 /data에서 네이티브 라이브러리를 로드하는 것이나 익명 실행 가능 메모리를 매핑하는 것을 모두 금지합니다. 이로 인해 네이티브 코드를 사용하는 것이 불가능해져 악용의 난이도가 높아집니다. 따라서 우리 APK에서 네이티브 코드를 로드할 수 있고 DirtyFrag를 악용할 수 있는 충분한 권한을 가진 com.android.networkstack 내부에서 코드를 실행하는 것이 더 바람직합니다.

다행히도 system_server는 ActivityManager가 실행되는 프로세스입니다. ActivityManager는 모든 앱 프로세스의 IApplicationThread 핸들을 Java 맵에 저장하며, 우리는 ActivityManager와 동일한 프로세스로 실행되므로 Java 리플렉션을 사용하여 이를 검색할 수 있습니다. 이를 통해 com.android.networkstack에 임의의 명령을 보내 우리 코드를 로드하도록 강제할 수 있습니다. 이 트릭에 대한 자세한 내용은 CVE-2026-0091에 대한 이전 익스플로잇을 참조하세요.

커널 진입

DirtyFrag를 사용하면 읽기 전용 파일을 덮어쓸 수 있습니다. 이는 Linux 세계에서 강력한 프리미티브입니다. SUID 비트가 있는 su 바이너리를 덮어쓸 수 있기 때문입니다. 그러나 Android 세계에는 su가 없습니다. polygraphene의 DirtyPipe 익스플로잇을 참조하여 DirtyFrag를 Android에서 커널 코드 실행으로 전환합니다:

  1. DirtyFrag를 통해 libc.so, libc++.so 및 /vendor/lib64/libstagefright_aidl_bufferpool2.so를 패치합니다. libstagefright_aidl_bufferpool2.so는 vendor_file 도메인으로 레이블이 지정되어 네트워크 스택 프로세스에서 액세스할 수 없습니다. 해결책은 먼저 /apex/com.android.runtime/bin/crash_dump64를 패치하고 실행한 다음, crash_dump 도메인으로 전환되면 libstagefright_aidl_bufferpool2.so를 열 수 있습니다.
  2. 고아 프로세스를 생성하고 파괴하여 init 프로세스에서 코드 실행을 트리거합니다. libc++.so가 패치되었으므로 우리 코드는 UID 0으로 init 도메인에서 실행됩니다. 그런 다음 /vendor/bin/modprobe를 실행하여 vendor_modprobe 도메인으로 전환합니다.
도구 다운로드
  • modprobe가 실행되면 libc.so도 패치되었으므로 우리 코드는 vendor_modprobe 도메인에서 실행됩니다. 이제 지정된 레이블이 있는 파일에 대해서만 커널 모듈을 로드할 수 있습니다. vendor_file 레이블이 있는 libstagefright_aidl_bufferpool2.so를 로드합니다.
  • libstagefright_aidl_bufferpool2.so를 패치했으므로 해당 파일의 실제 내용은 우리 자신의 커널 모듈로 대체되었습니다. 커널 모듈이 로드되면 이제 SELinux 정책 조정이나 SELinux를 permissive로 설정하는 것을 포함하여 무엇이든 할 수 있습니다.
  • SELinux를 permissive로 설정합니다. 이제 UID 0에 SELinux가 비활성화된 상태입니다. 사용자를 위해 KernelSU를 실행합니다.