
Exploit local de root para CVE-2025-21479 (Adreno KGSL) no iQOO Neo8 (SM8475) - leitura/escrita de memória física, desativa o SELinux, abre um shell root.
Código experimental de escalonamento local de privilégios para CVE-2025-21479, direcionado à GPU Qualcomm Adreno (KGSL) do vivo V2301A (iQOO Neo8 / PD2301, Snapdragon 8+ Gen 1 / SM8475, Adreno A730, Android 14, kernel GKI 5.10).
Em essência, a exploração usa o comando CP_SMMU_TABLE_UPDATE do KGSL para manipular a SMMU da GPU, obtendo capacidade de leitura/escrita arbitrária de memória física, para então desativar o SELinux, localizar símbolos do kernel e obter um shell root — sem necessidade de flashar o dispositivo nem depender de bootloader desbloqueado.
Somente para pesquisa de segurança em dispositivos que você possui ou para os quais possui autorização.
and $12, 0x3) do microcódigo da Adreno A730 (eureka_panther) é falha e classifica erroneamente pacotes Self-Draw-State (SDS) como RingBuffer (RB). Assim, ao enviar um CP_SMMU_TABLE_UPDATE forjado dentro de um bloco SDS no espaço do usuário, é possível sobrescrever o TTBR0 da GPU, fazendo a tabela de páginas da GPU apontar para páginas físicas arbitrárias — a partir daí a GPU pode ler e escrever em qualquer memória física.stabilize_rw, o mapeamento é reforçado para uma região auto-mapeada de 2 MB, permitindo leituras rápidas em grande volume (fast_pread_bytes, etc.).selinux_state.enforcing na memória física. Em seguida, despeja a imagem do kernel a partir do endereço físico fixo 0xA8000000 (limite padrão de 43 MB) — o SM8475 não tem KASLR físico — e analisa as estruturas kallsyms diretamente do dump bruto (com detecção automática de endianness); portanto, o KASLR virtual também não é um problema.__do_sys_capset com um shellcode AArch64 (prepare_kernel_cred(0) → commit_creds(), além de desativar o seccomp). O processo filho (fork) chama capset() → torna-se uid 0 → restaura o código do kernel sobrescrito → o filho reexecuta o comando especificado por você (padrão sh) no domínio u:r:shell:s0. Ao final, o código do kernel é byte a byte idêntico ao de antes da execução.As informações completas do dispositivo estão em device_info.txt.
├── source/
│ ├── cheese.c # Programa principal da exploração (inclui os dois arquivos abaixo)
│ ├── kallsyms_lookup.c # Analisador kallsyms para a imagem bruta do kernel
│ └── adrenaline.h # Definições de ioctl/comandos do KGSL (autor: hawkes)
├── exploit # Binário arm64 pré-compilado (linkagem dinâmica, minSdk 21,
│ # NDK r30-beta2)
├── device_info.txt # Informações do dispositivo de destino para ajuste de parâmetros
└── README.md
Nota: cheese.c faz #include de kallsyms_lookup.c e adrenaline.h; os três arquivos precisam estar no mesmo diretório na hora da compilação.
Qualquer Android NDK recente (r25+) serve; exemplo:
# Linkagem estática (autocontido, roda em qualquer nível de API; porém maior)
aarch64-linux-android34-clang -o cheese source/cheese.c -static
# Linkagem dinâmica (menor, depende da libc do dispositivo; o nível de API precisa corresponder ao dispositivo)
aarch64-linux-android34-clang -o exploit_dyn source/cheese.c
# Para compatibilidade com dispositivos API 21+, use aarch64-linux-android21-clang
adb push exploit /data/local/tmp/
adb shell
chmod +x /data/local/tmp/exploit
/data/local/tmp/exploit
Clique aqui para o tutorial detalhado (inclui uma ferramenta de um clique)
Após a execução bem-sucedida, você obtém um shell com uid=0 e o SELinux fica desativado nesta sessão. Após concluir o escalonamento de privilégios, o processo reexecuta argv[1..] (padrão: sh). Cada execução mostra uma contagem regressiva ("pte wait 3 second") e o progresso da leitura de 43 MB do kernel ("Read X / 43 MB").

$(find /data/app -type f -name "libksud.so" | grep resukisu) late-load --kmi android12-5.10
As constantes específicas do dispositivo estão no topo de source/cheese.c:
KERNEL_PHYS_BASE — Endereço físico base do kernel (padrão 0xA8000000, SM8475)IDMAP_PG_DIR_OFFSET — Deslocamento de idmap_pg_dir (padrão 0x245a000)selinux_state_offset — Deslocamento usado para localizar selinux_state (padrão 0x729030; o endereço físico real = endereço físico do idmap_pg_dir + esse deslocamento)gPhyAddrs[] — Endereços físicos base candidatos, tentados em sequênciaDEFAULT_KERNEL_SIZE_MB — Valor de fallback / limite para a leitura do kernelPré-requisitos: GPU da série Adreno A730/A7xx (firmware afetado), kernel cujo kallsyms possa ser analisado a partir do dump físico e ausência de KASLR físico.
can't get GPU r/w — Falha na inicialização da GPU; aguarde primeiro; se ainda não funcionar, tente CHEESE_ATTEMPT=1..n ou defina CHEESE_PHYADDR manualmente; para depurar uma única tentativa, adicione CHEESE_NO_RETRY=1.CHEESE_READ_FULL_KERNEL=1 (aumentando o limite com CHEESE_KALLSYMS_MAX_MB); depois considere CHEESE_FORCE_MANUAL_PATCHFINDER=1.cheese de código aberto na qual esta ferramenta se baseiaadrenaline.h de 2020, fornecendo as definições da interface ioctlBaseado em zhuowei/cheese (repositório original sem licença). Princípio completo em comunicado de vulnerabilidade.
Este repositório destina-se apenas a fins educacionais e de pesquisa em segurança; use somente em dispositivos que você possui ou para os quais possui autorização. O autor não se responsabiliza por qualquer uso indevido. O uso desta ferramenta pode anular a garantia ou violar os termos de serviço aplicáveis.
| Modelo | vivo V2301A (iQOO Neo8, codinome PD2301, placa 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 (desativado em tempo de execução por esta ferramenta) |
| Variável | Função |
|---|
CHEESE_PHYADDR=<phys> | Força o endereço físico base do kernel (ignora a tabela de sondagem embutida gPhyAddrs). |
CHEESE_ATTEMPT=<n> | Usa gPhyAddrs[n] em vez de gPhyAddrs[0]; após falha na inicialização da GPU, maybe_retry incrementa e tenta novamente automaticamente. |
CHEESE_SWAPPER_PG_DIR_OFF=<off> | Ignora a varredura de swapper_pg_dir e usa diretamente esse deslocamento absoluto. |
CHEESE_READ_FULL_KERNEL=1 | Lê a imagem completa do kernel (o limite padrão de leitura é DEFAULT_KERNEL_SIZE_MB = 43 MB). |
CHEESE_KALLSYMS_MAX_MB=<MB> | Substitui o limite de leitura do kallsyms. |
CHEESE_FORCE_MANUAL_PATCHFINDER=1 | Força o fallback do patchfinder manual (init_cred, prepare_kernel_cred). |
CHEESE_SKIP_GPU=1 | Ignora a fase de leitura/escrita da GPU (somente para depuração). |
CHEESE_NO_RETRY=1 | Sai imediatamente após falha, sem tentar novamente com outro número. |