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
OrganizerTransaction — PoC für CVE-2021-39749, der das Starten beliebiger Activities auf Android 12L Beta ermöglicht | Kitploit
Tools/GitHubGitHub/michalbednarski/organizertransaction
Android-SicherheitSchwachstellenanalyseExploitationMobile App-PenetrationstestsMobile Sicherheit
GitHubmichalbednarski/organizertransaction

OrganizerTransaction

PoC für CVE-2021-39749, der das Starten beliebiger Activities auf Android 12L Beta ermöglicht

Repository anzeigen

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen
3711vor 4 JahrenVon Kitploit geprüft

Dies ist ein PoC für CVE-2021-39749, der das Starten von Aktivitäten anderer Apps auf Android 12L Beta unabhängig von deren permission- und exported-Einstellungen ermöglicht

In Android 12L erfordert der TaskFragmentOrganizer-Zugriff (absichtlich) nicht länger die MANAGE_ACTIVITY_TASKS-Berechtigung

Die Verwendung der hier bereitgestellten App erfordert das Deaktivieren der Hidden API Checks. Dies kannst du über adb shell settings put global hidden_api_policy 1 tun. Diese stellen keine Sicherheitsgrenze dar und es gibt bekannte app-basierte Umgehungen

Hier sind Commits, die diesen Fehler (und einige verwandte, die im ursprünglichen Bericht erwähnt wurden) beheben:

  1. startActivityInTaskFragment verlässt sich nicht länger auf Binder.getCallingUid()
  2. ResolverActivity hat jetzt relinquishTaskIdentity aktiviert
  3. (Nicht zum Starten anderer Aktivitäten erforderlich, ermöglicht aber deren Neupositionierung auf dem Bildschirm sowie das Transparentmachen und Tap-Jacking) SurfaceControl von TaskFragment wird nicht länger bereitgestellt
  4. (Im Code hier nicht gezeigt, Problem nur im ursprünglichen Bericht erwähnt) Die Entscheidung, ob ActivityRecord#appToken an TaskFragmentOrganizer gesendet wird, basiert jetzt auf uid statt pid

Du kannst android-12.1.0_r4 auschecken und die ersten 3 Commits (oder die ersten 2) zurücknehmen. Die Anwendung kann das Gerät dann weiterhin herunterfahren (durch Starten von ShutdownActivity), aber das Kontrollkästchen „Zoom and set alpha" funktioniert nicht

(Der erste Commit aus dieser Liste führt zu Merge-Konflikten in Tests, wenn du versuchst, ihn zurückzunehmen, aber diese kannst du ignorieren)

Binder.getCallingUid(), das immer die System-uid zurückgibt

Die Methode Binder.getCallingUid() gibt die uid des Prozesses zurück, der die aktuell verarbeitete Binder-Transaktion gesendet hat. Diese uid wird in einer thread-lokalen Variable gespeichert. Code, der eine Transaktion verarbeitet, kann Binder.clearCallingIdentity() aufrufen, um diese Variable auf die uid des eigenen Prozesses zu setzen. Dies zeigt Methoden, die später während der Transaktionsverarbeitung aufgerufen werden, an, dass Berechtigungsprüfungen gegen sich selbst (den Code, der die Transaktion verarbeitet) und nicht gegen den Aufrufer der Binder-Transaktion durchgeführt werden sollten

Manchmal gibt es Binder.getCallingUid()-Aufrufe, die immer nach Binder.clearCallingIdentity() aufgerufen werden und daher immer die uid des eigenen Prozesses zurückgeben. Manchmal geschieht dies absichtlich, zum Beispiel in ActivityTaskManagerService#startDreamActivity (obwohl dies ein eher umständlicher Weg ist, um Process.myUid() oder Os.getuid() zu verwenden)

Ich habe für mich selbst ein (auf Soot basierendes) statisches Analysetool geschrieben, das solche Binder.getCallingUid()-Aufrufe (und andere Berechtigungsprüfungen) meldet, die nur nach Binder.clearCallingIdentity() auftreten können. (Ich habe eine eigene Logik für die Verarbeitung der von Soot bereitgestellten Jimple/Shimple-IR, obwohl es möglicherweise einen besseren Weg mit Soot gibt, aber das ist, was ich derzeit habe)

In der Android 12L Beta fand dieses Tool einen solchen Aufruf in ActivityStartController#startActivityInTaskFragment (Hinweis: Der Quellcode war damals nicht verfügbar, da Beta-Versionen nicht Open Source sind, aber Shimple ist im Allgemeinen lesbar, daher habe ich Soot auch als Java-Dekompilierer verwendet)

So rufst du startActivityInTaskFragment auf

Als Teil des statischen Analyseberichts habe ich die Aufrufhierarchie von der onTransact()-Implementierung (wo der Binder-Aufruf beginnt) bis zu startActivityInTaskFragment erhalten:

  1. onTransact im aidl-generierten Code von IWindowOrganizerController
  2. WindowOrganizerController#applyTransaction (ohne CallerInfo-Argument)
  3. WindowOrganizerController#applyTransaction (mit CallerInfo-Argument)
  4. WindowOrganizerController#applyHierarchyOp
  5. ActivityStartController#startActivityInTaskFragment

Ich habe festgestellt, dass Binder-Aufrufe an applyTransaction in der TaskFragmentOrganizer-Klasse vorhanden sind, und ich habe beschlossen, sie als bequemeren Wrapper zu verwenden, als alle Binder-Aufrufe direkt durchzuführen (keines davon ist eine öffentliche API, daher musste ich ohnehin Reflection verwenden)

Zunächst ruft Methode „2." enforceTaskPermission auf, das unter Android 12.0 die nur-signature-Berechtigung MANAGE_ACTIVITY_TASKS prüfte, die wir nicht erhalten konnten. Unter Android 12L wurden die Regeln jedoch gelockert, sodass bestimmte Transaktionen ohne Berechtigungen durchgeführt werden können. Es stellte sich heraus, dass keine der für startActivityInTaskFragment benötigten Operationen eine Berechtigung erforderte (wenn die Transaktion einen zugeordneten TaskFragmentOrganizer hatte)

Wir möchten also HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT durchführen. Dazu müssen wir unseren TaskFragment in mLaunchTaskFragments registriert haben, andernfalls wird die Ausnahme "Not allowed to operate with invalid fragment token" gemeldet

Wir können einen solchen TaskFragment über HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT registrieren, das createTaskFragment() aufruft

(Im PoC-Code werden diese Transaktionen in SecondActivity gesendet: HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT wird von initOrganizerAndFragment() gesendet und HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT wird von startActivityInOrganizer gesendet)

Das ermöglicht uns also, startActivityInTaskFragment aufzurufen, und Intents von hier gestarteten Aktivitäten werden als vom System-uid stammend betrachtet. Es stellt sich jedoch heraus, dass uns das allein nichts ermöglicht: vom System gestartete Aktivitäten können keine URI-Grants durchführen, und wenn wir versuchen, eine Aktivität einer anderen App zu starten, werden wir durch die canEmbedActivity-Prüfung gestoppt

Umgehung von canEmbedActivity

Schauen wir uns canEmbedActivity noch einmal an: Das Einbetten ist erlaubt, wenn taskFragment.getTask().effectiveUid die uid des Systems ist oder mit der uid der gestarteten App übereinstimmt. Wir müssen uns in einer Task befinden, deren effectiveUid das System ist

Gehen wir auch zurück zu createTaskFragment(): Die Erstellung von TaskFragment war nur erlaubt, wenn rootActivity.getUid() != ownerActivity.getUid(). Das bedeutet, dass unsere Aktivität am unteren Ende des Back-Stacks der Task, in der sie sich befindet, liegen muss

Wir müssen eine neue Task starten (über Intent.FLAG_ACTIVITY_NEW_TASK), die eine Aktivität enthält, die zum System-uid gehört (damit Task#effectiveUid auf AID_SYSTEM gesetzt wird), und dann startet diese Aktivität unsere Aktivität (innerhalb derselben Task) und ruft finish() für sich selbst auf (damit unsere Aktivität zur Wurzel dieser Task wird und wir createTaskFragment() verwenden können)

Eine solche Aktivität ist ChooserActivity. Der Chooser wird normalerweise verwendet, um auszuwählen, welche App der Benutzer verwenden möchte, nachdem er die Option „Teilen" ausgewählt hat. ChooserActivity hat jedoch android:relinquishTaskIdentity="true" in AndroidManifest.xml gesetzt, was bedeutet, dass sie beim Starten einer anderen Aktivität Task#effectiveUid mit der uid der neu gestarteten App überschreibt

(relinquishTaskIdentity funktioniert nur, wenn es von der ersten App in der Task verwendet wird, und nur für System-Apps. Wir können also relinquishTaskIdentity nicht selbst verwenden und eine System-App starten, um Task#effectiveUid unserer Task zu überschreiben)

Eine weitere solche Aktivität (die unsere Aktivität starten und finish() für sich selbst aufrufen kann) ist ResolverActivity. Sie wird verwendet, wenn ein impliziter Intent gestartet wird, der zu mehreren Aktivitäten aufgelöst wird. Der Resolver bietet (anders als der Chooser) die Option, die Auswahl zu speichern, woran du (als Telefonbenutzer) die beiden unterscheiden kannst. ResolverActivity hatte relinquishTaskIdentity nicht gesetzt, aber der Resolver verwendet seinen eigenen Intent, um herauszufinden, welche Optionen verfügbar sind (während der Chooser den in Extras bereitgestellten Intent verwendet). Dies erweist sich als Problem für die Ausnutzung, da die vom Resolver beim Starten der ausgewählten Aktivität verwendeten Intent-Flags dieselben sind wie die zum Starten des Resolvers verwendeten, und:

  • Wenn wir Intent.FLAG_ACTIVITY_NEW_TASK nicht setzen, wird der Resolver innerhalb unserer Task gestartet, die bereits eine dauerhaft gesetzte effectiveUid hat
  • Wenn wir Intent.FLAG_ACTIVITY_NEW_TASK setzen, startet der Resolver die Auswahl in eine weitere Task, die dann eine effectiveUid hat, die der gestarteten App gehört

Die Lösung für diese Probleme besteht darin, beides zu verwenden:

  1. Zuerst starten wir ChooserActivity: Wir geben ihrem Intent Folgendes mit:
    • Intent.FLAG_ACTIVITY_NEW_TASK, damit der Chooser in einer neuen Task gestartet wird (die die effectiveUid des Systems hat, aber nur bis zum nächsten Aktivitätsstart)
    • Intent.EXTRA_INTENT gesetzt auf einen Intent, der keiner Aktivität entspricht, und die einzigen im Chooser verbleibenden Optionen stammen aus Intent.EXTRA_INITIAL_INTENTS
    • Intent.EXTRA_INITIAL_INTENTS mit einem Array mit einem Element: dem Intent, den der Chooser starten soll (wenn es nur eine Option gibt, überspringen sowohl Chooser als auch Resolver die Eingabeaufforderung und starten sofort die einzige Option und rufen finish() für sich selbst auf)
  2. Dann wird ResolverActivity von ChooserActivity gestartet:

(Im PoC-Code wird die Vorbereitung dieser Schritte in FirstActivity durchgeführt)

Andere Tricks mit TaskFragmentOrganizer

TaskFragmentOrganizer erhielt ein SurfaceControl über den onTaskFragmentAppeared-Callback, und mit diesem SurfaceControl kann man die gestartete Aktivität skalieren und transparent machen, während sie weiterhin Touch-Ereignisse empfängt und nicht als verdeckt betrachtet wird (sodass tap-jacking-geschützte Elemente weiterhin angetippt werden können)

Du kannst dies sehen, indem du das Kontrollkästchen „Zoom and set alpha" in der PoC-App aktivierst

Dies wird durch Commit „3." aus der Fix-Liste oben behoben


Eine weitere Sache ist, dass ActivityRecord#appToken-s von Aktivitäten, die innerhalb von TaskFragment ausgeführt werden, an TaskFragmentOrganizer-Callbacks übergeben werden. Diese Liste wurde gefiltert, um nur Token von Aktivitäten innerhalb desselben Prozesses zu enthalten. Die Prüfung wurde jedoch durchgeführt, indem die pid von TaskFragmentOrganizer mit der pid der Aktivität verglichen wurde, deren appToken wir erhalten konnten. Ich habe es nicht tatsächlich überprüft, aber ich denke, eine Anwendung könnte einen TaskFragmentOrganizer erstellen, den Prozess verlassen, der ursprünglich zum Erstellen verwendet wurde, und dessen pid als pid der Aktivität einer anderen App wiederverwenden lassen, um deren appToken zu erhalten. Hier ist der Commit („4." in der Fix-Liste oben), der die Überprüfung von pid-basiert auf uid-basiert umstellt (es sieht so aus, als ob dieser Commit unabhängig von meinem Bericht erstellt wurde (obwohl danach))

Sobald ein Angreifer das appToken einer Aktivität erhält, kann er onActivityResult()-Aufrufe injizieren (auch wenn die Ziel-App selbst kein startActivityForResult() aufgerufen hat) und möglicherweise savedInstanceState manipulieren (durch Aufruf von activityStopped(), vorausgesetzt, der Angreifer kann das Rennen mit der Zielanwendung gewinnen, die diese Methode aufruft, und ein zusätzlicher Aufruf führt nicht zu einem Zustandsverlust aufgrund eines Absturzes)

Ich habe nicht überprüft, ob dies in diesem Fall möglich ist. Zuvor konnte ich jedoch mit CVE-2020-0001 (Ja, ich habe eine ausgefallene Nummer bekommen) savedInstanceState-Manipulation und onActivityResult()-Injektion verwenden, um die Systemeinstellungen-App dazu zu bringen, meinen AccessibilityService ohne Benutzerinteraktion zu aktivieren, aber das ist eine Geschichte für ein anderes Mal

Tool herunterladen
  • Der Resolver hatte relinquishTaskIdentity nicht gesetzt, daher ist Task#effectiveUid jetzt auf das System gesetzt und bleibt so, unabhängig von den nächsten in dieser Task gestarteten Aktivitäten
  • Der Intent hat kein Intent.FLAG_ACTIVITY_NEW_TASK, daher wird die nächste Aktivität innerhalb derselben Task gestartet
  • Die Intent-Aktion ist auf eine nicht standardmäßige gesetzt, die nur mit dem von uns selbst in unserer App deklarierten <intent-filter> übereinstimmt, sodass der Resolver sofort mit dem Starten unserer Aktivität fortfährt
  • ResolverActivity startet unsere Aktivität
    • Jetzt befinden wir uns in einer Task, deren effectiveUid AID_SYSTEM ist, sodass canEmbedActivity() alles erlaubt
    • Sowohl Chooser als auch Resolver haben finish() für sich selbst aufgerufen, daher sind wir die Wurzelaktivität in der Task und dürfen createTaskFragment() verwenden