Skip to content
KitploitKITPLOIT
ToolsBlog
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
TLPE — CVE-2026-49881, ein Logikfehler in der Klasse InCallController im Telecom-Dienst von Android 17, der es einer App ohne Privilegien ermöglicht, beliebigen Code als UID 1000 system_server auszuführen. | Kitploit
Tools/GitHubGitHub/supersonic/tlpe
Android-SicherheitPrivilege EscalationPersistenzmechanismenSchwachstellenanalyseExploitationMobile SicherheitBinary-Exploitation
GitHubsupersonic/tlpe

TLPE

CVE-2026-49881, ein Logikfehler in der Klasse InCallController im Telecom-Dienst von Android 17, der es einer App ohne Privilegien ermöglicht, beliebigen Code als UID 1000 system_server auszuführen.

Repository anzeigen
111vor 11h 46mNoch nicht geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen

Dies ist ein PoC und ein Writeup für CVE-2026-49881, ein Logikproblem in der Klasse InCallController im Telecom-Dienst von Android 17, das es einer nicht privilegierten App ermöglicht, beliebigen Code als UID 1000 system_server ohne zusätzliche Benutzerinteraktion auszuführen. Wir zeigen hier außerdem, dass die Codeausführung in system_server auch in modernen Android-Versionen noch leicht für Persistenz angepasst werden kann.

Ich habe diese Schwachstelle am 2026-04-10 an das Android Security Team gemeldet, sie wurde am 2026-05-06 bestätigt und im September-2026-Android-Sicherheitsbulletin behoben. (Patch hier)

Zum Zeitpunkt der Meldung betraf sie meines Wissens nur aktiv Pixel-Builds ab Android 16 QPR3 (zusammen mit 17-Beta-Builds), später fand sie jedoch ihren Weg in das stabile AOSP-Release von Android 17.

Um sich vor diesem Problem zu schützen, stellen Sie sicher, dass Sie sowohl das Google-Play-Systemupdate als auch den Systemsicherheitspatch installieren. (Telecom ist seit Android 17 eine Mainline-Komponente)

TLPE

Hinweise zum PoC

  • Der PoC demonstriert die Erlangung von Codeausführung in system_server mithilfe der Schwachstelle, protokolliert id und Stack-Trace in logcat und installiert sich selbst als system_server-Komponente neu.
  • Sobald der PoC installiert ist, löst entweder das Tippen auf die Schaltfläche „Start Exploit“ oder ein über den Telecom-Stack getätigter Anruf ihn aus.
  • Kompilieren Sie den PoC, indem Sie das enthaltene build.sh ausführen. Alternativ können Sie manuell ./gradlew assembleSystemRelease ausführen, die resultierende app-system-release.apk nach app/src/poc/assets/system.apk verschieben und dann ./gradlew assemblePocRelease ausführen.
  • Der PoC registriert nach erfolgreicher Ausnutzung sein eigenes Zertifikat als Vorgängerzertifikat für UID 1000. Dieser Zustand übersteht OTAs, einschließlich des Patches der Schwachstelle selbst. Um Ihr Gerät nach einem PoC-Lauf zu bereinigen, sollten Sie die Schaltfläche „Uninstall“ im PoC drücken (die das injizierte Zertifikat bereinigt) – dennoch empfehle ich dringend, während des Testens einen eigenen Release-Keystore zum Signieren eines kompilierten PoC-APKs zu verwenden. (anstelle desjenigen, den der PoC standardmäßig unter TLPE/app/teststore.jks verwendet)
  • Beachten Sie, dass der PoC, nachdem er in system_server gelangt ist, auch Play Protect deaktiviert, indem er package_verifier_user_consent in Settings.Global auf -1 setzt, da dies manchmal die Neuinstallations-Transaktion aufgrund unbekannter Signaturen abfangen kann. Sie sollten dies nach dem Testen in den Einstellungen wieder aktivieren.
  • Es wurde auf Pixel-Android und AOSP validiert, aber die Codeausführungsphase sollte über OEM-angepasste Android-17-Versionen hinweg funktionieren. Die Neuinstallation als system_server-Phase könnte eine gewisse OEM-spezifische Anpassung erfordern, da sie auf dem Durchlaufen der PMS-Struktur mithilfe von Symbolen basiert, die OEMs manchmal ändern.

Die erwartete PoC-logcat-Ausgabe ist:

root@kitploit:~
04-15 03:02:47.911  1558 12775 E TLPE    : ===================================
04-15 03:02:47.911  1558 12775 E TLPE    : [+] Exploit successful!
04-15 03:02:47.911  1558 12775 E TLPE    : [+] Running as: [uid=1000(system) gid=1000(system) groups=1000(system),1001(radio),1002(bluetooth),1003(graphics),1004(input),1005(audio),1006(camera),1007(log),1008(compass),1009(mount),1010(wifi),1018(usb),1021(gps),1023(media_rw),1024(mtp),1032(package_info),1065(reserved_disk),3001(net_bt_admin),3002(net_bt),3003(inet),3005(net_admin),3006(net_bw_stats),3007(net_bw_acct),3009(readproc),3010(wakelock),3011(uhid),3012(readtracefs) context=u:r:system_server:s0]
04-15 03:02:47.911  1558 12775 E TLPE    : [+] Current stack trace:
04-15 03:02:47.911  1558 12775 E TLPE    : [dalvik.system.VMStack.getThreadStackTrace(Native Method), java.lang.Thread.getStackTrace(Thread.java:2842), poc.sithi.tlpe.EvilFactory.instantiateClassLoader(EvilFactory.kt:31), android.app.LoadedApk.createOrUpdateClassLoaderLocked(LoadedApk.java:1215), android.app.LoadedApk.getClassLoader(LoadedApk.java:1267), android.app.ContextImpl.getClassLoader(ContextImpl.java:542), com.android.server.telecom.InCallController.serviceClassExists(InCallController.java:2561), com.android.server.telecom.InCallController.getInCallServiceComponents(InCallController.java:2606), com.android.server.telecom.InCallController.getInCallServiceComponents(InCallController.java:2515), com.android.server.telecom.InCallController.getInCallServiceComponents(InCallController.java:2499), com.android.server.telecom.InCallController.bindToBTService(InCallController.java:2247), com.android.server.telecom.InCallController.onCallAdded(InCallController.java:1437), com.android.server.telecom.CallsManager.addCall(CallsManager.java:5457), com.android.server.telecom.CallsManager.processIncomingCallIntent(CallsManager.java:1970), com.android.server.telecom.callsequencing.voip.IncomingCallTransaction.processTransaction(IncomingCallTransaction.java:76), com.android.server.telecom.callsequencing.CallTransaction$$ExternalSyntheticLambda3.apply(R8$$SyntheticClass:0), java.util.concurrent.CompletableFuture$UniCompose.tryFire(CompletableFuture.java:1126), java.util.concurrent.CompletableFuture$Completion.run(CompletableFuture.java:458), com.android.server.telecom.LoggedHandlerExecutor$1.loggedRun(LoggedHandlerExecutor.java:41), android.telecom.Logging.Runnable$1.run(Runnable.java:37), android.os.Handler.handleCallback(Handler.java:1095), android.os.Handler.dispatchMessageImpl(Handler.java:135), android.os.Handler.dispatchMessage(Handler.java:125), android.os.Looper.loopOnce(Looper.java:269), android.os.Looper.loop(Looper.java:367), android.os.HandlerThread.run(HandlerThread.java:139)]
04-15 03:02:47.911  1558 12775 E TLPE    : ===================================
04-15 03:02:47.934  1558 12785 E TLPE    : [+] Retrieved system APK, attempting persistence...
04-15 03:02:47.935  1558 12785 E TLPE    : [+] Injection successful, forcing packages.xml flush
04-15 03:02:47.957  1558 12785 E TLPE    : [+] Persistence successful, reinstalling...

Writeup

Dies ist eine ungewöhnlich direkte Schwachstelle. Immer wenn bestimmte Telecom-bezogene Aktionen stattfinden, versucht InCallController, verfügbare Dienste über getInCallServiceComponents zu entdecken. Dies geschieht natürlicherweise, wenn ein Anruf beim System registriert wird, aber eine App kann es dank der transaktionalen Anruf-API TelecomManager.addCall tatsächlich auch auf Abruf auslösen. (Hinweis: Das nutzt die Schaltfläche „Start Exploit“ im PoC) Diese API benötigt MANAGE_OWN_CALLS, aber das ist eine normale und für den Benutzer unsichtbare Berechtigung, die bei der Installation automatisch gewährt wird.

Diese Aufzählung ist in den verwundbaren Versionen wie folgt implementiert:

root@kitploit:~
private List<InCallServiceInfo> getInCallServiceComponents(UserHandle userHandle,
        String packageName, ComponentName componentName,
        int requestedType, boolean ignoreDisabled) {
        ...
        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;
                }
                ...
            }
        }
}

Beachten Sie, wie serviceClassExists gegen jede Komponente läuft, die einen InCallService-Intent zusammen mit dem Metadatenwert android.telecom.CLASS_EXISTENCE_CHECK deklariert, nicht nur gegen gültige/aktivierte InCallServices. (diese Prüfung wird weiter unten im Zweig mit getInCallServiceType und isServiceEnabled implementiert)

serviceClassExists ist wie folgt implementiert:

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

Die Gefahren von createPackageContext mit CONTEXT_IGNORE_SECURITY sind gut dokumentiert, und in diesem Fall ist es system_server selbst, das es gegen eine nicht vertrauenswürdige Komponente ausführt, nur um zu prüfen, ob eine Klasse im DEX dieser App existiert. Auf den ersten Blick kann dies jedoch sicher aussehen, da der erhaltene Kontext in Class.forName mit initialize=false verwendet wird – der Entwickler war sich dieses Risikos sehr wahrscheinlich bewusst und hat dieses Argument angegeben, um sicherzustellen, dass die fremde Klasse nicht in system_server initialisiert wird.

Leider kommt diese Vorsicht zu spät – der Schaden ist bereits durch getClassLoader auf dem fremden Kontext angerichtet. Wenn die Angreifer-App eine AppComponentFactory in ihrem Manifest als android:appComponentFactory definiert, ruft die Methode getClassLoader auf einem LoadedApk, das einem mit CONTEXT_INCLUDE_CODE und CONTEXT_IGNORE_SECURITY erstellten Kontext entspricht, zuerst den Standard-Classloader der fremden App von der Festplatte ab und führt dann sowohl den Konstruktor der Factory als auch ihre Methode instantiateClassLoader aus, bevor der Classloader zurückgegeben wird.

Da diese Klasse von der Angreifer-App kontrollierbar ist, führt dies sofort zu beliebiger Codeausführung im Kontext von Telecom.

Nach der Ausnutzung

Um die Konsequenzen einer erfolgreichen Codeausführung in system_server zu zeigen, demonstriert der PoC die Neuinstallation seiner selbst als persistente system_server-Komponente. (die Technik ist angepasst von der im AbxOverflow / CVE-2024-34740-PoC von Michał Bednarski veröffentlichten Technik)

Wir tun dies, indem wir:

  • Das Signature-Objekt unserer PoC-App abrufen, indem wir direkt in PackageManagerService.mSettings reflektieren, sobald wir in system_server sind.
  • Das SharedUserSetting abrufen, das "android.uid.system" entspricht, und die Signature des PoC zweimal in dessen getSigningDetails().mPastSigningCertificates mit CertCapabilities.SHARED_USER_ID injizieren.
  • Die PoC-App zwangsdeinstallieren und eine Variante davon neu installieren, der android:sharedUserId="android.uid.system" und android:process="system" zum Manifest hinzugefügt wurden. (was auch die flüchtige Änderung aus dem vorherigen Schritt in packages.xml spült)

Dies lässt canJoinSharedUserId() in PackageSignatures aufgrund der Rotationsverlaufsübereinstimmung bestehen und gewährt anhaltende system_server-Berechtigungen.

Abschließende Anmerkung

Eine Frage, die Sie sich vielleicht stellen, ist, warum diese Schwachstelle überhaupt existiert – insbesondere, warum es eine Opt-in-Klassenexistenzprüfung gibt, die hinter einem undokumentierten Metadaten-Flag geschützt ist?

Es ist unmöglich, dies mit Sicherheit zu sagen, aber die wahrscheinliche Antwort auf dieses Rätsel kann gefunden werden, wenn man etwas tiefer in AOSP gräbt: Sowohl die Klassenprüfung als auch der Metadatenvergleich wurden wahrscheinlich hinzugefügt, um android.net.ConnectivityCallListenerService zu berücksichtigen, das ungefähr zur gleichen Zeit implementiert wurde.

Dieser Dienst war zunächst im Framework-Manifest definiert, wurde aber nirgendwo implementiert. (und als er doch hinzugefügt wurde, stand er hinter dem Feature-Flag Flags.FLAG_ENABLE_INCALL_SERVICE_API) Sobald die Abstürze beim Testen beobachtet wurden, entschied sich wahrscheinlich jemand, den Fix als wiederverwendbare „Defense-in-Depth“-Prüfung zu implementieren, anstatt als hartcodierte Ausnahme – die Manifestdefinition dieses Dienstes besagt, dass er "android.telecom.CLASS_EXISTENCE_CHECK" definiert, um „anzuzeigen, dass die Klasse dieses Dienstes möglicherweise nicht in allen Builds vorhanden ist“ und „Telecom anzuweisen, die Klassenexistenz zu überprüfen, bevor versucht wird, eine Bindung herzustellen“. (und deshalb sind wir jetzt hier)

Tool herunterladen