Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
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é.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2026-43499-S26 — Exploit du noyau Android GKI 6.12 pour CVE-2026-43499, enchaînant un bug de rollback rt_mutex avec un écrasement de pile pselect pour obtenir les droits root sur les appareils Samsung et Pixel. | Kitploit
Outils/GitHubGitHub/sammyenigma/cve-2026-43499-s26
Sécurité AndroidEscalade de PrivilègesCriminalistique MémoireMécanismes de PersistanceExploitationRétro-ingénierieShellcodePost-ExploitationSécurité Mobile

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
Développement de Charges Utiles
Exploitation de Binaires
GitHubsammyenigma/cve-2026-43499-s26

CVE-2026-43499-S26

Exploit du noyau Android GKI 6.12 pour CVE-2026-43499, enchaînant un bug de rollback rt_mutex avec un écrasement de pile pselect pour obtenir les droits root sur les appareils Samsung et Pixel.

Voir le dépôt
1il y a 22 joursPas encore vérifié

CVE-2026-43499 — Exploit du noyau Android GKI 6.12 pselect / configfs

Un exploit du noyau Linux pour la branche Android GKI 6.12 (appareils Samsung et Pixel) ciblant CVE-2026-43499 : un rollback défectueux de remove_waiter() dans rt_mutex_start_proxy_lock() qui utilise current au lieu de waiter::task, laissant le pi_blocked_on d'un waiter pointer vers sa pile noyau (ensuite dépilée) rt_mutex_waiter. Combiné à un écrasement de pile fd_set via pselect() et à un parcours rb_tree factice piloté par sched_setattr côté consommateur, cela produit une primitive d'écriture noyau déterministe et un root complet.

L'exploit s'exécute en tant que bibliothèque partagée LD_PRELOAD (preload.so) et installe un démon su root ainsi qu'un fond d'écran comme artefacts post-root.

Statut : développement actif. La cible m1q (ZF1) est le point focal du bring-up. Le bootstrap pipei tmp_page-uname est la route active actuelle : le parcours est prouvé propre sur l'appareil (build #33 : régions rt_mutex seedées par enfant) et le balayage complet des 168 candidats est en cours. La route configfs CFI est une impasse sur m1q (ashmem Rust — aucun slot fops injectable) et reste la route pour les cibles C-ashmem. Les offsets par cible varient selon l'appareil — vérifiez-les par rapport au binaire noyau réel avant de leur faire confiance.

Primitive vulnérable (CVE-2026-43499)

Lorsque FUTEX_CMP_REQUEUE_PI remet un waiter en file et que le parcours de détection d'interblocage de la requeue renvoie -EDEADLK, __rt_mutex_start_proxy_lock() effectue un rollback via remove_waiter() (rtmutex.c:1535). Le rollback retire correctement le waiter de l'arbre d'attente mais efface le pi_blocked_on de l'appelant de la requeue au lieu de waiter->task->pi_blocked_on. Le pi_blocked_on du WAITER reste donc pendant sur son rt_mutex_waiter de pile, qui est dépilé une fois le futex expiré.

Après le timeout, la région de pile noyau du waiter est réutilisée : core_sys_select() copie les trois fd_sets dans ce tampon de pile (le chemin de pile nfds < 344 sur ZF1). Un tableau de mots fd_set forgé re-matérialise un faux rt_mutex_waiter / fausse task / faux rt_mutex (avec une racine rb_tree dont les pointeurs sont contrôlés). Un thread consommateur appelle alors sched_setattr_tid(waiter) → rt_mutex_adjust_pi() → rb_erase_cached, ce qui produit une écriture à une adresse arbitraire d'une valeur contrôlée.

Toute la primitive nécessite le cycle de chaîne PI : le propriétaire détient f_pi_target et bloque également sur f_pi_chain (détenu par le waiter), de sorte que le parcours de chaîne atteint owner → chain → waiter → target → owner et échoue avec -EDEADLK. Supprimer le cycle fait que la requeue renvoie un succès sans aucun effet noyau.

Chaîne d'exploitation

  1. Slide (KASLR) — fuite de la base du noyau. Deux routes :
    • tracefs sched_blocked_reason (m1q principal, prouvé sur l'appareil) : le PC de retour sauvegardé d'un kworker bloqué (stack_trace_save_tsk) est lu depuis le ring buffer et comparé à l'offset worker_thread compilé en dur. S'exécute avant que les mots de la route boot_id ne soient utilisés, afin que les cibles d'écriture d'alias de données (data_addr() = alias p0 + slide_p0_offset) restent correctes à un slide non nul.
    • route boot_id pselect (repli, indépendante du slide) : une écriture pselect plante SLIDE_LOGGERS_0_1 dans les données sysctl boot_id via un alias de linear-map ; la valeur fuitée reconstruit stext. Mise en pause sur m1q : sa cible W1 (SLIDE_RANDOM_BOOT_ID_DATA_OFF, RAM physique basse) a une écriturabilité dépendante de la page — le slide déplace la cible à travers les frontières de page à chaque boot, donc ~6/7 exécutions plantent. Exercée uniquement explicitement via SLIDE_FORCE_BOOTID (test de mécanisme) avec tree_pc/tree_left redirigés vers la page sprayée.
  2. KernelSnitch — spray d'objets de la taille d'un mm_struct et utilisation d'une collision de hash de futex pour localiser un sur le tas, fuitant une adresse de page de tas noyau utilisée comme base de spray d'objets factices. Les waiters sont détachés (non joints) au nettoyage pour éviter l'OOM de pile noyau qui a fait planter les builds avant #26.

Forme du parcours (m1q, prouvé sur l'appareil)

Les mots fd_set de pselect re-matérialisent un faux rt_mutex_waiter sur la pile noyau du waiter ; le sched_setattr_tid(waiter) d'un thread consommateur le parcourt via rt_mutex_adjust_pi(). Carte des mots ZF1 (vérifiée par désassemblage) : PSELECT_WAITER_WORD_SHIFT = 0, mot 12 = task (@+0x50), mot 13 = lock (@+0x58), mot 14 = wake_state (@+0x60 = 3). Avec un faux rt_mutex seedé, le parcours se termine proprement : les rb_erase W1 *(tree_left)=tree_pc / W2 *(tree_pc&~3+8)=tree_left de [7] sont les seules écritures noyau ; [11] (setprio / dequeue_pi) et le wake [9] sont tous deux ignorés (le faux waiter local à prio 100 garde le nœud de pile hors du top-waiter). Chaque complétion entrant dans le parcours depuis le build #19 survit sur l'appareil ; les plantages déterministes antérieurs étaient une erreur de placement de payload de 8 octets (SKB_DATA_DELTA), pas une faute du corps du parcours.

Étape 3 (pont pgd-swap, statique/build uniquement)

Phase optionnelle STAGE3=1 qui fork un enfant pont, échange son mm->pgd contre une table de pages factice préparée (3 niveaux : PGD→L1→L2, feuille boucle RX + feuille pile RW), et laisse l'enfant exécuter un blob assembleur registres uniquement qui patche son propre cred via une fenêtre de scan physique de 2 Mo — phys R/W sur toute la RAM sans les primitives configfs/pipe. Actuellement câblée uniquement dans la cible m1q et n'a pas été exécutée sur l'appareil.

Cibles supportées

41 cibles dans src/targets/<codename>-<build>/, chacune nécessitant au minimum un target.h (offsets de symboles noyau, KIMAGE_TEXT_BASE, base de direct-map, offsets de struct). Les cibles Pixel (comet, tokay, tegu, caiman, komodo, frankel, mustang, rango, stallion, blazer) surchargent les sources partagées ; les cibles Samsung (m1q-*) ajoutent une logique spécifique à l'appareil.

root@kitploit:~
make list-projects   # liste complète

Les dispositions des structs noyau diffèrent selon l'appareil — les offsets doivent être vérifiés par rapport au binaire noyau réel / kallsyms pour chaque cible.

Build

Sortie : build/<PROJECT>/bin/preload.so (bibliothèque partagée chargée via LD_PRELOAD).

root@kitploit:~
# Projet par défaut
CC=clang make

# Cible d'appareil spécifique (défaut : blazer-CP2A.260605.012)
CC=clang make PROJECT=m1q-BP4A.251205.006

# Sans CC=clang : utilise le NDK si trouvé ($NDK_ROOT / $ANDROID_NDK_HOME /
# $ANDROID_NDK_ROOT), sinon clang hôte + sysroot Android
make PROJECT=m1q-BP4A.251205.006

# Afficher la configuration de build
make info

# Nettoyer
make clean

Le build embarque un binaire PIE su_daemon (src/su_daemon.c, compilé vers build/embed/su_daemon_aarch64_pie) et assets/wallpaper.webp dans preload.so via src/su_blob.S / src/wallpaper_blob.S.

Exécution sur l'appareil

Poussez les sorties de build vers /data/local/tmp, puis exécutez l'exploit sous LD_PRELOAD. Exécutez SANS tee sur une cible de bring-up active — tee bufferise son propre stdio et perd la fin du log lors d'un panic noyau (la sortie stdout de l'exploit est non bufferisée, donc rediriger directement vers un fichier préserve chaque ligne) :

root@kitploit:~
adb push build/m1q-BP4A.251205.006/bin/preload.so /data/local/tmp/preload.so
adb push build/embed/su_daemon_aarch64_pie /data/local/tmp/su_daemon_aarch64_pie
adb shell "chmod 755 /data/local/tmp/preload.so /data/local/tmp/su_daemon_aarch64_pie"

adb shell "LD_PRELOAD=/data/local/tmp/preload.so \
  /data/local/tmp/su_daemon_aarch64_pie \
  > /data/local/tmp/output.log 2>&1"

En cas de succès, le démon écoute sur /data/local/tmp/temp_su.sock et su est installé sous /apex/com.android.virt/bin.

Balayage pipei m1q (bring-up actuel)

tools/run_pipei.sh pousse le binaire, le vérifie par sha256 sur l'appareil (un preload.so obsolète produit des verdicts silencieusement illisibles — l'échelle a déjà exécuté un build < #22 dont le format de log diffère), puis exécute le balayage complet des 168 candidats découpé en 3x56 afin qu'un panic ne perde au plus qu'un chunk (~10 min). Le stdout de l'exploit streame directement via adb (pas de redirection sur l'appareil — un tee côté appareil perd la fin en cas de panic) ; une copie côté hôte de chaque chunk est conservée à /tmp/m1q_pipei_chunk{1..3}_stream.log :

root@kitploit:~
sh tools/run_pipei.sh

PASS = n'importe quel chunk affiche tmp_page uname changed + after uname='CatOS. PIPEI_CHILD_REGIONS doit être défini au moins à la taille du chunk, sinon les régions rt_mutex par enfant restent non seedées et le balayage plante au candidat 0. La couverture complète nécessite également TMP_UNAME_PIPEI_SPLIT_ORDER_PAGES=1 — sans cela, seule une page du bloc mm libéré de 8 pages est balayée. run_pipei_slim.sh est la variante diagnostique à candidat unique ; run_m1q_ladder.sh est la batterie parcours/isolation (oracle B16-2, mécanisme de slide, étapes de balayage).

Configuration d'exécution (variables d'environnement)

Disposition du projet

root@kitploit:~
exploit/
  Makefile
  assets/                     # wallpaper.webp (artefact post-root embarqué)
  src/
    main.c                    # point d'entrée run_exploit() (partagé)
    preload.c                 # constructeur LD_PRELOAD, installation su/wallpaper
    util.c                    # traduction d'adresses, préparation de page, helpers configfs
    pipe.c                    # lecture/écriture physique basée sur pipe
    root.c                    # patch de cred / parcours de task
    slide.c                   # slide KASLR (routes tracefs + boot_id)
    fops.c                    # corruption pselect + manipulation fops
    faketables.c              # constructeurs de tables de pages factices étape-3 (3 niveaux)
    stage3.c                  # enfant pont pgd-swap (no-op sauf si configuré)
    stage3_loop.S, stage3_poll.S  # blob pont + trampoline d'entrée
    su_daemon.c               # démon root (compilé en PIE séparé)
    su_blob.S, wallpaper_blob.S   # binaires embarqués
    offset.h                  # tire le target.h par cible via -DTARGET_CONFIG_H
    kernelsnitch/             # bibliothèque de fuite mm_struct par hash de futex
    targets/<codename>-<build>/    # target.h par appareil + surcharges optionnelles
poc/poc.c                     # POC autonome (séparé de l'exploit)

Les cibles surchargent les sources partagées via le mécanisme pick_src du Makefile : si src/targets/<PROJECT>/<file>.c existe, il est utilisé à la place du src/<file>.c partagé ; sinon la version partagée est utilisée.

Notes et pièges

  • Ne jamais supprimer le cycle de chaîne PI. La primitive d'écriture dépend de l'échec de la requeue avec -EDEADLK (errno 35). Un cycle manquant se manifeste par success=1 sans aucun effet noyau.
  • Le waiter doit détenir le verrou de chaîne PI pendant pselect. Le libérer d'abord laisse le propriétaire se réveiller et courir après le parcours rb_tree factice → panic.
  • m1q : PSELECT_WAITER_WORD_SHIFT doit être 0 (profondeurs de pile ZF1 vérifiées depuis l'ELF). Les autres cibles restent par défaut à 1 — vérifiez par appareil avant de leur faire confiance.
  • p0_data_alias() utilise DIRECT_MAP_BASE (la linear map), qui diffère de P0_PAGE_OFFSET sur ZF1. Les alias de données pour les objets noyau déréférencés doivent utiliser la linear map.
  • pr_error() est fatal (exit(-1)), contrairement aux macros warning/info.
  • La reconstruction du slide utilise SLIDE_LOGGERS_0_1, pas SLIDE_NFULNL_LOGGER ; le mot est un alias de linear-map non randomisé, donc la route boot_id est indépendante du slide (à utiliser uniquement à slide=0 — exécutez d'abord la fuite tracefs).

Avertissement

Ceci est du code d'exploit pour appareils Android et est destiné à la recherche en sécurité, aux tests de vulnérabilité autorisés et au travail de défense sur du matériel que vous possédez ou pour lequel vous avez l'autorisation explicite de tester. Toute utilisation abusive sur des appareils que vous ne possédez pas est illégale. L'auteur n'est pas responsable des appareils brickés, des noyaux brickés ou des garanties annulées.

Télécharger l’outil
mm_struct
  • Route principale — bootstrap pipei tmp_page (m1q, focus actuel) — la route configfs CFI originale est une impasse sur m1q : l'ashmem Rust n'a aucun slot write_iter statique injectable et l'objet tas ASHMEM_SET_NAME est un KVec dont la disposition ne s'aligne pas avec le private_data de configfs_bin_write_iter. m1q pivote vers une écriture kmalloc à la place : récupérer le bloc mm d'ordre 3 fuité en tant qu'objets pipe_inode_info et utiliser l'écriture pselect W1 (*(tree_left) = tree_pc) pour écraser le slot tmp_page d'un candidat (+0x90) avec la page du namespace UTS (init_uts_ns). Une écriture fanout plante un nom marqueur à l'offset sysname et uname() rapporte "CatOS" — une écriture noyau vérifiable sans dépendance à un objet statique. Les cibles C-ashmem conservent le chemin configfs.
  • Pipe physrw — forger des pages de tampon pipe dans la page noyau sprayée pour la lecture/écriture physique : patch de cred, désactivation de SELinux, et manipulation directe de la mémoire noyau.
  • Root — patcher le cred de l'enfant root (uid/gid/caps/SID SELinux), puis preload.c installe le démon su embarqué (monté en tmpfs dans /apex/com.android.virt/bin, plus les variantes adbd-namespace et locales) et échange le fond d'écran.
  • VariableDéfautObjectif
    PSELECT_ROUTE_SHIFTcompile-timeDécalage de mot fd_set A/B pour la route principale (m1q)
    SLIDE_SHIFTcompile-timeDécalage de mot fd_set A/B pour la route slide
    PSELECT_ROUTE_DELAY_USEC50000Délai consommateur avant sched_setattr (doit être > 0)
    SLIDE_CONSUME_USEC, SLIDE_ENTER_DELAY_USEC, SLIDE_CONSUMER_CORE—Timing/pinning de la route slide
    SKIP_SLIDE0Utiliser le repli direct-map (slide = 0)
    SLIDE_ONLY0Exécuter uniquement le slide KASLR et quitter
    SLIDE_P0_OFFSET, SLIDE_BOOTID_OFF—Surcharger les offsets slide p0-alias / boot_id
    SLIDE_FORCE_BOOTID0Forcer la route boot_id pselect après le slide tracefs (test de mécanisme)
    KSNITCH_COLLISIONS4Nombre de collisions KernelSnitch
    STAGE30Activer la phase enfant pont pgd-swap (m1q)
    STAGE3_DRYRUN0Préparer/vérifier les tables puis abandonner avant le swap
    PAGE_RECLAIM_SENDS, PSELECT_SIMPLE_LAYOUT—Ajustements de spray/disposition fd_set
    TMP_PAGE_UNAME0Exécuter le bootstrap m1q pipei tmp_page-uname
    TMP_UNAME_PIPEI_SWEEP, TMP_UNAME_PIPEI_ALIGN_ORDER, TMP_UNAME_PIPEI_SPLIT_ORDER_PAGES—Mode balayage ; split_order balaye les 8 pages du bloc mm libéré
    TMP_UNAME_PIPEI_SLOT, TMP_UNAME_PIPEI_SLOT_CANDIDATES—Plage de balayage (début de chunk / nombre)
    PIPEI_CHILD_REGIONS0Régions rt_mutex seedées par enfant ; DOIT être >= chunk de balayage sinon les régions non seedées plantent au candidat 0
    PIPEI_RECLAIM_COUNT, PIPEI_DRAIN_COUNT—Récupérer/vider les paires pipe ; augmenter en cas d'échec de récupération
    PIPEI_SPONGE_MB3072Éponge d'inondation Order-0 maintenue pendant la récupération (build #29)
    PSELECT_W0_PRIO_OVERRIDE130Prio du top-waiter ; inerte avec régions seedées (garde arbre obsolète legacy, A/B uniquement)
    PSELECT_SKB_DELTA_OVERRIDE0Offset intra-page du payload A/B (uniquement si un appareil diffère de -0xe78)
    TMP_UNAME_NAME, TMP_UNAME_HOLD_SEC"CatOS", 5Nom marqueur / durée de maintien après succès
  • SKB_DATA_DELTA vaut -0xe78 (mesuré sur l'appareil, build #19) — la vraie tête de frag skb est 8 octets de moins que le -0xe80 dérivé du désassemblage ; cette erreur de 8 octets a causé chaque plantage pselect déterministe des builds #6-18 (entrée dans le parcours → déréférencement de données corrompues). Valeur partagée GKI 6.12 ; les copies util.c par cible portent un bouton A/B d'environnement SKB_DATA_DELTA. Ne PAS empiler PSELECT_SKB_DELTA_OVERRIDE par-dessus la constante intégrée.
  • Le corps du parcours est propre ; le placement était le bug. Avec le payload seedé aux offsets modélisés, le parcours de chaîne se termine sans faute sur l'appareil. Un plantage entrant dans le parcours signifie que la mémoire aux adresses des mots fd n'est PAS le payload (course de récupération/placement), pas une erreur de modèle.
  • m1q : la route configfs CFI est une impasse (ashmem Rust) — aucun slot fops statique injectable et l'enveloppe KVec ASHMEM_SET_NAME ne s'aligne pas avec configfs_bin_write_iter. Utilisez le bootstrap pipei tmp_page. Les gardes PSELECT_WRITE_RIGHT_SPRAYED / PSELECT_PI_TOP_TASK_SELF dans le target.h m1q redirigent les écritures [11] du parcours hors des fops statiques RO.
  • Régions rt_mutex par enfant (build #33) : chaque enfant de balayage redirige le mot fd w13 vers sa propre région seedée (payload+0x6000 + idx*0x40) à l'intérieur de la page récupérée. Les balayages à fake_lock partagé plantaient au candidat ~9-12 (nœuds de pile noyau obsolètes recyclés), et les régions vides du build #32 plantaient au candidat 0 (wake [9] owner==NULL sur le fake_task rempli de 0x41). PIPEI_CHILD_REGIONS doit être défini sinon les régions restent non seedées.
  • Le slide boot_id pselect m1q est en pause — sa cible W1 est de la RAM basse dont l'écriturabilité des pages varie à chaque boot ; tracefs est la route principale et mesure le slide à chaque exécution.