Skip to content
KitploitKITPLOIT
OutilsExploitsBlog
Log in
Soumettre
OutilsExploitsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

FluxContactConfidentialité© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
Outils/GitHubGitHub/nanoturtle1145/cve-2026-98260-uaf
Sécurité AndroidEscalade de PrivilègesCriminalistique MémoireAnalyse des VulnérabilitésExploitationArticles et RechercheExploitation de Binaires
GitHub

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager
nanoturtle1145/cve-2026-98260-uaf

cve-2026-98260-uaf

Recherche d'exploitation de CVE-2026-98260 (UAF exec de posix-cpu-timers) : S9280 Android 14 GKI 6.1 — déclencheur d'UAF + primitive d'écriture de zéros + SELinux Permissive. Document de passation dans HANDOFF.md

Voir le dépôt
334il y a 3 joursPas encore vérifié

CVE-2026-98260 — PoC de déclenchement uniquement (UAF posix-cpu-timers)

Vulnérabilité

exec: Cleanup POSIX timers right after de_thread() (auteur Hyunwoo Kim, amont du 2026-09-11, Fixes: 55e8c8eb2c7b = depuis 5.7).

Un minuteur CLOCK_THREAD_CPUTIME_ID est attaché au thread non-leader E ; dans execve(), après l'échange de tid par de_thread(), la recherche de la tâche par PID pour le minuteur échoue et le nœud reste dans les posix_cputimers de E. Si mmap_read_lock_killable() de exec_mmap() renvoie -EINTR (échec de l'exec et le code de nettoyage se trouve plus loin), do_exit() → exit_itimers() libère le k_itimer mais le nœud n'est pas retiré ; lorsque le processus parent le récupère via wait4(), cleanup_timerqueue() → timerqueue_del() opère sur un objet déjà libéré → UAF.

État du correctif (vérification réelle du 2026-10-27 sur fs/exec.c de chaque branche stable)

Brancheposixtimer_execÉtat
linux-6.1.yabsentnon corrigé
linux-5.15.yabsentnon corrigé
linux-6.6.yprésentcorrigé (6.6.158+)
linux-6.12.yprésentcorrigé (6.12.112+)

→ Les cibles de ce projet (lignes 5.15 / 6.1, ainsi que les 6.6 antérieurs à 6.6.158) sont toutes dans la plage affectée.

Pourquoi ce déclenchement est « déterministe » et non un pari sur le timing

  1. Longue fenêtre de verrou en écriture : fragment_vmas() utilise memfd + MAP_FIXED pour découper l'espace d'adressage en --vmas (50000 par défaut) VMA non fusionnables (le vm_pgoff de chacune vaut 0, les pgoff d'adresses adjacentes ne sont pas contigus → vma_merge échoue). Le thread locker répète mprotect sur toute la plage, et le parcours de dizaines de milliers de VMA fait que le verrou en écriture mmap est détenu pendant des millisecondes.
  2. Point de synchronisation précis : le killer utilise tgkill(tgid, ancien tid, 0) renvoyant ESRCH pour déterminer que « de_thread() a terminé l'échange de tid », et n'envoie SIGKILL qu'à ce moment-là.
  3. Le thread victime se bloque nécessairement dans exec_mmap() sur mmap_read_lock_killable() (le verrou en lecture étant bloqué par le verrou en écriture), et le SIGKILL arrive quelques microsecondes plus tard → cet appel renvoie -EINTR → l'exec échoue. La fenêtre (ms) ≫ le temps de réaction (µs), donc cela ne dépend pas de la chance.

⚠️ À distinguer : le déclenchement est déterministe ; le passage de l'UAF à une écriture arbitraire/modification des cred dépend de l'orchestration de réutilisation du slab de posix_timers_cache (order, objects/slab, CPU partial, appartenance memcg), ce qui relève d'un travail d'ingénierie du tas mesurable et itératif, pas d'une course.

Compilation

export ANDROID_NDK_HOME=/home/nt/项目/samsung_root_research/06_工具链/android-ndk-r29
make            # 产物 trigger(aarch64 / Android 21+ / 仅依赖 libc、libdl)

Étapes de test

adb push trigger /data/local/tmp/
adb shell "chmod 755 /data/local/tmp/trigger"
adb shell "/data/local/tmp/trigger --check"        # 先看环境
adb shell "/data/local/tmp/trigger; echo exit=\$?"

Indicateurs de succès (un seul des trois suffit) :

  • Après l'affichage de [*] 开始触发, l'appareil redémarre / l'écran devient noir (le plus direct lorsque panic_on_oops=1)
  • Le processus est tué par un signal (exit=137/139), et le système reste ensuite en vie
  • dmesg (nécessite root) affiche une pile d'appels de cleanup_timerqueue / timerqueue_del / rb_erase / rb_next

En cas d'échec (non déclenché), le programme réessaie lui-même --attempts fois et affiche le résultat de wait4 à chaque tour, puis suggère en dernier lieu d'ajuster les paramètres : --vmas 120000 --lockers 4 --cpu 0.

Limitations connues

  • Un déclenchement réussi entraîne presque inévitablement un redémarrage de l'appareil (c'est la preuve du « succès ») ; avant le test, vérifiez qu'aucun travail n'est non enregistré.
  • Chez Samsung, panic_on_oops peut valoir 0 : dans ce cas la tâche est tuée et le noyau continue de tourner avec un rbtree corrompu ; il est recommandé d'enchaîner 2 à 3 exécutions pour observer l'apparition d'un panic ultérieur.
  • Ce PoC ne contient pas de code d'élévation de privilèges (pas de reclaim, pas de manipulation des cred) ; il sert uniquement à vérifier l'accessibilité et la déterminisme.

Références

  • Correctif amont https://github.com/torvalds/linux/commit/acb03d3881818581052924a9bbbe92b8741ed448
  • Chaîne complète Honor MagicPad3 Pro (6.12.38, avec uid-0 et amorçage KSU) https://github.com/woshigenm/CVE-2026-98260
  • Notes de portage vivo/iQOO 6.6.127 (orchestration du reclaim, paramètres SLUB) https://github.com/janewas/cve-2026-98260-vivo-gki-research

Journal de tests réels (SM-S9280 / e3q / 6.1.162 / Android 17 / correctif de sécurité 2026-09-05)

Environnement de test : S9280ZHU6EZIF, noyau 6.1.162-android14-11-34343818-abS9280ZHU6EZIF (construit le 2026-09-20), non rooté, domaine shell (uid 2000), SELinux Enforcing.

Conclusion : le déclenchement est déterministe (pas de pari sur le timing)

TourObservationSignification
Chaque tourLe processus parent tgkill(child_tgid, etid, 0) renvoie ESRCHde_thread() a terminé l'échange de tid
Chaque tourPuis kill(child_tgid, SIGKILL), le processus enfant renvoie rc=-9exec a été interrompu (et non rc=0 d'un exec réussi)
Contrôle --no-killerL'exec de l'état armed jusqu'à la fin prend 277 ms (287 ms avec killer)Le verrou d'écriture mmap bloque bien le mmap_read_lock_killable() de exec_mmap()

5 tours sur 5 ont tous donné « ESRCH → SIGKILL → échec de l'exec », aucun exec n'a réussi. Cela prouve que la fenêtre d'interruption de l'exec après de_thread est contrôlable et stable ; la fenêtre provient du mprotect du locker (60 000 VMA, 200–700 ms par appel), la réaction du processus parent étant de l'ordre de la µs.

Deux pièges clés rencontrés (tous deux déjà corrigés dans le code)

  1. Le killer doit être dans le processus parent : de_thread() appelle zap_other_threads() qui tue tous les autres threads du même processus ; un killer placé dans le processus enfant mourrait lui-même au moment de de_thread, et ne verrait jamais ESRCH (ce qui se traduit par un exec qui réussit à chaque fois).
  2. Moment du reclaim : un spray immédiatement après SIGKILL est inefficace — à ce moment l'enfant n'est pas encore mort et k_itimer n'a pas encore été libéré par RCU. L'ordre correct est waitid(WNOWAIT) attendre le zombie → attendre la libération par le callback RCU → récupérer la page → enfin wait4 déclencher le chemin de reclaim.

Pas encore réalisé : rendre l'UAF visible

Actuellement, l'étape « le nœud reste dans la file après libération » est atteinte de manière stable, mais le rb_erase au moment de wait4 ne provoque pas d'oops — cela indique que la page slab victime n'a pas encore été récupérée par notre spray (le partial slab de posix_timers_cache est encore en cache, la page n'est pas retournée au buddy). Cela relève de l'orchestration du reclaim de l'exploit, non d'un problème de course ; il faut procéder comme dans les notes vivo : d'abord saturer/vider le slab avec k_itimer lui-même pour provoquer le retour de la page entière, puis utiliser un porteur order-0 (AIO ring, etc.) pour récupérer précisément la demi-page issue du split.

Suggestion pour la suite : prendre cette machine comme référence et effectuer un reclaim ciblé selon l'approche de docs/08-reclaim-choreography.md (notes vivo) ; ou vérifier avec un noyau auto-compilé avec KASAN si l'UAF se produit réellement.


Faisabilité du portage de la chaîne complète (seuils mesurés sur SM-S9280 / 6.1.162)

La chaîne publique (Honor uid0.c) dépend des événements kmem de tracefs pour deux choses essentielles :

Télécharger l’outil