
Exploitiert CVE-2026-43499 auf Android-GKI-6.12-Geräten: deterministisches beliebiges Kernel-Lesen/-Schreiben, KASLR-Bypass und vollständiger Root-Zugriff per LD_PRELOAD-Payload.
Ein Linux-Kernel-Exploit für die Android-GKI 6.12-Reihe (Samsung- und Pixel-Geräte), der CVE-2026-43499 ausnutzt: ein fehlerhaftes Rollback von remove_waiter() in rt_mutex_start_proxy_lock(), das current statt waiter::task verwendet, sodass das pi_blocked_on eines Waiters auf dessen (später per Stack-Pop entferntes) Kernel-Stack-rt_mutex_waiter zeigt. In Kombination mit einem pselect()-fd_set-Stack-Overwrite und einem durch sched_setattr eines Consumers gesteuerten Fake-rb_tree-Durchlauf ergibt das eine deterministische Kernel-Write-Primitive und vollen Root-Zugriff.
Der Exploit läuft als LD_PRELOAD-Shared-Library (preload.so) und installiert einen Root-su-Daemon sowie ein Hintergrundbild als Post-Root-Artefakte.
Status: aktive Entwicklung. Die Stack-Tiefen-Offsets pro Ziel (
PSELECT_*-Wortverschiebungen) und das Slide-Verhalten variieren je nach Gerät und werden derzeit noch auf dem Gerät verifiziert. Das m1q-Ziel (ZF1) ist der aktuelle Fokus der Inbetriebnahme.
Wenn FUTEX_CMP_REQUEUE_PI einen Waiter wiedereinreiht und der Deadlock-Erkennungs-Durchlauf der Kette des Requeues -EDEADLK zurückgibt, führt __rt_mutex_start_proxy_lock() ein Rollback über remove_waiter() (rtmutex.c:1535) aus. Das Rollback entfernt den Waiter korrekt aus dem Wait-Baum, löscht aber das pi_blocked_on des Requeue-Aufrufers statt waiter->task->pi_blocked_on. Das pi_blocked_on des WAITERS bleibt hängend auf dessen Stack-rt_mutex_waiter, das entfernt wird, sobald der Futex ausläuft.
Nach dem Timeout wird die Kernel-Stack-Region des Waiters wiederverwendet: core_sys_select() kopiert die drei fd_sets in diesen Stack-Puffer (der nfds < 344-Stack-Pfad auf ZF1). Ein präpariertes fd_set-Wortarray materialisiert ein Fake-rt_mutex_waiter / Fake-Task / Fake-rt_mutex neu (mit einer rb_tree-Wurzel, deren Zeiger kontrolliert werden). Ein Consumer-Thread ruft anschließend sched_setattr_tid(waiter) → rt_mutex_adjust_pi() → rb_erase_cached auf, was einen Schreibvorgang eines kontrollierten Werts an eine beliebige Adresse erzeugt.
Die gesamte Primitive erfordert den PI-Kettenzyklus: Der Owner hält f_pi_target und blockiert zugleich auf f_pi_chain (das vom Waiter gehalten wird), sodass der Ketten-Durchlauf Owner → Chain → Waiter → Target → Owner trifft und mit -EDEADLK fehlschlägt. Wird der Zyklus entfernt, liefert das Requeue Erfolg ohne jede Kernel-Auswirkung.
sched_blocked_reason (primär auf m1q): die gespeicherte Rücksprungadresse (PC) eines blockierten Kworkers (stack_trace_save_tsk) wird aus dem Ringpuffer gelesen und mit dem einkompilierten worker_thread-Offset verglichen.SLIDE_LOGGERS_0_1 über einen Linear-Map-Alias in den boot_id-Sysctl-Daten; der geleakte Wert rekonstruiert stext.mm_struct-große Objekte sprayen und per Futex-Hash-Kollision ein mm_struct auf dem Heap lokalisieren; dabei wird die Adresse einer Kernel-Heap-Seite geleakt, die als Spray-Basis für die Fake-Objekte dient.configfs_bin_write_iter in den write_iter-Slot (+0x30) der statischen .data-ashmem-file_operations. Das Öffnen von ashmem liefert anschließend über den configfs-Binär-FS-Pfad beliebiges Kernel-Lesen/-Schreiben.Optionale STAGE3=1-Phase: Sie forkt ein Bridge-Kind, tauscht dessen mm->pgd gegen eine gestufte Fake-Seitentabelle (3 Ebenen: PGD→L1→L2, RX-Loop-Blatt + RW-Stack-Blatt) und lässt das Kind ein reines Register-Assembly-Blob ausführen, das seine eigenen Creds über ein 2-MB-physikalisches Scan-Fenster patcht — physikalisches R/W im gesamten RAM ohne die configfs-/pipe-Primitiven. Derzeit nur in das m1q-Ziel eingebunden und noch nicht auf dem Gerät ausgeführt.
41 Ziele in src/targets/<codename>-<build>/, die jeweils mindestens eine target.h benötigen (Kernel-Symbol-Offsets, KIMAGE_TEXT_BASE, Direct-Map-Basis, Struct-Offsets). Pixel-Ziele (comet, tokay, tegu, caiman, komodo, frankel, mustang, rango, stallion, blazer) überschreiben die gemeinsamen Quellen; Samsung-Ziele (m1q-*) fügen gerätespezifische Logik hinzu.
make list-projects # full list
Die Kernel-Struct-Layouts unterscheiden sich je Gerät — die Offsets müssen für jedes Ziel gegen die tatsächliche Kernel-Binärdatei bzw. kallsyms verifiziert werden.
Ausgabe: build/<PROJECT>/bin/preload.so (Shared Library, geladen über LD_PRELOAD).
# Default project
CC=clang make
# Specific device target (default: blazer-CP2A.260605.012)
CC=clang make PROJECT=m1q-BP4A.251205.006
# Without CC=clang: uses NDK if found ($NDK_ROOT / $ANDROID_NDK_HOME /
# $ANDROID_NDK_ROOT), otherwise host clang + Android sysroot
make PROJECT=m1q-BP4A.251205.006
# Show build configuration
make info
# Clean
make clean
Der Build bettet eine PIE-su_daemon-Binärdatei (src/su_daemon.c, gebaut nach build/embed/su_daemon_aarch64_pie) sowie assets/wallpaper.webp über src/su_blob.S / src/wallpaper_blob.S in preload.so ein.
Übertrage die Build-Ausgaben nach /data/local/tmp und führe den Exploit dann unter LD_PRELOAD aus. Führe ihn bei einem Live-Bring-up-Ziel OHNE tee aus — tee puffert sein eigenes stdio und verliert bei einer Kernel-Panic das Log-Ende (der Exploit-Stdout ist ungepuffert; wer also direkt in eine Datei umleitet, erhält jede Zeile):
adb push build/m1q-BP4A.251205.006/bin/preload.so /data/local/tmp/preload.so
adb push build/embed/su_daemon_aarch64_pie /data/local/tmp/su_daemon_aarch64_pie
adb shell "chmod 755 /data/local/tmp/preload.so /data/local/tmp/su_daemon_aarch64_pie"
adb shell "LD_PRELOAD=/data/local/tmp/preload.so \
/data/local/tmp/su_daemon_aarch64_pie \
> /data/local/tmp/output.log 2>&1"
Im Erfolgsfall lauscht der Daemon auf /data/local/tmp/temp_su.sock, und su wird unter /apex/com.android.virt/bin installiert.
exploit/
Makefile
assets/ # wallpaper.webp (embedded post-root artifact)
src/
main.c # run_exploit() entry point (shared)
preload.c # LD_PRELOAD constructor, su/wallpaper install
util.c # addr translation, page prep, configfs helpers
pipe.c # pipe-based physical read/write
root.c # cred patching / task walk
slide.c # KASLR slide (tracefs + boot_id routes)
fops.c # pselect corruption + fops manipulation
faketables.c # stage-3 fake page-table builders (3-level)
stage3.c # pgd-swap bridge child (no-op unless configured)
stage3_loop.S, stage3_poll.S # bridge blob + enter trampoline
su_daemon.c # root daemon (compiled as separate PIE)
su_blob.S, wallpaper_blob.S # embedded binaries
offset.h # pulls per-target target.h via -DTARGET_CONFIG_H
kernelsnitch/ # mm_struct futex-hash leak library
targets/<codename>-<build>/ # per-device target.h + optional overrides
poc/poc.c # standalone POC (separate from the exploit)
Ziele überschreiben die gemeinsamen Quellen über den pick_src-Mechanismus des Makefiles: Existiert src/targets/<PROJECT>/<file>.c, wird diese Datei anstelle der gemeinsamen src/<file>.c verwendet; andernfalls kommt die gemeinsame Version zum Einsatz.
-EDEADLK (errno 35) fehlschlägt. Ein fehlender Zyklus zeigt sich als success=1 ohne jede Kernel-Auswirkung.PSELECT_WAITER_WORD_SHIFT muss 0 sein (ZF1-Stack-Tiefen anhand der ELF verifiziert). Andere Ziele verwenden standardmäßig weiterhin 1 — vorher pro Gerät verifizieren, bevor man ihnen vertraut.p0_data_alias() verwendet DIRECT_MAP_BASE (die lineare Map), die sich auf ZF1 von P0_PAGE_OFFSET unterscheidet. Daten-Aliase für dereferenzierte Kernel-Objekte müssen die lineare Map verwenden.pr_error() ist fatal (exit(-1)), anders als die Warn-/Info-Makros.SLIDE_LOGGERS_0_1, nicht SLIDE_NFULNL_LOGGER; das Wort ist ein nicht randomisierter Linear-Map-Alias, daher ist der boot_id-Weg slide-unabhängig (nur bei slide=0 verwenden — zuerst den tracefs-Leak ausführen).Dies ist Exploit-Code für Android-Geräte und für Sicherheitsforschung, autorisierte Schwachstellentests sowie Verteidigungsarbeit an Hardware gedacht, die dir gehört oder für die du die ausdrückliche Erlaubnis zum Testen hast. Die missbräuchliche Verwendung an Geräten, die dir nicht gehören, ist illegal. Der Autor übernimmt keine Verantwortung für gebrickte Geräte, gebrickte Kernel oder erloschene Garantien.
preload.c den eingebetteten su-Daemon (tmpfs-gemountet in /apex/com.android.virt/bin, zusätzlich adbd-Namespace- und lokale Varianten) und tauscht das Hintergrundbild aus.| Variable | Default | Purpose |
|---|
PSELECT_ROUTE_SHIFT | Kompilierzeit | A/B-fd_set-Wortverschiebung für den Hauptweg (m1q) |
SLIDE_SHIFT | Kompilierzeit | A/B-fd_set-Wortverschiebung für den Slide-Weg |
PSELECT_ROUTE_DELAY_USEC | 50000 | Verzögerung des Consumers vor sched_setattr (muss > 0 sein) |
SLIDE_CONSUME_USEC, SLIDE_ENTER_DELAY_USEC, SLIDE_CONSUMER_CORE | — | Timing/Pinning des Slide-Wegs |
SKIP_SLIDE | 0 | Direct-Map-Fallback verwenden (slide = 0) |
SLIDE_ONLY | 0 | Nur den KASLR-Slide ausführen und beenden |
SLIDE_P0_OFFSET, SLIDE_BOOTID_OFF | — | Slide-p0-Alias-/boot_id-Offsets überschreiben |
KSNITCH_COLLISIONS | 4 | KernelSnitch-Kollisionsanzahl |
STAGE3 | 0 | pgd-Swap-Bridge-Kind-Phase aktivieren (m1q) |
STAGE3_DRYRUN | 0 | Tabellen aufbauen/verifizieren und vor dem Swap abbrechen |
PAGE_RECLAIM_SENDS, PSELECT_SIMPLE_LAYOUT | — | Spray-/fd-set-Layout-Anpassungen |
TMP_PAGE_* / TMP_UNAME_* | — | tmp_page-Experimentierweg (m1q) |