
Exploit de root local para CVE-2025-21479 (Adreno KGSL) en iQOO Neo8 (SM8475) - lectura/escritura de memoria física, desactiva SELinux, genera un shell root
Código de prueba de escalada local de privilegios para CVE-2025-21479, dirigido a la GPU Qualcomm Adreno (KGSL) en el vivo V2301A (iQOO Neo8 / PD2301, Snapdragon 8+ Gen 1 / SM8475, Adreno A730, Android 14, kernel GKI 5.10).
En principio, el exploit utiliza el comando CP_SMMU_TABLE_UPDATE de KGSL para controlar la SMMU de la GPU,
obteniendo así la capacidad de lectura y escritura arbitraria en memoria física, y a continuación desactiva SELinux, localiza los símbolos del kernel y obtiene una shell de root;
todo el proceso no requiere flashear el dispositivo ni depende de desbloquear el Bootloader.
Solo para investigación de seguridad en dispositivos que poseas o para los que tengas autorización.
and $12, 0x3) tiene un defecto que hace que los paquetes Self-Draw-State (SDS) se
clasifiquen erróneamente como RingBuffer (RB). Por lo tanto, al colocar un CP_SMMU_TABLE_UPDATE falsificado en un bloque SDS
y enviarlo desde el espacio de usuario, se puede sobrescribir el TTBR0 de la GPU, haciendo que la tabla de páginas de la GPU apunte a una página física arbitraria — a partir de ahí la GPU
puede leer y escribir cualquier memoria física.stabilize_rw se refuerza la asignación como una región de automapeo de 2 MB,
lo que permite lecturas rápidas en grandes volúmenes (fast_pread_bytes, etc.).selinux_state.enforcing en la memoria física.
A continuación se vuelca la imagen del kernel desde la dirección física fija 0xA8000000 (límite predeterminado de 43 MB) —
SM8475 no tiene KASLR físico — y se analiza la estructura de kallsyms directamente desde el volcado sin procesar
(detección automática de endianness), por lo que el KASLR virtual tampoco afecta.__do_sys_capset con un fragmento de shellcode AArch64 (prepare_kernel_cred(0) →
commit_creds(), y se desactiva seccomp). El proceso hijo creado con fork
llama a capset() → pasa a ser uid 0 → restaura el código del kernel sobrescrito → el proceso hijo vuelve a ejecutar el comando que especifiques
en el dominio u:r:shell:s0 (por defecto sh). El código del kernel finalmente queda
idéntico byte a byte a como estaba antes de la ejecución.| Modelo | vivo V2301A (iQOO Neo8, nombre en clave PD2301, placa base taro) |
| SoC | SM8475 (Snapdragon 8+ Gen 1) + Adreno 730 |
| Android | 14 (UP1A.231005.007, SDK 34, qssi-user) |
| Kernel | 5.10.198-gki-gfb2f5774872c |
| SELinux | Enforcing (desactivado en tiempo de ejecución por esta herramienta) |
La información completa del dispositivo está en 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
注意:cheese.c 会 #include kallsyms_lookup.c 和 adrenaline.h,
编译时三个文件需要放在同一目录。
Cualquier Android NDK relativamente reciente (r25+) es válido, por ejemplo:
# 静态链接(自包含、任意 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
#推送exploit
adb push exploit /data/local/tmp/
#运行exploit
adb shell
chmod +x /data/local/tmp/exploit
/data/local/tmp/exploit
详细教程点我(里面有一键工具)
Tras el éxito se obtiene un shell con uid=0, y el SELinux de esta sesión ya ha sido desactivado. El proceso, tras completar
la escalada de privilegios, vuelve a ejecutar argv[1..] (por defecto sh). Cada ejecución tiene una cuenta atrás
("pte wait 3 second") y un progreso de lectura del kernel de 43 MB ("Read X / 43 MB").

| Variable | Función |
|---|---|
CHEESE_PHYADDR=<phys> | Fuerza la dirección física base del kernel (omite la tabla de sondeo integrada gPhyAddrs). |
CHEESE_ATTEMPT=<n> | Usa gPhyAddrs[n] en lugar de gPhyAddrs[0]; tras un fallo de inicialización de la GPU, maybe_retry incrementa y reintenta automáticamente. |
CHEESE_SWAPPER_PG_DIR_OFF=<off> | Omite el escaneo de swapper_pg_dir y usa directamente ese desplazamiento absoluto. |
CHEESE_READ_FULL_KERNEL=1 | Lee la imagen completa del kernel (el límite de lectura predeterminado es DEFAULT_KERNEL_SIZE_MB = 43 MB). |
CHEESE_KALLSYMS_MAX_MB=<MB> | Sobrescribe el límite de lectura de kallsyms. |
CHEESE_FORCE_MANUAL_PATCHFINDER=1 | Fuerza el uso del patchfinder manual como respaldo (init_cred, prepare_kernel_cred). |
CHEESE_SKIP_GPU=1 | Omite la fase de lectura/escritura de la GPU (solo para depuración). |
CHEESE_NO_RETRY=1 | Sale directamente tras un fallo, sin reintentar con otro número. |
Debido al mecanismo especial de los modelos vivo, en el iQOO Neo8 es necesario deshabilitarlo para mantener estables los permisos de root (el principio concreto no se explica aquí. Puede no ser aplicable a otros modelos vivo y versiones de kernel; es necesario extraer vr.ko y realizar un análisis inverso por cuenta propia). Antes de ejecutar las siguientes operaciones, asegúrese primero de que la sesión de shell actual ya ha obtenido permisos de root.
#备份原始apanic
dd if="$(readlink -f /dev/block/by-name/apanic)" of=/sdcard/apanic_full.bin
#adb shell内执行
adb pull /sdcard/apanic_full.bin ./
#主机端执行
#禁用vr.ko(风险自行评估)
printf '\370\037\000\000\000\000\000\000\000\000\000\000' | dd of=$(readlink -f /dev/block/by-name/apanic) bs=1 seek=$(( $(blockdev --getsize64 /dev/block/by-name/apanic) - 65472 )) conv=notrunc && sync
Después, si reiniciar el teléfono en frío provoca la pérdida de los permisos de root, basta con volver a ejecutar el exploit y luego realizar el jailbreak
$(find /data/app -type f -name "libksud.so" | grep resukisu) late-load --kmi android12-5.10
Las constantes específicas del dispositivo se encuentran en la parte superior de source/cheese.c:
KERNEL_PHYS_BASE — dirección física base del kernel (por defecto 0xA8000000, SM8475)IDMAP_PG_DIR_OFFSET — desplazamiento de idmap_pg_dir (por defecto 0x245a000)selinux_state_offset — desplazamiento usado para localizar selinux_state (por defecto
0x729030, dirección física real = dirección física de idmap_pg_dir + este desplazamiento)gPhyAddrs[] — direcciones físicas base candidatas que se prueban en ordenDEFAULT_KERNEL_SIZE_MB — valor de reserva / límite superior para la lectura del kernelRequisitos previos: GPU de la serie Adreno A730/A7xx (con firmware afectado) y que el sistema del dispositivo no tenga instalado el parche de seguridad publicado por Qualcomm en junio de 2025, que el kernel pueda resolverse mediante kallsyms en el volcado físico, y que no haya KASLR físico.