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-pmg110-root — Exploit LPE Android pour CVE-2026-43499 ciblant l'OPPO PMG110 (kernel 6.6). Utilise une UAF futex PI pour obtenir les droits root et installer un démon su via LD_PRELOAD. | Kitploit
Outils/GitHubGitHub/soralis0912/cve-2026-43499-pmg110-root
Sécurité AndroidEscalade de PrivilègesAnalyse des VulnérabilitésExploitationPost-ExploitationTests d'IntrusionSécurité MobileRed TeamingDéveloppement de Charges Utiles

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 →
Exploitation de Binaires
GitHubsoralis0912/cve-2026-43499-pmg110-root

CVE-2026-43499-pmg110-root

Exploit LPE Android pour CVE-2026-43499 ciblant l'OPPO PMG110 (kernel 6.6). Utilise une UAF futex PI pour obtenir les droits root et installer un démon su via LD_PRELOAD.

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

pmg110-root

CVE-2026-43499 (futex PI, use-after-free de rt_mutex_waiter) escalade de privilèges locale, porté sur le OPPO PMG110 / K15 Pro+ — MediaTek MT6991, ColorOS 16.

Un seul fichier poussé, lancé via LD_PRELOAD :

root@kitploit:~
adb push out/preload-pmg110-16.0.9.400.so /data/local/tmp/preload.so
adb shell chmod 644 /data/local/tmp/preload.so
adb shell LD_PRELOAD=/data/local/tmp/preload.so /system/bin/true

En cas de succès, un su persistant est laissé en place :

root@kitploit:~
adb shell /data/local/tmp/su -c id      # uid=0(root)

Vérifié sur l'appareil (2026-07-27) : uid=0 en environ 35 secondes à partir d'une exécution brute, sans aucune variable d'environnement modifiée, et su répondant ensuite depuis un adb shell non privilégié ordinaire :

root@kitploit:~
$ adb shell "/data/local/tmp/su -c 'echo 0 > /proc/sys/kernel/kptr_restrict'"
$ adb shell "/data/local/tmp/su -c 'grep -w init_task /proc/kallsyms'"
ffffffe89033e780 D init_task

L'exploit et l'installation de su sont tous deux vérifiés sur cet appareil. Ce que ce shell rooté a ensuite relu confirme également P0_KERNEL_PHYS_LOAD, les offsets de symboles et KS_MTE_TAGGED=0 indépendamment de l'exploit — voir targets/pmg110-16.0.9.400/NOTES.md.

Ce qu'il fait, et ce qu'il ne fait pas

Il exécute Write 1 (SELinux permissif) et Write 2 (cred → init_cred), amène un processus enfant à uid=0, et, à partir de là, installe le démon su embarqué.

  • toujours un seul fichier poussé. su n'est pas un second artefact : su_daemon.c est compilé comme un PIE aarch64 autonome et incorporé via .incbin dans le .rodata de la bibliothèque, il voyage donc à l'intérieur de preload.so et est réécrit à l'exécution. La voie de warhol-root, inchangée.
  • pas de script root, pas de ksud, pas de KernelSU
  • le processus appelant reste non privilégié — il obtient root en demandant au démon, ce qui est la même chose que vous ferez depuis le shell par la suite
  • SELinux est laissé permissif, comme warhol-root le laisse : le démon doit servir des clients non privilégiés via sa socket. Redémarrez pour rétablir le mode enforcing.

su est installé à trois endroits, car l'un d'eux est celui que vous atteindrez réellement :

Le démon écoute sur /data/local/tmp/temp_su.sock et journalise dans /data/local/tmp/su_daemon.log. Root n'est pas persistant après un redémarrage — relancez la ligne LD_PRELOAD après chaque démarrage.

Pour l'installation de KernelSU, utilisez plutôt la voie /data/local/tmp/a/e dans ghostlock-oneplus.

Relation avec warhol-root

Tout, sauf le cœur de l'exploit, provient de warhol-root, repris plutôt que réinventé :

  • l'organisation — des en-têtes par appareil sous targets/<device>/, mis en place dans source/src/ à la compilation, de sorte que changer de DEVICE ne puisse jamais laisser les en-têtes de l'appareil précédent derrière
  • la compilation — la sélection de la chaîne d'outils de source/Makefile (NDK s'il y en a un, clang hôte contre le sysroot NDK sinon) et la règle d'intégration en deux étapes qui produit build/embed/su_daemon_aarch64_pie avant de lier le .so
  • la voie su — su_daemon.c et su_blob.S sont octet-pour-octet identiques à ceux de warhol-root, et su_install.c est son installateur preload.c

Le cœur de l'exploit n'est pas celui de warhol-root. warhol-root est popsicle, qui est verrouillé sur GKI 6.12 / android16 et dont generate_target.py refuse toute autre bannière. PMG110 est en 6.6 / android15, donc le cœur ici est l'arborescence ghostlock 6.6 — elle-même descendante du même code (kernelsnitch/utils.h et timeutils.h sont octet-pour-octet identiques entre les deux dépôts), développée plus avant.

Chaque ligne de l'exploit proprement dit — Write 1, Write 2, KernelSnitch, la voie pselect — est le même code dans les deux arborescences.

D'où l'installation de su est appelée

C'est la seule différence structurelle, et elle est imposée par les deux arborescences qui obtiennent root de manières différentes.

warhol-root root le processus de l'exploit lui-même et appelle donc install_embedded_su() directement depuis run_direct_root(). Ici, Write 2 échange le pointeur cred d'un enfant forké et le parent reste l'appelant non privilégié ; l'enfant dans child_main() est donc le seul contexte capable d'effectuer l'installation — c'est là qu'elle s'exécute.

Les deux arborescences portent le même stub faible install_embedded_su() dans util.c, qui retourne ENOSYS ; fournir la définition forte est ce qui active la voie. C'est bon à savoir, car une compilation qui, pour une raison ou une autre, omet su_install.c se lie et s'exécute toujours — elle rapporte simplement su=0/38 et n'installe rien.

Compilation

root@kitploit:~
make                      # = make preload -> out/preload-<DEVICE>.so
make DEVICE=<name>        # use targets/<name>/
make devices              # list available DEVICE values
make info                 # show the selected target and the resolved toolchain

La chaîne d'outils est trouvée automatiquement : ANDROID_NDK_HOME / ANDROID_NDK_ROOT d'abord, puis les emplacements d'installation NDK habituels pour Linux et macOS et, si tout cela échoue, clang hôte ciblant le sysroot NDK. Ne définissez ANDROID_NDK_HOME que pour remplacer la recherche. make info affiche ce qu'elle a choisi.

La compilation se fait en deux étapes, ce qu'il vaut la peine de savoir :

  1. su_daemon.c → build/embed/su_daemon_aarch64_pie, un PIE aarch64 autonome
  2. su_blob.S intègre ce binaire via .incbin dans .rodata, et l'ensemble est lié dans l'unique preload.so

Donc make clean et une recompilation sont le seul moyen de changer le su embarqué — modifier su_daemon.c seul suffit, la dépendance est déclarée, mais le blob est un artefact de compilation et n'est pas suivi.

targets/<device>/{target.h,device_offsets.h} sont remis en place dans source/src/ à chaque compilation, afin qu'un en-tête périmé d'un autre appareil ne puisse pas être pris en compte silencieusement.

out/*.so n'est pas suivi (même convention que warhol-root) — clonez et lancez make.

Le .so est compilé avec -fvisibility=hidden et exporte zéro symbole. Une bibliothèque LD_PRELOAD a priorité dans la résolution des symboles pour tout le processus ; tout ce qu'elle exporterait pourrait donc masquer un symbole du même nom dans le binaire hôte ou dans libc. Ce drapeau ne régit que la génération de code C ; c'est pourquoi su_blob.S marque ses deux symboles .hidden à la main — sans ces lignes, les bornes du blob seraient la seule chose que la bibliothèque exporterait encore.

Variables d'environnement

Aucune de ces variables n'était nécessaire lors de l'exécution vérifiée.

Lecture du journal

root@kitploit:~
[*] futex_hashsize 2048 (8 possible CPUs)
[*] ks collisions=3/3 baseline=8 threshold=10x (80) accepted=[1244..1597] slowest_rejected=N
[+] child uid = 0
[+] embedded su wrote 15304 bytes to /apex/com.android.virt/bin/su
[+] embedded su daemon ready pid=NNNN socket=/data/local/tmp/temp_su.sock daemon=/apex/com.android.virt/bin/su
[+] embedded su install ok=1 errno=0 daemon=NNNN
[+] su ready: /data/local/tmp/su and /apex/com.android.virt/bin/su
[+] ghostlock preload verdict: EXPLOIT OK

child uid = 0 signifie que l'exploit a réussi ; tout ce qui suit est l'installation. Les deux sont rapportés séparément à dessein, et le verdict aussi :

Celui du milieu est la distinction intéressante : il indique que les offsets dans target.h sont corrects pour cette compilation et que le problème se situe quelque part dans l'installation, ce qui est une chose complètement différente à déboguer. su=0/38 (ENOSYS) à l'intérieur signifie précisément que c'est le stub faible qui a été lié.

mm_struct leak failed suivi de prepare_kernel_page retry N/24 n'est pas un échec. C'est la progression de la boucle, et l'exécution réussie le montre aussi. Rien n'a échoué jusqu'à ce que les 24 tentatives soient épuisées et que prepare_kernel_page timeout apparaisse. De même, probing cfi ... expected=9 avec child uid = 2000 est un tour manquant, sur dix.

Ne jugez pas une exécution à partir d'un journal tronqué — cette erreur a coûté ici un cycle complet de diagnostic erroné.

Une exécution entièrement échouée est normale aussi. La course pselect n'est pas fiable à 100 % : une exécution peut la perdre cinq fois de suite et se terminer par Write 1 failed, puis la suivante la gagne du premier coup avec ret=9. Observé sur cet appareil. ret=4 expected=9 est ce à quoi ressemble une course perdue, pas un target.h erroné — un échec n'est pas une raison pour aller redériver les offsets. Relancez-la.

Fichiers

Licence

Uniquement à des fins de recherche en sécurité autorisée et d'éducation.

Télécharger l’outil
AppareilOPPO PMG110 / K15 Pro+ / OP61E5L1
SoCMediaTek MT6991 (Dimensity 9500s)
Noyau6.6.118-android15-8-g93e223c276e7-abogki500782043-4k (GKI, pages 4K)
BuildColorOS 16 / PMG110_16.0.9.400(CN01) — mêmes octets de noyau que 16.0.8.300
BugCVE-2026-43499, non corrigé dans cette image (prouvé par désassemblage, pas par la version)
CheminPourquoi
/apex/com.android.virt/bin/susur un tmpfs monté par-dessus ce répertoire ; sur PATH pour un shell root
/data/local/tmp/suatteignable depuis un simple adb shell, sans contournements de PATH
/apex/com.android.virt/bin/su dans l'espace de noms de montage d'adbdinstallé via setns, donc un nouveau adb shell le voit
FichierRelation
util.c slide.c fops.c pipe.c root.c miniadb.c common.h offset.h kernelsnitch/*de ghostlock, octet-pour-octet identique
su_daemon.c su_blob.Sde warhol-root, octet-pour-octet identique (su_blob.S ajoute deux lignes .hidden — voir Compilation)
su_install.cl'installateur preload.c de warhol-root, déplacé dans son propre fichier car le preload.c de cette arborescence a déjà un autre rôle
main.cde ghostlock, plus l'appel su dans l'enfant rooté et le rapport des résultats
preload.cuniquement ici — le constructeur et le journal à double sortie
offsets.huniquement la définition de structure ; l'entrée est mise en place depuis targets/<device>/device_offsets.h
VariableEffet
GHOSTLOCK_LOGdestination du journal (défaut /data/local/tmp/.ghostlock.log) ; la sortie va sur stdout et dans le fichier
GHOSTLOCK_KS_VERBOSE=1affiche les adresses de collision et les plages de balayage de KernelSnitch
GHOSTLOCK_KS_THRESHOLD=<n>remplace le multiplicateur de seuil de collision
GHOSTLOCK_MTE=1balaye également les tags des pointeurs du noyau (15x plus lent)
GHOSTLOCK_PHYS_LOAD=0x...remplace l'adresse de chargement physique du noyau
PSELECT_SHIFT=<n>remplace le décalage de la superposition de pile (remplace, n'ajoute pas)
VerdictSignification
EXPLOIT OKroot, et su répond
EXPLOIT OK, SU INSTALL FAILEDWrite 1 et Write 2 ont abouti ; seule l'installation a échoué
EXPLOIT FAILEDles écritures n'ont pas abouti
ABORTEDl'exécution s'est arrêtée avant de pouvoir faire son rapport — lisez la dernière ligne [!]
CheminContenu
source/src/preload.cconstructeur : exécute l'exploit, rapporte, s'arrête
source/src/main.cl'exploit lui-même (Write 1 / Write 2)
source/src/su_daemon.cle binaire su — compilé de manière autonome comme PIE aarch64, non lié dans le .so
source/src/su_blob.Sintègre ce PIE via .incbin dans le .rodata du .so
source/src/su_install.créécrit le blob, démarre le démon, le sonde
source/src/target.hdestination de mise en place (ignoré par git)
targets/<device>/target.hdisposition à la compilation : offsets de structures, constantes physmap, formes de slab et futex
targets/<device>/device_offsets.hoffsets de symboles globaux depuis kallsyms
tools/extract_device.pyboot.img → offsets, champs de structures BTF, résultat de la superposition pselect
tools/preloader_memlayout.pypreloader MediaTek → P0_KERNEL_PHYS_LOAD
tools/qemu_verify.pydémarre le noyau sous QEMU : mesure la superposition de pile, vérifie la stabilité du mapping linéaire
tools/device_probe.shcontrôle préalable depuis un adb shell non privilégié