1 pontos por GN⁺ 2024-03-20 | 1 comentários | Compartilhar no WhatsApp
  • 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_context com chamadas ioctl
  • kbase_context gerencia 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_pages indica o tamanho virtual e gpu_alloc->nents o 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_CREATE pode criar uma kbase_kcpu_command_queue
    • KBASE_IOCTL_KCPU_QUEUE_ENQUEUE enfileira comandos
    • BASE_KCPU_COMMAND_TYPE_JIT_ALLOC e BASE_KCPU_COMMAND_TYPE_JIT_FREE são usados para alocação e liberação JIT
  • kbase_jit_allocate procura uma região reutilizável no pool de memória JIT liberada e, se o tamanho físico for insuficiente, amplia as backing pages com kbase_jit_grow
  • kbase_jit_grow pode liberar temporariamente kctx->reg_lock e kctx->mem_partials_lock durante a chamada a kbase_mem_pool_grow
  • kctx->reg_lock protege 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_worker verifica 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_GROW e KBASE_REG_GPU_WR
  • A flag KBASE_REG_DONT_NEED, anexada quando a região JIT é liberada, é removida por kbase_mem_evictable_unmake no começo de kbase_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_grow está em execução, o handler de fault pode expandir essa região
  • Se o handler de fault altera reg->gpu_alloc->nents, os valores old_size e delta armazenados anteriormente por kbase_jit_grow deixam de refletir o estado real
  • Depois disso, kbase_alloc_phy_pages_helper_locked e kbase_mem_grow_gpu_mapping executam alocação de backing pages e mapeamento da GPU com valores obsoletos, criando uma inconsistência entre o mapeamento da GPU e o array pages
  • Essa race é fácil de vencer porque kbase_mem_pool_grow envolve 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, invalidando old_size e delta
  • Após o patch do GHSL-2023-005, o ioctl KBASE_IOCTL_MEM_COMMIT nã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_pages percorre 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_region normal, 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_pages encontra 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_context da GPU
  • A alocação de backing pages no driver Mali é hierárquica
    • Primeiro, ele obtém páginas do kbase_mem_pool do kbase_context atual
    • 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
  • pool->next_pool é um pool de memória gerenciado pelo driver Mali e compartilhado por todos os kbase_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 pages e o mapeamento da GPU, mas vistos separadamente eles não contêm entradas inválidas
  • Quando kbase_mmu_teardown_pgd_pages deixa 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

 
GN⁺ 2024-03-20
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

    • Acho que GPUs móveis feitas pela metade deveriam parar de incluir sua própria MMU e usar a MMU de entrada/saída padrão
    • O que AP significa aqui?
  • 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?

    • Um dos principais objetivos do GrapheneOS é distribuir atualizações de segurança o mais rápido possível, então, se foi corrigido no upstream, quase certamente entrou no GrapheneOS
      À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
    • Parece ser um problema próximo do hardware dentro da GPU, talvez relacionado a firmware, então achei que ainda seria afetado mesmo depois da atualização de março. Aquela atualização era relacionada à pilha Bluetooth
      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.”

    • Se você tem interesse em CHERI para usos semelhantes a embarcados/IoT, a lowRISC está criando algumas plataformas de avaliação baseadas em FPGA para CHERIoT: https://www.sunburst-project.org/
      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
    • Também existe o Solaris SPARC ADI. Por causa do estado atual da Oracle e do Solaris SPARC, a maioria das pessoas se esquece dele, o que é uma pena
    • Este é um bug de hardware fora da CPU, então não sei como CHERI ajudaria
      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?
    • “5 a 10 anos para substituir tudo” parece otimista demais, se estivermos falando em anos
      Só a discussão de bicicletário já deve levar esse tempo
    • O problema fundamental parece ser que o usuário executa código malicioso e explora alguma colisão de hash da MMU
      Parece que este exploit poderia ser escrito na maioria das linguagens, incluindo Rust
  • O hardware é ruim a esse ponto? Nossa...

    • O hardware de GPU é cheio de bugs. Eles só refazem o hardware quando o problema não pode ser contornado no driver a um custo aceitável
      Essa abordagem é possível porque a GPU não permite acesso relativamente direto ao hardware como a CPU
    • Este é um bug de driver executado na 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.

    • Man Yue Mo trabalhava na Semmle antes de ela ser adquirida pelo GitHub (https://blog.sonatype.com/steps-to-responsible-disclosure, https://github.blog/2019-09-18-github-welcomes-semmle/)
      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.
    • Este trabalho veio do Security Lab do GitHub: https://securitylab.github.com/
    • Com a aquisição pela Microsoft, passaram a existir recursos para patrocinar esse tipo de pesquisa.
      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.
    • O GitHub também tem runners do Actions hospedados para Arm.
      Então pode haver interesse em verificar e validar os recursos de segurança do hardware Arm para sandboxing usando MTE.
    • Na prática, vejo isso como algo mais próximo de pesquisa básica [0].
      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.

    • A desvantagem óbvia é não ter uma tela sensível ao toque de alta resolução, o que nos levaria de volta ao Blackberry ou ao Palm Treo. Na prática, esses aparelhos eram vendidos como business phones.