
Adaptateur GhostLock (CVE-2026-43499) pour le noyau Android 4.19.152-perf+
Exploit noyau pour les appareils OnePlus/OPPO/realme avec bootloader verrouillé. Obtient root + installation de KernelSU sans déverrouiller le bootloader ni modifier l'image de boot. Détection automatique au runtime de la version du noyau avec table d'offsets multi-appareils.
CVE-2026-43499 — Use-After-Free Futex PI (héritage de priorité)
Affecte le noyau Linux de 2.6.39 à 7.1. Corrigé dans mainline 7.1 (commit 3bfdc63936dd). Android GKI 6.12.x reste vulnérable.
L'appel système pselect6 copie les données fd_set sur la pile du noyau. Combiné au mécanisme de waiter futex PI, une frame de pile libérée peut être réutilisée comme structure rt_mutex_waiter. Le rééquilibrage de l'arbre rb pendant le parcours de la chaîne PI écrit ensuite des valeurs contrôlées à des adresses noyau arbitraires.
| Appareil | SoC | Noyau | Statut |
|---|---|---|---|
| OnePlus Ace 6T (PLR110) | SM8845 | 6.12.38-...-ab14275539 | Fonctionne |
| OnePlus Ace 6T (PLR110) | SM8845 | 6.12.38-...-ab14552068 | Fonctionne |
| OnePlus 15 (CPH2749) | SM8850 | 6.12.23-...-ab14541642 | Fonctionne |
| Xiaomi 17 (pudding) | SM8850 | 6.12.23-...-abogki463945075 | Fonctionne |
| Xiaomi 17 (pudding) | SM8850 | 6.12.69-...-abogki514973465 | Fonctionne (mise à jour d'août 2026) |
| OnePlus 13 (IN2060) | SM8750 | 6.6.89-...-abogki446052083 | Fonctionne (PSELECT_SHIFT=-2) |
| OPPO Pad 4 Pro | SM8750 | 6.6.89-...-ab14358676 | Fonctionne (PSELECT_SHIFT=-2) |
| Appareil | SoC | Noyau | Notes |
|---|---|---|---|
| OnePlus 15T (PLZ110) | SM8845 | 6.12.38-...-ab14552068 | Même noyau que l'Ace 6T. SP diff=-64 vérifiée via QEMU. |
L'overlay de pile pselect ne fonctionne que lorsque le rt_mutex_waiter libéré atterrit dans la région contrôlable par l'utilisateur du buffer stack_fds. L'endroit où atterrit le waiter est déterminé par la sortie du compilateur (PGO + LTO), pas par la version du noyau. Voir Configuration de pile pour plus de détails.
| Appareil | SoC | Noyau | Raison |
|---|---|---|---|
| OPPO Find X9 Ultra | SM8750 | 6.12.58-android16-6 | PGO élimine la frame do_futex → SP diff=+32, waiter word=14. Aucun shift sûr n'existe. |
| OPPO Find X7 | — | 6.1.157 | 6.1 GKI : waiter au word 13 (tous les appareils 6.1 OPLUS/GKI) |
| realme RMX5070 | SM6650 | 6.1.141 | 6.1 GKI : waiter au word 13 |
| realme RMX3852 | SM8635 | 6.1.141 | Même branche 6.1 que le RMX5070 |
| OnePlus 13R / Ace 5 | SM8650 | 6.1.x | Même branche 6.1 |
| OnePlus 12 | SM8650 | 6.1.141 | 6.1 GKI : do_futex inliné par PGO, waiter word=13/19 |
| OPPO Pad 5 (OPD2502) | MT6878 | 6.1.134 | Même branche 6.1 |
| OPPO PKW110 | — | 5.15.180 | Frame do_futex de 0x140 (4,5× la normale) → waiter word=-29, inaccessible |
| Motorola Edge 60 Fusion | MT6878 | 6.1.145 | 6.1 GKI : waiter au word 13 (non-OPLUS, même résultat) |
| iQOO Z9 5G | — | 5.15.178 | Frame do_futex trop grande, waiter inaccessible. Pas un appareil OPLUS (vivo). |
Deux chemins de root, sélectionnés automatiquement en fonction des capacités de l'appareil :
Nécessite off_ashmem_misc_fops != 0 (ashmem C avec miscdevice statique dans 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
Avantages par rapport au chemin B :
selinux_state.policycap (corrige les problèmes réseau sur OnePlus 13)Actuellement disponible sur : OnePlus 13 (noyau 6.6, ashmem C). Non disponible sur les appareils ashmem Rust (6.12 GKI) — le miscdevice est alloué sur le tas, l'adresse n'est pas prédictible au moment de la compilation.
Utilisé lorsque les offsets UMH ou les misc_fops ashmem C ne sont pas disponibles.
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
Avec NFDS=320, core_sys_select du noyau alloue un buffer stack_fds de 256 octets :
stack_fds: 0 5 10 14 | 15 20 25 29
├─in─┤─out─┤─ex──┤ ├res_in┤res_out┤res_ex┤
◄── USER CONTROLLED ──►│◄── KERNEL ZEROED ──►
L'exploit écrit des champs de waiter factices (task, lock) dans les bitmaps d'entrée fd_set. Pour que cela fonctionne, les champs task et lock du waiter doivent se trouver dans la zone contrôlable (words 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
Règle de faisabilité : word du waiter + 11 (offset de lock dans rt_waiter_node) doit être ≤ 14. Le word de waiter maximal réalisable est 3.
La position du waiter est déterminée par la disposition des frames de pile du compilateur (profils d'optimisation PGO + LTO + BOLT), qui varie selon la branche SoC. Une même version de noyau peut avoir des dispositions différentes sur des SoC différents.
Toutes les écritures noyau passent par l'alias de mappage linéaire de l'image :
data_addr(x) = PAGE_OFFSET + (kernel_phys_load - PHYS_OFFSET) + (x - KIMAGE_TEXT_BASE)
Le bootloader choisit kernel_phys_load, donc il varie selon le SoC et ne se trouve ni dans boot.img ni dans le DT. Champ par appareil dans struct kernel_offsets ; 0 = utiliser la valeur par défaut de target.h.
| SoC | kernel_phys_load |
|---|---|
| SM8845 (Ace 6T, 15T) | 0xa8000000 |
| SM8750 (OnePlus 13, OPPO Pad 4 Pro) | 0xa8000000 |
| SM8850 (OnePlus 15, Xiaomi 17) | 0xc7800000 |