- CVE-2023-6241 é um bug lógico na unidade de gerenciamento de memória da GPU Arm Mali que permite a um app Android malicioso alcançar execução arbitrária de código no kernel e obter privilégios de root mesmo em um Pixel 8 com o MTE do kernel ativado
- Os afetados são dispositivos recentes com GPU Arm Mali que usam Command Stream Frontend (CSF), incluindo Google Pixel 7 e Pixel 8
- A vulnerabilidade funciona explorando uma breve janela em que o lock é liberado durante a expansão de memória JIT, criando uma inconsistência entre o mapeamento da GPU e o array de backing pages
- O exploit usa um mapeamento de GPU remanescente sobre backing pages liberadas, faz com que essa página seja reutilizada como PGD do contexto da GPU e então mapeia memória do kernel e código do kernel
- O MTE detecta incompatibilidades entre ponteiros e tags de memória, mas este fluxo de ataque ocorre fora do escopo de proteção do MTE, porque a GPU acessa diretamente endereços físicos
Escopo da vulnerabilidade e status do patch
- CVE-2023-6241 é uma vulnerabilidade da GPU Arm Mali que permite a um app Android malicioso obter execução arbitrária de código no kernel e privilégios de root no dispositivo
- Foi reportada à Arm em 15 de novembro de 2023 e corrigida no driver Arm Mali r47p0, publicado em 14 de dezembro de 2023
- A correção no Android foi incluída na atualização de segurança de março de 2024
- Os afetados são dispositivos recentes com GPU Arm Mali que usam o recurso CSF (Command Stream Frontend), com Google Pixel 7 e Pixel 8 citados como exemplos
- Foi confirmado que o exploit funciona mesmo com o MTE do kernel ativado no Pixel 8
Modelo de defesa do MTE no Arm64
- MTE (Memory Tagging Extension) é um recurso de hardware dos processadores Arm modernos que compara as tags de ponteiros e blocos de memória para detectar corrupção de memória
- Ponteiros Arm64 têm 64 bits, mas o espaço de endereçamento real da aplicação normalmente é de 52 bits ou menos, então parte dos bits superiores pode ser usada para armazenar tags
- Em overflows lineares, a tag de um bloco de memória adjacente pode diferir da tag do ponteiro, e em use-after-free pode surgir incompatibilidade por causa da mudança de tag durante liberação e realocação
- Diferentemente de mitigação em estágios mais finais, como kCFI, o MTE é uma mitigação de estágio inicial que tenta detectar a corrupção de memória no momento em que ela ocorre
- Como o número de bits de tag é limitado, colisões são inevitáveis, mas mesmo usando apenas tags de 4 bits a taxa de sucesso aleatória cai para 1/16
- Se um ataque de canal lateral como Spectre vazar os valores do ponteiro e do bloco de memória, é possível acertar a tag correta e contornar o MTE, mas esse tipo de vazamento normalmente só é viável para atacantes locais
- Atualmente, apenas o Google Pixel 8 permite ativar o MTE nas opções de desenvolvedor, e ele vem desativado por padrão
- Ativar o MTE no kernel exige etapas adicionais
A race condition na memória JIT da Mali
- Um app em user space que usa o driver da GPU Mali abre o arquivo do driver e cria/inicializa o objeto de kernel
kbase_contextcom chamadasioctl kbase_contextgerencia vários tipos de memória compartilhada entre o dispositivo GPU e a aplicação em user space- Regiões de memória da GPU Mali são representadas por
kbase_va_region;nr_pagesindica o tamanho virtual egpu_alloc->nentso número real de backing pages - Memória JIT é memória nativa cujo ciclo de vida é gerenciado pelo driver do kernel, e o app a aloca ou libera por meio de comandos de GPU
- Em GPUs com CSF, comandos de software e hardware ficam em filas diferentes
KBASE_IOCTL_KCPU_QUEUE_CREATEpode criar umakbase_kcpu_command_queueKBASE_IOCTL_KCPU_QUEUE_ENQUEUEenfileira comandosBASE_KCPU_COMMAND_TYPE_JIT_ALLOCeBASE_KCPU_COMMAND_TYPE_JIT_FREEsão usados para alocação e liberação JIT
kbase_jit_allocateprocura uma região reutilizável no pool de memória JIT liberada e, se o tamanho físico for insuficiente, amplia as backing pages comkbase_jit_growkbase_jit_growpode liberar temporariamentekctx->reg_lockekctx->mem_partials_lockdurante a chamada akbase_mem_pool_growkctx->reg_lockprotege o acesso concorrente às regiões de memória, então esse intervalo sem lock vira a janela da race
Fluxo de disparo da CVE-2023-6241
- Se a GPU acessa o endereço de uma região de memória sem backing em páginas físicas, ocorre uma falha de acesso à memória da GPU
kbase_mmu_page_fault_workerverifica se a região pode ser expandida e então pode alocar e mapear imediatamente as backing pages necessárias- Uma região JIT satisfaz, quando criada, a condição GROWABLE_FLAGS_REQUIRED, que inclui
KBASE_REG_PF_GROWeKBASE_REG_GPU_WR - A flag
KBASE_REG_DONT_NEED, anexada quando a região JIT é liberada, é removida porkbase_mem_evictable_unmakeno começo dekbase_jit_grow - Como resultado, se for gerada uma page fault da GPU na mesma região JIT durante a janela de race enquanto
kbase_mem_pool_growestá em execução, o handler de fault pode expandir essa região - Se o handler de fault altera
reg->gpu_alloc->nents, os valoresold_sizeedeltaarmazenados anteriormente porkbase_jit_growdeixam de refletir o estado real - Depois disso,
kbase_alloc_phy_pages_helper_lockedekbase_mem_grow_gpu_mappingexecutam alocação de backing pages e mapeamento da GPU com valores obsoletos, criando uma inconsistência entre o mapeamento da GPU e o arraypages - Essa race é fácil de vencer porque
kbase_mem_pool_growenvolve uma grande alocação de memória
Como o ataque mudou após o patch do GHSL-2023-005
- Na vulnerabilidade anterior GHSL-2023-005, outra thread podia reduzir a região JIT com
KBASE_IOCTL_MEM_COMMIT, invalidandoold_sizeedelta - Após o patch do GHSL-2023-005, o
ioctl KBASE_IOCTL_MEM_COMMITnão pode mais alterar o tamanho da memória JIT - Na CVE-2023-6241, durante a janela de race não é possível reduzir a região, apenas aumentá-la
- Quando a região apenas cresce, no máximo algumas backing pages finais deixam de ser mapeadas na GPU; o mapeamento contínuo desde o início ainda é preservado, então isso não causa um problema imediato
- O exploit cria um novo mapeamento após o gap não mapeado com faults adicionais da GPU e depois usa a liberação JIT para fazer o ponto de shrink cair dentro desse gap, produzindo um estado explorável
A suposição frágil na remoção de mapeamentos da GPU
kbase_mmu_teardown_pgd_pagespercorre a tabela de páginas da GPU e marca entradas como inválidas para remover o mapeamento de endereços da GPU- Essa função assume que, se um PTE de nível superior está inválido, toda a grande faixa de endereços coberta por essa entrada já está desmontada e por isso pode ser ignorada
- Um único PTE de nível 2 cobre uma faixa de 512 páginas
- Em uma
kbase_va_regionnormal, os endereços virtuais mapeados são sempre contínuos a partir do início da região, sem gaps intermediários, então esse comportamento de pular é seguro - O exploit da CVE-2023-6241 cria um gap não mapeado entre mapeamentos e então posiciona o ponto inicial do shrink dentro desse gap
kbase_mmu_teardown_pgd_pagesencontra um PTE inválido de nível 2 e pula 512 páginas, mas alguns endereços após esse ponto ainda podem estar efetivamente mapeados- Os endereços da GPU pulados incorretamente mantêm acesso àquelas páginas físicas mesmo depois de as backing pages serem liberadas
Como isso leva à execução de código no kernel
- Quando a região JIT é liberada, as backing pages são devolvidas, mas o mapeamento incorretamente remanescente da GPU continua conseguindo acessar as páginas liberadas
- Essas backing pages liberadas podem depois ser reutilizadas como outras páginas do kernel
- Uma das técnicas usadas é fazer com que uma backing page liberada seja reutilizada como PGD (page table global directory) de um
kbase_contextda GPU - A alocação de backing pages no driver Mali é hierárquica
- Primeiro, ele obtém páginas do
kbase_mem_pooldokbase_contextatual - Se não houver páginas suficientes, usa
pool->next_pool - Se ainda assim for insuficiente, aloca páginas diretamente pelo buddy allocator do kernel
- Primeiro, ele obtém páginas do
pool->next_poolé um pool de memória gerenciado pelo driver Mali e compartilhado por todos oskbase_context, sendo também usado para alocação de PGD de contextos de GPU- Se a página liberada for reutilizada como PGD, a GPU pode voltar a escrever nesse PGD pelo endereço da GPU que permaneceu válido
- Ao reescrever o PGD, é possível mapear memória arbitrária do kernel e código do kernel na GPU
- Nesse estado, reescrever o código do kernel permite execução arbitrária de código no kernel, e ler/escrever dados do kernel também permite alterar credenciais de processos e desativar o SELinux
- O exploit para Pixel 8 e as notas de configuração estão disponíveis no repositório do GitHub Security Lab
Por que é possível contornar o MTE
- Esse fluxo de exploit não exige uma etapa separada específica para burlar o MTE
- O MTE verifica se a tag do bloco de memória apontado corresponde à tag do ponteiro para detectar dereferências incorretas
- No momento em que a CVE-2023-6241 é disparada, surge uma inconsistência entre o array
pagese o mapeamento da GPU, mas vistos separadamente eles não contêm entradas inválidas - Quando
kbase_mmu_teardown_pgd_pagesdeixa de remover um mapeamento da GPU, o endereço físico de uma página de memória liberada permanece na tabela de páginas da GPU - Quando a GPU acessa essa página liberada, ela acessa diretamente o endereço físico e não passa por verificações de dereferência de ponteiro
- Também não está claro qual é o efeito do MTE sobre acessos de memória da GPU
- Como resultado, esse bug contorna a proteção do MTE explorando o caminho em que a GPU, como coprocessador, acessa diretamente a memória física
Superfície de ataque que continua existindo mesmo após o MTE
- A CVE-2023-6241 mostra que, mesmo em um Pixel 8 com o MTE do kernel ativado, um único bug pode bastar para chegar à execução arbitrária de código no kernel
- O MTE é um avanço importante na mitigação de corrupção de memória e pode tornar muitas vulnerabilidades desse tipo inexplotáveis, mas não é uma defesa universal
- Neste caso, a GPU contorna o MTE ao acessar diretamente a memória física
- À medida que aumentam as mitigaçãoes de hardware e software no lado da CPU, coprocessadores e seus drivers de kernel podem continuar sendo uma forte superfície de ataque
1 comentários
Comentários do Hacker News
O ponto central aqui é que a GPU há muito tempo é uma dor de cabeça no Android
A GPU tem privilégios de acesso muito fortes ao AP, então consegue, na prática, contornar as mitigações colocadas na frente. Bugs no código de mapeamento do driver levam a primitivas de ataque poderosas e vêm sendo explorados repetidamente em exploits reais em circulação. No fim, parece difícil que algo mude muito até a arquitetura ser redesenhada
O interessante nessa vulnerabilidade é que ela é um bug lógico na unidade de gerenciamento de memória da GPU Arm Mali e que consegue contornar a Memory Tagging Extension
Mas o restante do texto parece explicar que a causa real é uma condição de corrida, e que o use-after-free é consequência disso
Será que instalações do GrapheneOS anteriores à atualização de março também foram afetadas?
Às vezes eles adotam o nível de patch de segurança do AOSP antes do lançamento, ou fazem backport de correções de segurança de fontes do AOSP ou do kernel que ainda não foram publicadas
Correção: ignorem. Confundi com um post recente do blog do GrapheneOS dizendo que “encontraram um problema em que o MTE também é aplicado a todos os apps do sistema”. O GrapheneOS trouxe o “nível completo de patch de segurança de 2024-03-05” no lançamento 2024030600, então parece que este patch também foi incluído
A segurança de memória probabilística do Arm MTE é um degrau rumo ao hardware determinístico CHERI, https://saaramar.github.io/memory_safety_blogpost_2022/ e https://news.ycombinator.com/item?id=39668053
A mitigação correta deve mirar a operação primitiva de ataque primária, isto é, a causa-raiz do bug. Entre as soluções de hardware estão CHERI (Morello, CheriIoT) e MTE; entre as mitigações de software estão kalloc_type+dataPAC, AUTOSLAB, Firebloom, GuardedMemcpy, CastGuard e redução da superfície de ataque; e, entre as linguagens de programação seguras, estão Rust e Swift. MTE e CHERI se encaixam bem e ajudam a eliminar bugs desse tipo na causa-raiz. MSR, MSRC e Azure Silicon impulsionaram a redução do CHERI até a menor especificação de núcleo RISC-V, o RISC-V32E
A Microsoft Research publicou como open source uma pilha de hardware/software CHERI para dispositivos IoT, https://msrc.microsoft.com/blog/2023/02/first-steps-in-cheri...
Um microcontrolador baseado em CHERI tem como objetivo obter garantias de segurança muito fortes ao coprojetar a arquitetura do conjunto de instruções (ISA), a interface binária de aplicação (ABI), o modelo de isolamento e o núcleo da pilha de software. Esse microcontrolador consegue mitigação determinística de segurança espacial por meio de recursos CHERI-ISA, mitigação determinística de segurança temporal de heap e pilha entre compartimentos por meio de barreiras de carregamento, zeragem, recuperação e controle de fluxo de informações de 1 bit, além de compartimentalização fina por meio de recursos CHERI-ISA adicionais e de um pequeno monitor
David Chisnall, U of Cambridge, https://lobste.rs/s/gnjx2n/c_can_be_memory_safe#c_9ohzku via https://eclypsium.com/blog/a-faster-path-to-memory-safety-ch...
“Há cerca de 13 bilhões de linhas de código C/C++ open source entrando em várias bases de computação confiáveis, e o número é ainda maior se incluir código proprietário. Mesmo que todo mundo parasse de escrever C/C++ agora e todos os engenheiros de software se concentrassem em reescrever código legado em linguagens seguras, levaria de 5 a 10 anos para substituir tudo, e é provável que surgissem muitos bugs lógicos ao trocar código há muito validado por código novo que exige algoritmos e estruturas de dados diferentes para se adequar aos idiomas permitidos por linguagens seguras.”
“Se apenas pararmos de escrever código C/C++ sem fazer reescritas, no ritmo normal de substituição de código, levará cerca de 50 anos para que as bases de computação confiáveis se tornem completamente seguras. Se nem todos concordarem em parar com C/C++, será no mínimo 100 anos.”
“Por outro lado, se os principais fabricantes de CPU lançarem CPUs CHERI em até 5 anos, então, sem que programadores mudem seu comportamento, a maioria das máquinas — especialmente as de alto valor — terá segurança de memória em até 15 anos a partir de hoje.”
A primeira é o sistema Sonata: https://github.com/lowRISC/sonata-system. Ele consiste em uma PCB dedicada com FPGA e vários periféricos e headers. O design da PCB está concluído e deverá ficar disponível pela Mouser; até o layout da placa é open source, então, se quiser, você pode montá-la por conta própria. No momento, o trabalho está no RTL para o FPGA. Quando estiver pronto, você terá um sistema do tipo microcontrolador baseado em CHERIoT, com documentação e ferramentas
Além disso, também estão criando o sistema Symphony, que combina o Sonata com a raiz de confiança OpenTitan Earl Grey: https://github.com/lowRISC/symphony-system
Além disso, da última vez que vi, CHERI não era sólido. Ainda dava para escrever bugs de memória em cima dele; isso já foi corrigido?
Só a discussão de bicicletário já deve levar esse tempo
Parece que este exploit poderia ser escrito na maioria das linguagens, incluindo Rust
O hardware é ruim a esse ponto? Nossa...
Essa abordagem é possível porque a GPU não permite acesso relativamente direto ao hardware como a CPU
É uma pesquisa e um texto excelentes, mas achei um pouco inesperado — e ao mesmo tempo agradável — ver isso no blog do GitHub.
Alguém sabe qual é o “motivo de negócio” do GitHub para fazer esse tipo de pesquisa? Não estou dizendo que necessariamente precise haver um motivo de negócio, mas achei meio surpreendente ver isso aqui.
Essa função de pesquisa acabou se tornando o GitHub Security Lab. A Semmle criou o CodeQL, que hoje é oferecido pelo GitHub (https://docs.github.com/en/code-security/code-scanning/intro...). O GitHub e a Microsoft querem associar o CodeQL a “insights profundos de segurança” (https://www.microsoft.com/en-us/security/blog/2023/11/02/ann...)
Por isso, continuam financiando esse tipo de pesquisa de segurança inovadora, e os profissionais de segurança da indústria agradecem.
Também existe o app do GitHub, e a segurança desse app não está fora do escopo do GitHub. Se um atacante conseguir instalar um app escondido no celular, pode fazer várias coisas se passando pelo usuário. Se for alguém com influência no GitHub, o dano pode ser considerável; portanto, encontrar esse tipo de vulnerabilidade também é do interesse do GitHub.
Então pode haver interesse em verificar e validar os recursos de segurança do hardware Arm para sandboxing usando MTE.
Em primeira análise, um produto como o GitHub não precisa necessariamente de especialistas em segurança Android. Mas, no longo prazo, há benefícios potenciais.
[0]: https://en.wikipedia.org/wiki/Basic_research
É surpreendente que ainda não tenha havido casos de CPUs e celulares com pouca ou nenhuma GPU sendo vendidos como business phones.
As vantagens em termos de segurança, custo e consumo de energia parecem claras.