
Adaptateur d'exploit CVE-2026-43499 pour MT6985 MediaTek Dimensity 9300 (vivo PD2241)
Conclusion : 68M tokens dépensés, pas de root obtenu.
Raison : l'attaque temporelle KernelSnitch n'est pas fiable sur MTK Dimensity 9200,CONFIG_PANIC_ON_OOPS=yne laisse aucune marge d'erreur.
Cet article documente l'intégralité du processus d'échec, pour référence et pour éviter ces pièges.
| Projet | Valeur |
|---|---|
| Appareil | vivo PD2241 (Dimensity 9200 / MT6985), Android 15 |
| Firmware | PD2241_A_15.2.10.2.W10.V000L1 |
| Noyau | 5.15.178-android13-8-gfb31f5bdd612-dirty |
| Bootloader | Verrouillé (ro.boot.flash.locked=1) |
| SELinux | Enforcing |
| panic_on_oops | Activé → tout OOPS du noyau = redémarrage immédiat |
| Exploit | CyberMeowfia — CVE-2026-43499 (IonStack) |
| Arborescence source | android_15.0_kernel_MT6985 (5.15.178) — ne correspond pas à la version de l'appareil (source = GKI android15, appareil = GKI android13) |
Extraction depuis arch/arm64/include/asm/memory.h + include/linux/fs.h + android/abi_gki_aarch64.xml :
KIMAGE_TEXT_BASE = 0xffffffc008000000, VA_BITS=39, DIRECT_MAP=256GBlayout-offset-in-bits)iopoll (GKI android13), IOCTL=0x48, open=0x68atomic_t usage = 4 octets, uid=0x04slab_cache=0x18Découverte clé : la source est GKI android15, l'appareil est GKI android13 — les offsets de task_struct diffèrent de 0x40~0x88 octets, impossible de copier directement depuis la source.
Zip OTA (8,3 Go)
→ payload.bin (8,2 Go)
→ payload_dumper → boot.img (96 Mo, en-tête v4)
→ décompression LZ4 → Image (50 Mo ARM64)
→ kallsyms-finder → 187810 symboles
Symboles extraits de deux versions du firmware (15.2.7.6 / 15.2.10.2) — le même symbole diffère de 10 Ko~200 Ko entre les deux versions, il faut utiliser la bonne version.
# Dans rt_mutex_adjust_pi :
LDR x21, [x19, #0x8b0] → pi_blocked_on = 0x8b0 (valeur android15, pas frankel)
Cela confirme que la disposition de task_struct est la branche android15, pas l'android13 de frankel.
NDK r29, make PROJECT=android_15.0_kernel_MT6985 → preload.so (150 Ko).
[+] preload starting pid=25414
[+] p0 profile ... tous les symboles chargés correctement
[-] KernelSnitch mm_struct leak failed ← parfois absent (succès occasionnel)
[+] slide child context route=pselect ← processus enfant de fuite KASLR slide démarré
[Panic du noyau] ← rt_mutex_adjust_prio_chain+0x1b0
Instruction du crash (capstone) :
ldar w8, [x27] ; x27 = waiter->lock (chargé depuis [x28, #0x38])
; la valeur de x27 est invalide → aucune entrée de table de pages → défaut de traduction
; → die() → panic → redémarrage
KernelSnitch est le point d'entrée de tout l'exploit — il fuite l'adresse de mm_struct via la différence temporelle des buckets de hachage futex :
MT6985 a CONFIG_KASAN_HW_TAGS=y → le noyau marque les allocations slab avec des tags MTE
le pointeur de mm_struct porte un tag KASAN → futex_hash est calculé sur le pointeur taggé
mais le scan bruteforce de la direct map (adresses non taggées) → le hash calculé ne correspond pas
même en itérant sur les tags MTE (0-14, 15 combinaisons), sur un système VA_BITS=39
les bits de tag (bit56-59) chevauchent les bits d'extension de signe → certaines combinaisons de tags
produisent des adresses invalides → non détectées
Les appareils Pixel n'ont pas KASAN_HW_TAGS, ce mécanisme fonctionne. Pas sur MTK.
CONFIG_PANIC_ON_OOPS est le tueurPixel : OOPS du noyau → dump_stack → continue → l'exploit peut réessayer
MT6985 : OOPS du noyau → die() → panic() → redémarrage immédiat → aucune marge d'erreur
Toute déviation légère dans la chaîne π fait tout planter ; sur Pixel, une déviation signifie juste
"pas réussi cette fois, on réessaie avec un autre ensemble d'adresses".
Et le bootloader est verrouillé (flash.locked=1) → impossible de flasher un noyau personnalisé pour désactiver cette option.
Arborescence source : 5.15.178 GKI android15
Appareil : 5.15.178-android13 (vendor vivo)
Bien que le numéro de version majeur soit identique (5.15.178), les branches GKI diffèrent (android13 vs android15), donc les dispositions des structures clés comme task_struct/cred ne correspondent pas. Après avoir basculé à plusieurs reprises entre les deux ensembles d'offsets frankel/android15, seul le désassemblage a permis de trancher.
| Modification | Objectif | Résultat |
|---|---|---|
THRESHOLD_MULT 10→5→3 | Réduire le seuil de détection de collision | <5 trop de faux positifs |
APPENDED_FUTEXES 4096→8192 | Augmenter la différence de chaîne de hachage | Aucun effet |
REPEAT_MEASUREMENT/AVERAGE | Augmenter la précision d'échantillonnage | Aucun effet |
MTE=1 | Faire itérer le bruteforce sur les tags | Plus lent, moins de crashs en revanche |
MM_STRUCT_SZ 0x500→0x400 | Corriger le pas de mm_struct | Nécessaire, l'ABI fait réellement 992 octets |
IDENTITY_END 64 Go→256 Go | Élargir la zone de scan | Trop lent (itération MTE), toujours pas de correspondance |
| Offsets TASK : android15↔frankel | Verrouiller le bon offset | Désassemblage confirme android15 |
| Offsets FOPS : android15↔frankel | android13 n'a pas d'iopoll | Utiliser frankel |
Dans exploit/targets/android_15.0_kernel_MT6985/target.h :
| Catégorie | Fiabilité | Méthode de vérification |
|---|---|---|
| Disposition mémoire | Correcte | Calcul memory.h + vérification kallsyms _text |
| Offsets de symboles (22) | Corrects | Extrait de boot.img 15.2.10.2 |
| Offsets task_struct | Corrects | ABI XML + désassemblage capstone (pi_blocked_on=0x8b0) |
| Offsets FOPS | Erronés (corrigés le 2026-07-31) | Valeur d'origine copiée de frankel (android13 sans iopoll) ; l'ABI de l'arborescence source a en réalité iopoll@0x30, ioctl=0x50, open=0x70 — voir VERIFICATION.md |
| Offsets CRED | Corrects (vérifiés) | ABI XML : uid=0x04, securebits=0x24, caps=0x28, security=0x78 |
L'assemblage passe, ça tourne, mais on perd sur le dernier kilomètre.
CONFIG_PANIC_ON_OOPS — soit flasher un noyau personnalisé (nécessite de déverrouiller le bootloader), soit trouver un appareil MT6985 où cette option est désactivée par défaut