
Exploit local root pour CVE-2025-21479 (Adreno KGSL) sur iQOO Neo8 (SM8475) - accès r/w à la mémoire physique, désactive SELinux, ouvre un shell root
Code expérimental d'élévation de privilèges locale pour CVE-2025-21479, ciblant le GPU Qualcomm Adreno (KGSL) du vivo V2301A (iQOO Neo8 / PD2301, Snapdragon 8+ Gen 1 / SM8475, Adreno A730, Android 14, noyau GKI 5.10).
Sur le principe, l'exploit pilote le SMMU du GPU via la commande CP_SMMU_TABLE_UPDATE de KGSL, obtenant une capacité de lecture/écriture arbitraire de la mémoire physique, permettant ensuite de désactiver SELinux, de localiser les symboles du noyau et d'obtenir un shell root — le tout sans flasher l'appareil ni déverrouiller le Bootloader.
Réservé à la recherche en sécurité sur des appareils que vous possédez ou pour lesquels vous êtes autorisé.
and $12, 0x3) est défectueux et confond les paquets Self-Draw-State (SDS) avec des paquets RingBuffer (RB). Ainsi, en soumettant depuis l'espace utilisateur un CP_SMMU_TABLE_UPDATE falsifié placé dans un bloc SDS, on peut réécrire le TTBR0 du GPU, faisant pointer la table de pages du GPU vers n'importe quelle page physique — le GPU peut alors lire et écrire n'importe quelle mémoire physique.stabilize_rw, le mappage est consolidé en une zone auto-mappée de 2 Mo, permettant des lectures rapides en masse (fast_pread_bytes, etc.).selinux_state.enforcing est mis à zéro dans la mémoire physique. L'image du noyau est ensuite dumpée depuis l'adresse physique de base fixe 0xA8000000 (limite par défaut : 43 Mo) — le SM8475 n'a pas de KASLR physique — et les structures kallsyms sont analysées directement depuis le dump brut (endianness détectée automatiquement), donc le KASLR virtuel n'a pas d'effet non plus.prepare_kernel_cred(0) → commit_creds(), avec désactivation de seccomp) patche __do_sys_capset. Le processus enfant issu de fork() appelle capset() → devient uid 0 → restaure le code noyau écrasé → ce processus enfant réexécute la commande de votre choix dans le domaine u:r:shell:s0 (par défaut sh). Le code du noyau est au final identique octet pour octet à celui d'avant l'exécution.Les informations complètes de l'appareil se trouvent dans device_info.txt.
├── source/
│ ├── cheese.c # 漏洞利用主程序(include 了下面两个文件)
│ ├── kallsyms_lookup.c # 针对原始内核映像的 kallsyms 解析器
│ └── adrenaline.h # KGSL ioctl / 命令定义(作者 hawkes)
├── exploit # 预编译 arm64 二进制(动态链接,minSdk 21,
│ # NDK r30-beta2)
├── device_info.txt # 用于调参的目标设备信息
└── README.md
Remarque : cheese.c fait un #include de kallsyms_lookup.c et adrenaline.h ; les trois fichiers doivent se trouver dans le même répertoire lors de la compilation.
N'importe quel NDK Android relativement récent (r25+) convient, par exemple :
# 静态链接(自包含、任意 API 等级可运行;体积较大)
aarch64-linux-android34-clang -o cheese source/cheese.c -static
# 动态链接(体积小,依赖设备自带的 libc;API 等级需匹配设备)
aarch64-linux-android34-clang -o exploit_dyn source/cheese.c
# 若要兼容 API 21+ 设备,请改用 aarch64-linux-android21-clang
adb push exploit /data/local/tmp/
adb shell
chmod +x /data/local/tmp/exploit
/data/local/tmp/exploit
Cliquez ici pour le tutoriel détaillé (il contient un outil en un clic)
En cas de succès, vous obtenez un shell uid=0 et SELinux est désactivé pour cette session. Après l'élévation de privilèges, le processus réexécute argv[1..] (par défaut sh). Chaque exécution affiche un compte à rebours (« pte wait 3 second ») et la progression de la lecture du noyau de 43 Mo (« Read X / 43 MB »).

$(find /data/app -type f -name "libksud.so" | grep resukisu) late-load --kmi android12-5.10
Les constantes spécifiques à l'appareil se trouvent en haut de source/cheese.c :
KERNEL_PHYS_BASE — adresse physique de base du noyau (défaut : 0xA8000000, SM8475)IDMAP_PG_DIR_OFFSET — offset de idmap_pg_dir (défaut : 0x245a000)selinux_state_offset — offset utilisé pour localiser selinux_state (défaut : 0x729030, adresse physique réelle = adresse physique de idmap_pg_dir + cet offset)gPhyAddrs[] — adresses physiques de base candidates, essayées successivementDEFAULT_KERNEL_SIZE_MB — valeur de repli / limite de la lecture du noyauPrérequis : GPU de la série Adreno A730/A7xx (firmware affecté), noyau dont kallsyms peut être résolu dans le dump physique, et absence de KASLR physique.
can't get GPU r/w — échec de l'initialisation du GPU ; attendez d'abord, puis essayez CHEESE_ATTEMPT=1..n ou spécifiez CHEESE_PHYADDR manuellement ; ajoutez CHEESE_NO_RETRY=1 pour déboguer une seule tentative.CHEESE_READ_FULL_KERNEL=1 (en augmentant la limite avec CHEESE_KALLSYMS_MAX_MB), puis envisagez CHEESE_FORCE_MANUAL_PATCHFINDER=1.cheese sur lequel cet outil est baséadrenaline.h de 2020, fournissant les définitions des interfaces ioctlBasé sur zhuowei/cheese (dépôt d'origine sans licence). Le principe complet est décrit dans le bulletin de sécurité.
Ce dépôt est destiné uniquement à l'éducation et à la recherche en sécurité ; utilisez-le uniquement sur des appareils que vous possédez ou pour lesquels vous êtes autorisé. L'auteur n'est pas responsable de tout usage abusif. L'utilisation de cet outil peut annuler la garantie ou enfreindre les conditions de service applicables.
| Modèle | vivo V2301A (iQOO Neo8, nom de code PD2301, carte mère taro) |
| SoC | SM8475 (Snapdragon 8+ Gen 1) + Adreno 730 |
| Android | 14 (UP1A.231005.007, SDK 34, qssi-user) |
| Noyau | 5.10.198-gki-gfb2f5774872c |
| SELinux | Enforcing (désactivé à l'exécution par cet outil) |
| Variable | Rôle |
|---|
CHEESE_PHYADDR=<phys> | Force l'adresse physique de base du noyau (ignore la table de sondage gPhyAddrs intégrée). |
CHEESE_ATTEMPT=<n> | Utilise gPhyAddrs[n] au lieu de gPhyAddrs[0] ; après un échec d'initialisation du GPU, maybe_retry incrémente automatiquement et réessaie. |
CHEESE_SWAPPER_PG_DIR_OFF=<off> | Ignore l'analyse de swapper_pg_dir et utilise directement cet offset absolu. |
CHEESE_READ_FULL_KERNEL=1 | Lit l'image complète du noyau (la limite de lecture par défaut est DEFAULT_KERNEL_SIZE_MB = 43 Mo). |
CHEESE_KALLSYMS_MAX_MB=<MB> | Surcharge la limite de lecture de kallsyms. |
CHEESE_FORCE_MANUAL_PATCHFINDER=1 | Force l'utilisation du patchfinder manuel en secours (init_cred, prepare_kernel_cred). |
CHEESE_SKIP_GPU=1 | Ignore la phase de lecture/écriture GPU (débogage uniquement). |
CHEESE_NO_RETRY=1 | En cas d'échec, quitte directement sans nouvelle tentative. |