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
ghostlock-pfem10 — GhostLock (CVE-2026-43499) pour OPPO Find X5 Pro (PFEM10) — rétro-ingénierie du watchdog OPlus et du détecteur de heap-spray | Kitploit
Outils/GitHubGitHub/imeiplus/ghostlock-pfem10
Sécurité AndroidEscalade de PrivilègesCriminalistique MémoireAnalyse des VulnérabilitésExploitationRétro-ingénierieSécurité MobileDéveloppement de Charges UtilesExploitation de Binaires
GitHubimeiplus/ghostlock-pfem10

ghostlock-pfem10

GhostLock (CVE-2026-43499) pour OPPO Find X5 Pro (PFEM10) — rétro-ingénierie du watchdog OPlus et du détecteur de heap-spray

46il y a 22 joursPas encore vérifié

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
Voir le dépôt

GhostLock — OPPO Find X5 Pro (PFEM10)

English · 中文

build

Portage de GhostLock (CVE-2026-43499) pour l'OPPO Find X5 Pro sous ColorOS 16. Atteint un processus enfant avec uid=0 et un kernelsu.ko chargé ; le processus root est intercepté.

Vulnérabilité

CVE-2026-43499 — use-after-free de futex PI. remove_waiter() efface current->pi_blocked_on lorsque current est le requeueur, sur le chemin de rollback -EDEADLK de rt_mutex_start_proxy_lock().

remove_waiter @ 0xffffffc0081ed254 — forme pré-correctif.

Appareil

AppareilOPPO Find X5 Pro (PFEM10)
SoCSM8450 / Adreno 730
OSColorOS 16.0.3.520 (CN01)
Noyau5.10.236-android12-9-o-gaf2075ad2c06
Bootloaderverrouillé, vert
VA_BITS39 — KIMAGE_TEXT_BASE = 0xffffffc008000000

État

Étape
Déclenchement du waiter compact (CMP_REQUEUE_PI → EDEADLK)fonctionne
Fuite de task_struct (perf)fonctionne
Écriture PI (8 octets ; valeur = 0 ou une adresse noyau valide)fonctionne
task+0x778 ou task+0x780 seul → Uid=rootfonctionne — mais un atterrissage sur un seul champ laisse la tâche divergente, et c'est un BUG_ON dur latent. Voir le risque de divergence
Les deux champs écrits avec UNE seule valeur (une paire cohérente)❌ jamais produit avec une page pulvérisée. Observé uniquement avec l'alias global init_cred (09-14, CONTROL=1). Le runner l'impose désormais (SAME_VALUE=1) ; non exécuté sur l'appareil
Blanchiment des identifiants (setresgid + setresuid)implémenté derrière V12_LAUNDER=1 ; non exécuté sur l'appareil
kernelsu.ko chargéfonctionne
Le processus root survit⚠ non établi — voir ci-dessous
Mécanisme de redémarrage❌ non établi. Un candidat (la divergence) est désormais exclu ; voir ci-dessous
probe_state comme critère d'atterrissage❌ incorrect — ne pas utiliser. Trois contre-exemples ; voir le tableau ci-dessous
Canal de panique pstore/ramoops⚠ l'instrument existe ; canal jamais validé (pas encore de test nul)
« La victime tourne en pur espace utilisateur »⚠ aucune lecture pour l'instant — uid.stream enregistre désormais utime/stime/nvcsw pour pouvoir le vérifier
double écriture en une passe côté pi⚠ non établi ; pi.pc/pi.left sont codés en dur à 0 dans fdset_map.h
Chemin A (UMH / modprobe_path)STATIC_USERMODEHELPER_PATH=""

À propos de « le processus root survit » : les exécutions dans evidence/kill.log atteignent uid=0 et chargent kernelsu.ko, et dans l'exécution qui l'a réellement sondé, le processus du gestionnaire KernelSU a survécu 120 s avec kernelsu toujours Live dans /proc/modules. Dans une exécution ultérieure, la même chaîne a laissé les services du framework Android injoignables (Can't find service: package/power/input/phone/wifi) alors que le module était toujours Live. Aucune ligne noyau [ROOTCHECK-*] et aucune charge utile $$sys_call_number@@ n'a jamais été capturée, donc la cause de l'état de l'exécution ultérieure n'est pas attribuée. Voir evidence/notes.md §2.3, §2.4 et §7.

Décalages

task_struct

ChampDécalage
real_cred / cred0x778 / 0x780
syscallno mis en cache0xdf8
uid / euid / gid / egid mis en cache0xe00 / 0xe08 / 0xe10 / 0xe18

thread_info

ChampDécalage
flags0x0
addr_limit0x8
ttbr00x10
preempt_count0x18

cred

ChampDécalageChampDécalage
uid0x4cap_inheritable0x28
gid0x8cap_permitted0x30
suid0xccap_effective0x38
sgid0x10cap_bset0x40
euid0x14cap_ambient0x48
egid0x18
fsuid / fsgid0x1c / 0x20

Flux d'exploitation```

LT perf leak target task_struct → file W7 stage 1 task+0x778 = V (real_cred → private sprayed page; V observed) W7 stage 2 task+0x780 = V (cred) ★ V12_W7_VALUE=V — THE SAME VALUE, not a new page W7 stage 3 V+8 = 0 (LOCAL repair of the page that was installed, ZERO shape) LT child fexecve(memfd of loader) — no execve of a /data path loader ksud late-load → kernelsu ... Live

**L'étape 2 doit recevoir explicitement la valeur de l'étape 1.** Les étapes 1 et 2 sont deux
processus indépendants, chacun avec son propre spray, donc « écrire la page de creds dans les deux
slots » est un piège : lu naïvement, cela produit `(pageA, pageB)`, et comme
`commit_creds` compare des **pointeurs**, cette paire est divergente même lorsque les deux écritures
aboutissent. Ce n'est pas hypothétique — c'est exactement ce que font les exécutions 3 et 9 :```
run 9   0x778 shot  write value = 0xffffff88679bade0
        0x780 shot  write value = 0xffffff8785d6ade0     <- a different page
run 3   0x778 shot  write value = 0xffffff8787b5ade0
        0x780 shot  write value = 0xffffff881bad2de0     <- a different page

run_bootA.sh déclenche donc l'étape 2 avec V12_W7_VALUE=<valeur observée à l'étape 1> et refuse de la déclencher du tout si cette valeur ne peut pas être récupérée. HOLD doit survivre à l'étape 2, sinon la page de l'étape 1 est libérée et réallouée et « la même valeur » devient un pointeur suspendu. Voir la règle de la même valeur.

Une page par démarrage est réparée. L'étape 3 met à zéro V+8. Avec deux pages différentes, mettre à zéro les deux effacerait l'estampille gid/suid (ci-dessous) et ferait passer une divergence pour un accord, donc le runner ne répare que la page qui a été effectivement installée et s'arrête si les deux valeurs divergent.

La page cred est construite par payload.c : les huit champs id à zéro, les cinq ensembles de capacités complets, et user / user_ns / group_info pointant vers root_user / init_user_ns / init_groups. L'étape 3 existe parce que l'effet de bord de l'écriture écrase toujours cred+8 (gid/suid) du cred qu'il installe.

Sur init_cred — une dichotomie explicite

Deux sections ici se contredisaient auparavant (« jamais le global init_cred » vs « CONTROL=1 reproduit la cellule 2 », et la cellule 2 est init_cred). Les deux affirmations sont vraies pour des rôles différents :

  • Interdit comme cible. Écrire le pointeur init_cred fait que l'effet de bord corrompt init_cred+8 globalement — init_cred est partagé par chaque thread du noyau, et Uid: 0 0 4294967176 0 est précisément cette corruption. Le code refuse ce chemin sauf si V12_ALLOW_INIT_CRED=1 est défini délibérément.
  • Conservé comme la seule paire PROUVÉE cohérente. La chaîne du 09-14 qui a atteint ksud écrivait une adresse fixe (0xffffff802a7e0be0) dans les deux slots, donc real_cred == cred par construction — c'est pourquoi elle a survécu jusqu'à execve. CONTROL=1 la reproduit. C'est un contrôle, pas une configuration sur laquelle bâtir.

fuite perf : PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CPU_CLOCK, PERF_SAMPLE_REGS_INTR, exclude_user=1. Accepter [0xffffff8400000000, 0xffffff90000000), votes ≥ 15 %.

La primitive d'écriture, et son effet de bord

Télécharger l’outil