
# adb-gesteuerter KernelSU-Loader für Stock-Google-Pixel: temporärer Kernel-Lese-/Schreibzugriff über CVE-2026-43499 (GhostLock), anschließend spätes Laden eines signaturgeprüften kernelsu.ko für das laufende KMI. Manager-agnostisch.
Ein adb-gesteuertes Tool, das ein unverändertes Google Pixel mit gesperrtem Bootloader in ein KernelSU-gerootetes Gerät verwandelt, ohne den Bootloader zu entsperren oder das Boot-Image zu modifizieren. Vom Host aus führt es einen unprivilegierten Userspace-Kernel-Exploit auf dem Gerät aus, um temporären Kernel-Lese-/Schreibzugriff zu erhalten, nutzt diese Primitive, um eine Root-Anmeldeinformation zu patchen, und lädt anschließend ein KernelSU-Loadable-Kernel-Modul (kernelsu.ko) in den laufenden GKI-Kernel nach und übergibt die Kontrolle an den bereits installierten KernelSU-Manager (KernelSU, KernelSU-Next, SukiSU oder eine andere Variante). Es zielt auf Android-17-Pixels mit GKI-6.1- und 6.6-Kerneln ab und steuert den gesamten Ablauf über adb shell für deterministische Sequenzierung und Zeitstempel-Logs.
Das geräteseitige Payload ist der eingebundene Full-Chain-LPE für CVE-2026-43499 („GhostLock"), ein futex/rtmutex-Priority-Inheritance-Stack-Use-after-Free in kernel/locking/rtmutex.c. Auf dem Requeue-PI-Rollback-Pfad löscht remove_waiter() pi_blocked_on auf dem Requeuer (current) statt auf dem tatsächlichen Waiter, wodurch ein verwaister Zeiger in einen freigegebenen Kernel-Stack-Slot zurückbleibt, der ein rt_mutex_waiter enthielt. Der Bug ist von einem gewöhnlichen unprivilegierten Prozess aus erreichbar:
owner, waiter, consumer) bauen eine PI-Kette auf; der Waiter parkt auf FUTEX_WAIT_REQUEUE_PI, der Haupt-Thread feuert FUTEX_CMP_REQUEUE_PI, und ein sched_setattr vom Consumer treibt den Rollback.pselect()/select() (oder einer TCP_ZEROCOPY_RECEIVE-Route auf einigen 6.1-Zielen) zurückerobert, dessen fd_set-Wörter über der Waiter-Struktur landen und ein gefälschtes flaches rt_mutex_waiter schreiben, sodass der verwaiste Zeiger über Angreifer-kontrollierte rb-tree- und Lock-Felder läuft – eine einzelne kontrollierte Pointer-Write-Primitive.mm_struct//-Objekte enthält.Root- und SELinux-Zustand werden dann über die Pipe-Primitive gepatcht: Die cred des Root-Kind-Tasks wird auf uid/gid 0 mit vollständigen Capability-Sets genullt, seine SELinux-osid/sid auf SECINITSID_KERNEL gesetzt, seccomp gelöscht und selinux_state.enforcing auf 0 gesetzt.
Die Exploit-Kette, das KASLR-Orakel und der KernelSnitch-Seitenkanal stammen aus NebuSecs IonStack Part II — GhostLock-Forschung (das NebuSec/CyberMeowfia-PoC, Apache-2.0), hier für Pixel/aarch64 adaptiert. Siehe Attribution & Lizenz.
Genau eine Stufe der Kette kann den Kernel zum Absturz bringen: die KASLR-Slide-Ableitung, die mit einer Seite um eine Rennbedingung kämpft, die sie hofft, zurückerobert zu haben. Jede andere Stufe ist retry-sicher, und die Kernel-Text-Basis ist für die Lebensdauer eines einzelnen Boots fest. Der Host-Ablauf teilt sich auf dieser Eigenschaft auf:
KASLR_BASE in seiner Umgebung. Der Forged-Waiter-Write zeigt das random_table-sysctl-ctl_table.data auf einen bekannten Kernel-Text-Pointer; das Lesen von /proc/sys/kernel/random/boot_id leakt es durch proc_do_uuid(), und das Subtrahieren des Image-Offsets ergibt _stext/die KASLR-Basis. restore_slide_boot_id() repariert das beschädigte ctl_table.data. Da eine verlorene Rennbedingung das Gerät neu startet, geht jedem Versuch ein Warten-auf-Boot voraus, und ein Liveness-Check klassifiziert ein Verschwinden als Absturz. Bei Erfolg emittiert das Gerätelog slide-kaslr-ok pid=<pid> base=<hex>, und die Basis wird an den aktuellen Boot gebunden.KASLR_BASE=0x<base>. Dieser Pfad stürzt nie ab und wird wiederholt, bis id durch das temporäre su meldet.Das verbundene Gerät wird zur Laufzeit gegen data/targets.json aufgelöst; nichts ist gerätehartcodiert. Zwei unabhängige Auflösungen finden statt:
uname -r) übernommen, entweder aus dem passenden Ziel-Eintrag oder aus der Release-Zeichenkette abgeleitet (z. B. android14-6.1).Die Wiederverwendung eines Payloads über viele Geräte folgt aus der GKI/KMI-Struktur. Jedes Gerät auf demselben GKI-Build läuft mit dem byte-identischen vmlinux, und Struct-Feld-Offsets (task_struct->cred, cred->uid, …) sind für die Lebensdauer eines KMI-Zweigs durch den KMI-Typvertrag und die MODVERSIONS-CRC-Durchsetzung eingefroren. Absolute Kernel-Symbol-Adressen hingegen werden vom Linker pro ab<NNN>-Build entschieden, sodass die festen Offsets des Exploits zu einem bestimmten vmlinux gehören; unterschiedliche Kernel-Images erfordern daher unterschiedliche Payloads, selbst wenn ihr KMI übereinstimmt. data/targets.json kodiert genau dies: Viele Geräte deduplizieren auf ein Payload, das nach Kernel-Image geschlüsselt ist, während ein abweichendes Kernel-Image sein eigenes erhält.
LKM-Spätladen erfordert einen GKI-Kernel (5.10+) mit Unterstützung für ladbare Module und ein KMI-passendes .ko. Das KernelSU-Kernelmodul authentifiziert seinen Manager, indem es den v2-Signaturblock der Manager-APK im Kernel verifiziert und den SHA-256 des Signaturzertifikats gegen ein in das .ko kompiliertes KSU_EXPECTED_SIZE/KSU_EXPECTED_HASH-Paar vergleicht. Das in einer Manager-Release mitgelieferte kernelsu.ko und die APK dieses Managers teilen daher eine Signaturidentität; ein nicht passendes ksud lädt den Treiber, setzt aber nie das Manager-Autorisierungsbit, sodass das Gerät ohne nutzbares Root bleibt.
Der Ablauf respektiert diese Bindung: Er löst den APK-Pfad des installierten Managers auf (pm path <manager package>), zieht die APK, extrahiert lib/arm64-v8a/libksud.so als ksud-Binärdatei, und wenn kein Manager installiert ist, bricht er ab. Mit gehaltenem temporärem Root wird dieses ksud als Root-eigene ausführbare Datei gestaged und als ksud late-load --kmi <kmi> --package-name <manager package> aufgerufen. Late-load erkennt das aktuelle KMI, zieht "{kmi}_kernelsu.ko" aus seinen eingebetteten Assets, führt manuelle Symbol-Relokation durch (löst jedes SHN_UNDEF-Symbol gegen /proc/kallsyms auf, schreibt Einträge auf SHN_ABS um) und ruft init_module(2) auf den gepatchten Puffer auf. Anschließend führt es die verbleibende Boot-Pipeline aus, die init ausführen würde (ksud installieren, restorecon, sepolicy.rule und Root-Profile laden, post-fs-data/Stage-Skripte ausführen, das Modul-Overlay mounten).
Late-load daemonisiert und setzt SELinux in seinem geforkten Kind erneut durch, das den temporären-su-Daemon des Exploits abreißt; die Verifikation darf daher nicht über su laufen. Stattdessen wird der geladene Treiber direkt über seine Syscall-Oberfläche abgefragt, die von einer einfachen Shell ohne Root erreichbar ist: ksud debug version wird gepollt und die gemeldete Kernel-Version geparst. Eine nicht-leere, nicht-null Version bestätigt, dass der Treiber resident ist und antwortet. Der Treiber-Installationspfad ist der reboot(2)-Magic → Install-fd → KSU_IOCTL_GET_INFO-Mechanismus (reboot(0xDEADBEEF, 0xCAFEBABE, 0, &fd) installiert einen anonymen [ksu_driver]-fd; GET_INFO gibt {version, flags, features, uapi_version} zurück), wobei der Legacy-prctl(0xDEADBEEF, …)-Kanal als Fallback sondiert wird. Dieselbe Sonde beim Start umgeht den gesamten Ablauf, wenn das Modul für den aktuellen Boot bereits resident ist.
Der Exploit staged sein temporäres su unter /apex/com.android.virt/bin/su auf einem tmpfs, das über dieses Apex-Bin-Verzeichnis im Mount-Namespace von adbd gemountet ist, da dieses Verzeichnis in der Shell-PATH vor /system/bin kommt – sodass ein nacktes su in adb shell das temporäre erreicht, während der Ablauf läuft. Late-load reißt dann den Temp-su-Daemon ab (siehe (e)), ohne den Schatten zu entfernen, was ein nacktes adb shell su hinterlässt, das einen verwaisten Client ausführt, der mit su: connect daemon: Permission denied fehlschlägt, obwohl Root funktioniert, und die echten Binärdateien des Apex (crosvm, virtmgr, vm, …) versteckt. Sobald die Verifikation einen Live-Treiber meldet, unmountet der Ablauf das Staging-tmpfs – über KernelSUs eigenes /system/bin/su, da der Exploit-Daemon bereits weg ist – entfernt den Temp-su-Client, Socket und Log und meldet, welches su ein einfaches nun auflöst. Es ist Best-Effort: Bei Fehlschlag warnt es mit dem manuellen -Befehl, statt den Lauf fehlschlagen zu lassen, und ein Neustart räumt das Mount unabhängig davon auf.
adb auf dem Host, mit autorisiertem Gerät (USB-Debugging aktiviert).kernelsu.ko.artifacts/exploits/ (siehe Payloads bauen).# Ein Gerät auf adb; Manager installiert; Payloads gebaut.
bin/pixel-ksu-root
Der Treiber löst das Gerät gegen data/targets.json auf, führt den Zwei-Phasen-KASLR-Ablauf aus, lädt das Modul über das managerabgeleitete ksud nach und verifiziert über den Treiber-Syscall. Er beendet sich mit Nicht-Null, wenn kein Payload für das Gerät aufgelöst wird, wenn kein Manager installiert ist oder wenn die Verifikation nie einen Live-Treiber meldet.
KASLR_BASE=0x<hex> – wird während Phase B an das Geräte-Payload übergeben, um gegen eine feste, bereits abgeleitete Pro-Boot-Basis zu replayen. Während Phase A nicht gesetzt, sodass das Payload die Basis selbst ableitet.ANDROID_NDK_HOME – Pfad zum Android-NDK, nur beim Bauen von Payloads erforderlich.API – Android-API-Level für die NDK-Toolchain beim Bauen von Payloads (Standard 35).pixel-ksu-root/
├── bin/ Host-Treiber-Einstiegspunkt (adb-gesteuerter Ablauf)
├── data/
│ └── targets.json Gerät→Payload- und Gerät→KMI-Auflösungstabelle
├── exploit/ Eingebundene CVE-2026-43499-Payload-Quelle
│ ├── Makefile Per-Ziel-aarch64-NDK-Build
│ ├── src/ android15-6.6-Basissource-Set
│ │ ├── main.c slide.c fops.c pipe.c root.c preload.c util.c
│ │ ├── su_daemon.c Temporär-su-Helfer, von der Kette erzeugt
│ │ ├── kernelsnitch/ Futex-Hash-Belegungs-Seitenkanal-Header
│ │ └── targets/ Per Gerät+Build target.h (Kernel-Offsets)
│ └── src/61/ android14-6.1-Source-Set (slide61.c, TCP-Route)
├── lib/ Hostseitige gemeinsame Shell-/Helferfunktionen
├── scripts/
│ └── build-payloads.sh Baut und dedupliziert das Payload-Set
├── artifacts/
│ └── exploits/ Gebaute, deduplizierte Payload-.so-Dateien
└── docs/ Design- und Analyse-Notizen
scripts/build-payloads.sh kapselt das Per-Ziel-exploit/Makefile und emittiert das deduplizierte Payload-Set, das in data/targets.json benannt ist, nach artifacts/exploits/. Es baut eine .so pro eindeutiger Offset-Gruppe (aus dem build_from-Ziel dieser Gruppe) statt eine pro Gerät.
export ANDROID_NDK_HOME=/path/to/android-ndk # muss die aarch64-NDK-Toolchain enthalten
scripts/build-payloads.sh # baut jedes Payload in data/targets.json
Das Makefile wählt die aarch64-NDK-Clang-Toolchain aus ANDROID_NDK_HOME und kompiliert jeweils ein einzelnes Ziel; API (Standard 35) wählt den aarch64-linux-android<API>-clang-Treiber. Das Source-Set wird nach Kernel-Familie gewählt – android15-6.6-Ziele kompilieren das src/-Basisset, android14-6.1-Ziele kompilieren src/61/ – und die absoluten Kernel-Offsets jedes Ziels stammen aus src/targets/<codename>-<build>/target.h. Um ein einzelnes Ziel direkt zu bauen:
make -C exploit TARGET=husky-CP2A.260705.006
data/targets.json listet 19 Geräte-/Build-Einträge, die 18 Pixel-Modelle abdecken (bluejay erscheint auf zwei Firmware-Builds), gruppiert in 5 Kernel-Offset-Payloads. Die Auswahl erfolgt nach Kernel-Image, sodass Geräte, die ein vmlinux teilen, auf ein Payload deduplizieren; ein abweichendes Kernel-Image erhält sein eigenes.
Geräte auf dem gemeinsamen android14-6.1-a-Kernel-Image (6.1.157-android14-11-gbd23337e42e7-ab14791245) umfassen die Pixel-6/6-Pro/6a-, 7/7-Pro/7a-, 8/8-Pro- und 9/9-Pro/9-Pro-XL/9-Pro-Fold-Familien; android14-6.1-b und android14-6.1-akita isolieren Modelle auf demselben KMI, deren Kernel-Build oder Offsets abweichen; android15-6.6 deckt die Pixel-10-Familie ab.
NebuSec/CyberMeowfia-Repository unter Apache-2.0; Entdeckung NebuSecs VEGA-Tooling zugeschrieben, offengelegt am 2026-07-07.exploit/-Baum fügt android14-6.1- und android15-6.6-Ziel-Offsets und einen KernelSU-Late-Load-Daemon auf dem NebuSec-Exploit hinzu; er trägt keine separate Lizenz und erbt die Upstream-Apache-2.0-Bedingungen.ksud und das Manager-Autorisierungsmodell bereit, das dieses Tool nachlädt.Eingebundene Quelle unter exploit/ behält ihre Upstream-Lizenz (Apache-2.0 für den NebuSec-abgeleiteten Exploit). Dieses Projekt ist manager-agnostisch: Es lädt den Manager der jeweils installierten KernelSU-Variante nach und zielt nicht auf einen bestimmten Fork ab oder bündelt ihn.
ksud-Late-Load-Pfadinit_module-Lader und Treibererkennungapk_sign.rssk_buffpipe_bufferashmem_miscs[0].fops mit einem gefälschten file_operations, dessen jeder Slot auf eine echte, prototypkompatible Kernel-Funktion zeigt (configfs_bin_write_iter, configfs_read_iter, copy_splice_read, ashmem_ioctl, noop_llseek, …), sodass Forward-Edge-CFI erfüllt ist, während read/write/splice auf einem ashmem-fd einen eingeschränkten Kernel-Lese-/Schreibzugriff ergeben.pipe_buffer-Strukturen auf der geleakten Slab-Seite (page zeigt über die vmemmap↔Direct-Map-Konvertierung auf jedes Ziel, ops = anon_pipe_buf_ops, PIPE_BUF_FLAG_CAN_MERGE), sodass einfaches read()/write() auf der Pipe Bytes zu und von beliebigen Kernel-Adressen bewegt – ein stabiler beliebiger Kernel-Lese-/Schreibzugriff.uid=0/proc/sys/kernel/random/boot_id mit dem beim Erfassen aufgezeichneten Boot; jede Änderung verwirft die Basis und kehrt zu Phase A zurück. Eine äußere Schleife wiederholt Ableiten→Replay über Neustarts hinweg.adb shellumount| Payload | KMI | Gebaut aus | Geräte |
|---|
android14-6.1-a | android14-6.1 | bluejay-CP2A.260705.006 | oriole, raven, bluejay (CP2A), panther, cheetah, comet, shiba, husky, lynx, caiman |
android14-6.1-b | android14-6.1 | komodo-CP2A.260705.006 | komodo, tegu, tokay |
android14-6.1-akita | android14-6.1 | akita-CP2A.260805.005 | akita |
android14-6.1-cp1a | android14-6.1 | bluejay-CP1A.260405.005 | bluejay (CP1A) |
android15-6.6 | android15-6.6 | blazer-CP2A.260705.006 | frankel, blazer, mustang, rango |