
Port de recherche CVE-2026-98260 Android spécifique à la cible pour Xiaomi 17 (pudding)
Chaîne d'exploitation Android AArch64 pour une build de noyau précise du Xiaomi 17 (nom de code
pudding) : en partant d'un shell ADB ordinaire, elle place SELinux en mode Permissive, remet à zéro l'UID/GID du processus courant, complète l'ensemble des capabilities Linux et fournit un service root pour le cycle de démarrage en cours.
Ce projet est une référence et un portage de CVE-2026-98260-popsicle-android (Xiaomi 17 Pro Max / popsicle), et non une recherche indépendante implémentée à partir de zéro.
Un execve() sur un thread non principal exécute d'abord de_thread() pour échanger l'identité des threads. Si l'exec échoue ensuite (par exemple, exec_mmap() est interrompu par SIGKILL et renvoie -EINTR), la tâche se termine avant le nettoyage des timers POSIX : le k_itimer est libéré, mais son nœud timerqueue reste dans l'arbre de la tâche encore vivante. Lorsque le processus parent récupère ensuite cette tâche, cleanup_timerqueue() manipule à nouveau ce nœud déjà libéré, formant un use-after-free.
L'exploitation transforme ce UAF en une écriture nulle restreinte de huit octets, puis progresse en trois étapes : SELinux → UID/GID → capabilities complètes. Une explication complète destinée aux débutants est disponible dans docs/VULNERABILITY.md.
| Élément | Valeur mesurée |
|---|---|
| Appareil | Xiaomi 17, modèle 25113PN0EC, nom de code pudding |
| Système | Xiaomi HyperOS 4 OS4.0.5.0.XPCCNXM, Android 17 |
| Noyau | 6.12.69-android16-6-g43b5ab7e247d-abogki561813157-4k |
| Taille de page | 4 KiB |
| Point de départ | Shell ADB ordinaire autorisé, UID 2000, SELinux Enforcing, bootloader verrouillé |
| Résultat obtenu | UID/GID 0, SELinux Permissive, CapPrm/CapEff/CapBnd=000001ffffffffff |
| Cycle de vie | Le service root est disponible pour les nouveaux shells ADB ultérieurs de ce démarrage ; il faut le relancer après un redémarrage |
Vérification indépendante après succès (nouveau shell ADB) :
uid=0(root) gid=0(root)
CapInh: 000001ffffffffff
CapPrm: 000001ffffffffff
CapEff: 000001ffffffffff
CapBnd: 000001ffffffffff
getenforce: Permissive
Ce n'est pas un root persistant. Rien n'a été écrit dans boot/init_boot ni dans aucune partition, le bootloader reste verrouillé ; après redémarrage, SELinux revient en mode Enforcing et le service root disparaît.
Aucune garantie de taux de réussite. En pratique, sur 9 démarrages, une séquence complète A→B→C a été obtenue une fois. Les conditions de réussite des trois phases sont indépendantes les unes des autres :
| Phase | Programme côté appareil | Condition de réussite |
|---|---|---|
| 1. SELinux | exploit-android-perf | getenforce renvoie Permissive, les autres champs du mot d'état restent conformes aux attentes |
| 2. Identité | exploit-android-perf-chain | L'UID/GID du même processus devient 0, puis exec fullcap |
| 3. Permissions complètes | exploit-android-key-ptr + pc98260_rootd.c | Capacités complètes, le service root est accessible depuis un nouveau shell |
Si l'une des étapes échoue, [PARKED] est affiché et l'état dangereux est conservé ; un redémarrage est obligatoire, il ne faut pas forcer une nouvelle tentative lors de ce même démarrage.
--aio 16384 est la valeur de réglage de cette machine (la tentative réussie a touché la page de métadonnées perf n° 3903).
| Emplacement | popsicle | pudding |
|---|---|---|
LINK_SELINUX_STATE | 0xffffffc08289aba0 | 0xffffffc0827f9ba0 |
LINK_GETPID | 0xffffffc080074eac | 0xffffffc080102e10 |
| Fuite d'adresse du timer | sonde de retour de alloc_posix_timer | ce symbole étant inliné, on utilise l'entrée posix_cpu_timer_create $arg1 |
PERF_MAX | 8192 | 16384 (augmente les chances de toucher la page) |
Les fondements et les preuves figurent dans docs/ADAPTATION.md.
Un NDK Android est requis. Le répertoire bin/ du dépôt est compilé avec NDK 26.3.11579264 / API 34 :
powershell -NoProfile -ExecutionPolicy Bypass -File .\build.ps1 -NdkHome "$env:LOCALAPPDATA\Android\Sdk\ndk\26.3.11579264" -ApiLevel 34
-VerifyReference compare un à un les SHA-256 des nouveaux artefacts avec ceux de bin/ ; seuls des byte-identiques sont obtenus avec la même chaîne d'outils que bin/. Changer de version de NDK permet toujours l'exécution, mais les hachages différeront.
Artefacts :
| Artefact | Source d'entrée |
|---|---|
exploit-android-perf | src/exploit-android-perf.c |
exploit-android-perf-chain | src/exploit-android-perf-chain.c |
exploit-android-key-ptr | src/exploit-android-key-ptr.c + src/pc98260_rootd.c |
pc98260-su | src/pc98260_su.c |
La compilation ne se connecte pas au téléphone et n'écrit pas sur l'appareil.
Connectez un appareil avec le débogage USB activé et autorisé, puis ajoutez platform-tools au PATH :
.\开始98260临时ROOT.bat
ou spécifier explicitement l'appareil :
powershell -NoProfile -ExecutionPolicy Bypass -File .\run_98260.ps1 -Serial <ADB序列号> -Reboot
Le script hôte vérifie le modèle de l'appareil, la version incrémentale du système, la chaîne de noyau complète, le boot ID et les hachages des quatre binaires ; il met également /sys/kernel/tracing/tracing_on à 0 avant de lancer chaque phase (certaines versions de miuibpf dans les builds HyperOS le maintiennent à 1, ce qui fait que la chaîne d'exploitation refuse de s'exécuter). Lorsque [PARKED] apparaît, le script s'arrête et invite à redémarrer.
Une fois le succès obtenu, vous pouvez utiliser root comme suit :
adb shell -t /data/local/tmp/pc98260-su
adb shell /data/local/tmp/pc98260-su -c "id"
La chaîne d'exploitation exige de récupérer avec précision « la page où se trouve le minuteur cible ». Si la même page cible n'est pas observable, ou si la vérification après l'écriture du pointeur est incohérente, le programme conserve les zombie/mappages concernés et s'arrête, afin d'éviter une récupération aveugle alors que le nœud pendant est encore référencé par le noyau. Dans ce cas, un nouveau démarrage du téléphone doit servir de limite pour la tentative suivante.
Ce dépôt ne contient pas : de sauvegarde de l'appareil, d'images boot/init_boot, d'images de partition, de payload.bin, de noyau/ELF extraits du firmware, de journaux d'exécution bruts, de numéros de série ADB ni d'ID de démarrage. logs/ et build/ sont ignorés par Git. bin/ ne contient que les quatre binaires temporaires de la chaîne d'élévation de privilèges.