
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 experimental de escalada de privilegios local para CVE-2025-21479, dirigido a la GPU Qualcomm Adreno (KGSL) del vivo V2301A (iQOO Neo8 / PD2301, Snapdragon 8+ Gen 1 / SM8475, Adreno A730, Android 14, kernel GKI 5.10).
Conceptualmente, el exploit usa el comando CP_SMMU_TABLE_UPDATE de KGSL para controlar la SMMU de la GPU,
obteniendo capacidad de lectura/escritura arbitraria de memoria física; luego desactiva SELinux, localiza los símbolos del kernel y obtiene
una shell root — sin flashear el dispositivo ni depender de un bootloader desbloqueado.
Solo para investigación de seguridad en dispositivos que poseas o para los que tengas autorización.
and $12, 0x3) del microcódigo del Adreno A730 (eureka_panther) tiene un fallo: clasifica erróneamente los paquetes Self-Draw-State (SDS) como
RingBuffer (RB). Por tanto, al enviar desde el espacio de usuario un CP_SMMU_TABLE_UPDATE falsificado dentro de un bloque SDS,
se puede sobrescribir el TTBR0 de la GPU, haciendo que su tabla de páginas apunte a cualquier página física: a partir de entonces la GPU
puede leer y escribir en cualquier memoria física.stabilize_rw como una región auto-mapeada de 2 MB,
lo que permite lecturas rápidas en grandes lotes (fast_pread_bytes, etc.).selinux_state.enforcing en la memoria física.
Después vuelca la imagen del kernel desde la dirección física base fija 0xA8000000 (límite predeterminado de 43 MB) —
el SM8475 no tiene KASLR físico — y analiza las estructuras kallsyms directamente del volcado en bruto
(detección automática de endianness), por lo que el KASLR virtual tampoco afecta.__do_sys_capset con un shellcode AArch64 (prepare_kernel_cred(0) →
commit_creds(), además de desactivar seccomp). El proceso hijo creado con fork()
llama a capset() → se convierte en uid 0 → se restaura el código del kernel sobrescrito → el proceso hijo
re-ejecuta el comando que especifiques (por defecto sh) en el dominio u:r:shell:s0. El código del kernel acaba
siendo idéntico byte a byte al de antes de la ejecución.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
Nota: cheese.c incluye con #include tanto kallsyms_lookup.c como adrenaline.h; los tres archivos deben estar en el mismo directorio al compilar.
Cualquier NDK de Android relativamente reciente (r25+) es válido. 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
adb push exploit /data/local/tmp/
adb shell
chmod +x /data/local/tmp/exploit
/data/local/tmp/exploit
Tutorial detallado aquí (incluye una herramienta de un clic)
Tras el éxito obtendrás una shell con uid=0 y SELinux quedará desactivado en esa sesión. Al completar la escalada, el proceso re-ejecuta argv[1..] (por defecto sh). Cada ejecución muestra una cuenta atrás ("pte wait 3 second") y el progreso de lectura de los 43 MB del kernel ("Read X / 43 MB").

$(find /data/app -type f -name "libksud.so" | grep resukisu) late-load --kmi android12-5.10
Las constantes específicas del dispositivo están al principio 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 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 respaldo / límite de lectura del kernelRequisitos previos: GPU de la serie Adreno A730/A7xx (firmware afectado), kernel cuyos kallsyms puedan analizarse desde el volcado físico y ausencia de KASLR físico.
can't get GPU r/w — falló la inicialización de la GPU; espera primero y, si sigue sin funcionar, prueba CHEESE_ATTEMPT=1..n o especifica
manualmente CHEESE_PHYADDR; para depurar un único intento añade CHEESE_NO_RETRY=1.CHEESE_READ_FULL_KERNEL=1 (ampliando el límite con
CHEESE_KALLSYMS_MAX_MB) y después considera CHEESE_FORCE_MANUAL_PATCHFINDER=1.cheese
en el que se basa esta herramientaadrenaline.h de 2020, que proporciona las definiciones de la interfaz ioctlBasado en una modificación de zhuowei/cheese (el repositorio original no tiene licencia). El principio completo se explica en el boletín de seguridad.
Este repositorio es solo para fines educativos y de investigación de seguridad; úsalo únicamente en dispositivos que poseas o para los que tengas autorización. El autor no se responsabiliza de ningún uso indebido. El uso de esta herramienta puede anular la garantía o contravenir los términos de servicio aplicables.
| 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) |
| Variable | Función |
|---|
CHEESE_PHYADDR=<phys> | Fuerza la dirección física base del kernel indicada (omite la tabla de sondeo gPhyAddrs integrada). |
CHEESE_ATTEMPT=<n> | Usa gPhyAddrs[n] en lugar de gPhyAddrs[0]; si falla la inicialización de la GPU, maybe_retry incrementa el intento 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 máximo 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 depuración). |
CHEESE_NO_RETRY=1 | Sale directamente tras un fallo, sin reintentar con otro número de intento. |