
GhostLock (CVE-2026-43499) Adapter für den Android-Kernel 4.19.152-perf+
Kernel-Exploit für OnePlus/OPPO/realme-Geräte mit gesperrtem Bootloader. Ermöglicht Root + KernelSU-Installation, ohne den Bootloader zu entsperren oder das Boot-Image zu verändern. Laufzeit-Autoerkennung der Kernelversion mit geräteübergreifender Offset-Tabelle.
CVE-2026-43499 — Futex-PI-Use-After-Free (Priority Inheritance)
Betrifft Linux-Kernel 2.6.39 bis 7.1. Behoben in Mainline 7.1 (Commit 3bfdc63936dd). Android GKI 6.12.x bleibt verwundbar.
Der Syscall pselect6 kopiert fd_set-Daten auf den Kernel-Stack. In Kombination mit dem Futex-PI-Waiter-Mechanismus kann ein freigegebener Stack-Frame als rt_mutex_waiter-Struktur wiederverwendet werden. Die rb-Tree-Rebalancierung während des PI-Chain-Walks schreibt daraufhin kontrollierte Werte an beliebige Kernel-Adressen.
| Gerät | SoC | Kernel | Status |
|---|---|---|---|
| OnePlus Ace 6T (PLR110) | SM8845 | 6.12.38-...-ab14275539 | Funktioniert |
| OnePlus Ace 6T (PLR110) | SM8845 | 6.12.38-...-ab14552068 | Funktioniert |
| OnePlus 15 (CPH2749) | SM8850 | 6.12.23-...-ab14541642 | Funktioniert |
| Xiaomi 17 (pudding) | SM8850 | 6.12.23-...-abogki463945075 | Funktioniert |
| Xiaomi 17 (pudding) | SM8850 | 6.12.69-...-abogki514973465 | Funktioniert (Update August 2026) |
| OnePlus 13 (IN2060) | SM8750 | 6.6.89-...-abogki446052083 | Funktioniert (PSELECT_SHIFT=-2) |
| OPPO Pad 4 Pro | SM8750 | 6.6.89-...-ab14358676 | Funktioniert (PSELECT_SHIFT=-2) |
| Gerät | SoC | Kernel | Hinweise |
|---|---|---|---|
| OnePlus 15T (PLZ110) | SM8845 | 6.12.38-...-ab14552068 | Gleicher Kernel wie Ace 6T. QEMU-verifiziert, SP-Differenz=-64. |
Das pselect-Stack-Overlay funktioniert nur, wenn der freigegebene rt_mutex_waiter in den benutzerkontrollierbaren Bereich des stack_fds-Puffers fällt. Wo der Waiter landet, bestimmt die Compiler-Ausgabe (PGO + LTO), nicht die Kernelversion. Details siehe Stack-Layout.
| Gerät | SoC | Kernel | Grund |
|---|---|---|---|
| OPPO Find X9 Ultra | SM8750 | 6.12.58-android16-6 | PGO eliminiert den do_futex-Frame → SP-Differenz=+32, Waiter-Wort=14. Es existiert kein sicherer Shift. |
| OPPO Find X7 | — | 6.1.157 | 6.1-GKI: Waiter bei Wort 13 (alle 6.1-OPLUS/GKI-Geräte) |
| realme RMX5070 | SM6650 | 6.1.141 | 6.1-GKI: Waiter bei Wort 13 |
| realme RMX3852 | SM8635 | 6.1.141 | Gleicher 6.1-Zweig wie RMX5070 |
| OnePlus 13R / Ace 5 | SM8650 | 6.1.x | Gleicher 6.1-Zweig |
| OnePlus 12 | SM8650 | 6.1.141 | 6.1-GKI: do_futex per PGO inline, Waiter-Wort=13/19 |
| OPPO Pad 5 (OPD2502) | MT6878 | 6.1.134 | Gleicher 6.1-Zweig |
| OPPO PKW110 | — | 5.15.180 | do_futex-Frame 0x140 (4,5x normal) → Waiter-Wort=-29, unerreichbar |
| Motorola Edge 60 Fusion | MT6878 | 6.1.145 | 6.1-GKI: Waiter bei Wort 13 (nicht-OPLUS, gleiches Ergebnis) |
| iQOO Z9 5G | — | 5.15.178 | do_futex-Frame zu groß, Waiter unerreichbar. Kein OPLUS-Gerät (vivo). |
Zwei Root-Pfade, automatisch basierend auf den Gerätefähigkeiten ausgewählt:
Erfordert off_ashmem_misc_fops != 0 (C-ashmem mit statischem miscdevice im BSS).
PI write (mode=4) → redirect miscdevice fops to fake fops (via W0 pi_tree)
configfs r/w established
→ pipe physrw (1-byte precise kernel r/w)
→ SELinux enforcing = 0 (single byte, no policycap corruption)
→ UMH: inject work_struct into system_unbound_wq
kernel executes /data/local/tmp/a/e --umh as UID 0
→ root script → ksud late-load → KSU installed
Vorteile gegenüber Pfad B:
selinux_state.policycap nicht (behebt Netzwerkprobleme auf OnePlus 13)Derzeit verfügbar auf: OnePlus 13 (Kernel 6.6, C-ashmem). Nicht verfügbar auf Rust-ashmem-Geräten (6.12-GKI) — das miscdevice wird auf dem Heap allokiert, die Adresse ist zur Kompilierzeit nicht vorhersagbar.
Wird verwendet, wenn UMH-Offsets oder C-ashmem-misc_fops nicht verfügbar sind.
Write 1 (mode=1) → SELinux enforcing = 0
(low byte of kernel ptr = 0x00, 8-byte write)
Write 2 (mode=2) → task->cred = init_cred
(uid=0, all capabilities)
Root shell → ksud late-load (KernelSU LKM)
→ su -c load_policy (fix SELinux policycap)
→ dynamic manager registration
App (seccomp) → Write 1 (no perf needed)
→ mini-adb connect TCP (port from /data/local/tmp/a/adb_port, default 5555)
→ adb shell: full exploit (perf works, no seccomp)
→ root → KSU → network fix
BOOT_COMPLETED → BootCompletedReceiver
├─ su available → skip (soft reboot / already rooted)
└─ no root → GhostlockService → setsid exploit --bootstrap
Mit NFDS=320 allokiert core_sys_select des Kernels einen 256-Byte-stack_fds-Puffer:
stack_fds: 0 5 10 14 | 15 20 25 29
├─in─┤─out─┤─ex──┤ ├res_in┤res_out┤res_ex┤
◄── USER CONTROLLED ──►│◄── KERNEL ZEROED ──►
Der Exploit schreibt gefälschte Waiter-Felder (task, lock) in die fd_set-Eingabe-Bitmaps. Damit das funktioniert, müssen die task- und lock-Felder des Waiters in die kontrollierbare Zone fallen (Wörter 0–14).
Ace 6T ✅ (waiter at word 2):
░░████████████████░░│░░░░░░░░░░░░░░░░░░
▲waiter t l │
task/lock controllable
RMX5070 ❌ (waiter at word 13):
░░░░░░░░░░░░░████│██████████████░░░░░░
▲ │ t l
waiter task/lock ZEROED
Machbarkeitsregel: Waiter-Wort + 11 (Lock-Offset in rt_waiter_node) muss ≤ 14 sein. Das maximal mögliche Waiter-Wort ist 3.
Die Position des Waiters wird durch das Stack-Frame-Layout des Compilers bestimmt (PGO- + LTO- + BOLT-Optimierungsprofile) und variiert je nach SoC-Zweig. Dieselbe Kernelversion kann auf verschiedenen SoCs unterschiedliche Layouts haben.
Alle Kernel-Schreibzugriffe laufen über den Linear-Map-Alias des Images:
data_addr(x) = PAGE_OFFSET + (kernel_phys_load - PHYS_OFFSET) + (x - KIMAGE_TEXT_BASE)
Der Bootloader bestimmt kernel_phys_load, daher variiert es pro SoC und ist weder in boot.img noch im DT enthalten. Gerätespezifisches Feld in struct kernel_offsets; 0 = den target.h-Standardwert verwenden.
| SoC | kernel_phys_load |
|---|---|
| SM8845 (Ace 6T, 15T) | 0xa8000000 |
| SM8750 (OnePlus 13, OPPO Pad 4 Pro) | 0xa8000000 |
| SM8850 (OnePlus 15, Xiaomi 17) | 0xc7800000 |
Ein falscher Wert schlägt still fehl — der Schreibzugriff landet weiterhin im gemappten RAM, daher gibt es weder einen Absturz noch eine Wirkung. Verwechsle das nicht mit einem PSELECT_SHIFT-Problem. Lies den Wert auf einem gerooteten Gerät desselben Modells aus (Kernel code beginnt bei _stext; _text liegt 0x10000 tiefer):
su -c 'grep -i "Kernel code" /proc/iomem' # c7810000-... -> 0xc7800000