
Demo CVE-2019-2215 (Bad Binder) für Android Q
Dieses Repository ist ein kleines Testprojekt zur Untersuchung der Schwachstelle
CVE-2019-2215 (Bad Binder) und zum Schreiben eines funktionierenden Exploit-Prototyps für Android mit einer einfachen grafischen Oberfläche in Kotlin/Jetpack Compose.
In der README:
Im Repository ist ein GitHub Actions Workflow eingerichtet, der bei jedem Push/PR das Projekt mit dem Befehl ./gradlew assembleDebug erstellt und das fertige badbinder-debug.apk als Artefakt veröffentlicht.
Herunterladen kann man es so:
badbinder-debug-apk mit dem erstellten APK.Dies wurde für den Komfort gemacht, falls man die App einfach testen möchte, ohne eine lokale Umgebung aufzusetzen.
CVE-2019-2215 ist ein Use-After-Free (UAF) in der Binder-IPC-Subsystem des Android-Kernels.
Vereinfacht:
struct binder_thread, die einen Thread beschreibt, der Binder-Aufrufe ausführt;waitqueue);remove_wait_queue zu arbeiten, was ein klassisches UAF-Szenario eröffnet;Eine detailliertere theoretische Analyse habe ich anhand folgender Materialien durchgeführt:
Laut Aufgabenstellung wird empfohlen, ein AVD mit Android 10.0 (Q) x86_64-Image zu verwenden.
Ich habe Folgendes gemacht:
/dev/binder vorhanden ist.adb shell
ls -l /dev/binder
An diesem Punkt bin ich auf eine unangenehme Tatsache gestoßen:
die aktuellen AVD-Images werden bereits mit einem gepatchten Kernel ausgeliefert, in dem CVE-2019-2215 geschlossen ist. Das heißt, man kann auf dem aktuellen offiziellen Emulator tatsächlich kein Root erlangen – der Exploit scheitert in späteren Phasen oder gewährt einfach keine Rechteerhöhung.
Letztendlich verwende ich das AVD als Trainingsumgebung zur Nachbildung der Exploit-Logik:
addr_limit zu überschreiben,Dies ist ein wichtiger Hinweis: Der gesamte Code und der Bericht unten sind zu Lehrzwecken, nicht „kampferprobt“.
Ich habe eine kleine Android-App erstellt:
Die wichtigsten Schritte:
Ein normales Projekt in Android Studio erstellt (Kotlin, minimale Unterstützung für Android 10).
NDK und CMake eingebunden.
Eine native Datei mit dem Exploit hinzugefügt (die Datei cve-2019-2215.c mit den Funktionen leak_task_struct, overwrite_addr_limit usw.).
In CMakeLists.txt die Erstellung von libcve-2019-2215.so hinzugefügt.
In MainActivity:
init {
System.loadLibrary("cve-2019-2215")
}
external fun runNativeExploit(): String
external fun setNativeLogger(logger: NativeLogger)
Auf der Kotlin-Seite ein ExploitViewModel erstellt, das das Interface NativeLogger implementiert und alle Nachrichten in einem StateFlow<List<String>> sammelt. Das UI abonniert diesen Flow und zeigt das Log in einem „Terminal“ an.
Beim Start der Activity rufe ich setNativeLogger(viewModel) auf, damit der native Code ein Objekt erhält, an das er Strings senden kann.
Ich erstelle und installiere die App:
./gradlew installDebug
Starte das AVD und die App.
Auf dem Bildschirm sehe ich ein „Terminal“ und einen Button RUN EXPLOIT.
Beim Drücken:
runNativeExploit() im Hintergrundthread auf.Auf einem echten verwundbaren Kernel würde ich am Ende etwas erwarten wie:
[+] Selinux changed: Permissive now.
[+] Root escalation successful!
uid=0(root)...
Auf dem aktuellen Android 10-Emulator passiert das natürlich nicht, aber der Rest – die Leckage von task_struct, der Versuch, addr_limit zu überschreiben, das Berechnen von cred und kernel_base – funktioniert als „Szenario“, was für die Aufgabe ausreicht.
Unten ist das logische Schema des Exploits mit Verweis auf die spezifischen C-Funktionen.
Der grobe Plan ist:
struct binder_thread erzeugen und nutzen, um die Adresse der task_struct des eigenen Prozesses zu leaken (leak_task_struct).addr_limit in der task_struct überschreiben (overwrite_addr_limit) – dies hebt die Einschränkung zwischen User-Space- und Kernel-Space-Adressen für spätere copy_to_user / copy_from_user auf.arb_read / arb_write).cred des aktuellen Prozesses und die Kernel-Basis finden (verifying), dann:
selinux_enforcing = 0),cred-Felder überschreiben, um Root zu werden und volle Capabilities zu erhalten (runNativeExploit).Parallel dazu habe ich einen JNI-Logger integriert, damit all diese Phasen direkt im UI sichtbar sind.
task_struct-Adresse (leak_task_struct)Die Schlüsselfunktion:
void leak_task_struct() {
android_log("[*] Starting leak_task_struct...");
cpu_set_t cpu_set;
CPU_ZERO(&cpu_set);
CPU_SET(0, &cpu_set);
ret = sched_setaffinity(0, sizeof(cpu_set), &cpu_set);
assert(ret >= 0);
...
}
Was die Funktion macht:
sched_setaffinity), um das Verhalten des Kernel-Allokators vorhersagbarer zu machen. Dies verbessert die Stabilität der UAF-Ausnutzung./dev/binder, erstellt einen epoll-Deskriptor:
fd = open("/dev/binder", O_RDONLY);
epfd = epoll_create(1000);
Der Binder-Deskriptor wird beim epoll registriert: