O Project Zero do Google descobriu como ignorar o hipervisor Knox da Samsung (corrigido no patch de janeiro)

Na última postagem do blog Project Zero, a equipe descobriu uma maneira de contornar a proteção do kernel em tempo real da Samsung, chamada Knox Hypervisor.

A equipe do Project Zero do Google verificou uma série de explorações que permitem que os telefones Samsung que executam o pacote de segurança Samsung Knox supostamente seguro sejam atacados. O blog observa que todas as vulnerabilidades foram repassadas à Samsung, que lançou correções para elas em uma atualização de software de janeiro.


Fundo

Como parte do pacote de software de segurança Samsung Knox introduzido pela Samsung, existe um software que fica entre os aplicativos Android e o kernel chamado Hipervisor. Isso pode ser usado como uma camada adicional para proteger ainda mais os dispositivos Android. O hipervisor Samsung Knox é chamado de "Proteção do kernel em tempo real"ou RKP, abreviadamente, como farei referência no restante deste artigo.

O Kernel fica abaixo do RKP na pilha de software Android, e os aplicativos executados no dispositivo ficam no topo. A ideia por trás do RKP é fornecer uma camada extra de segurança para o dispositivo, pois todas as solicitações (memória e outros recursos) feitas pelo aplicativos para o Kernel precisam passar primeiro pelo Knox, que tenta detectar se um aplicativo está fazendo algo que ele não deveria. O RKP também fornece segurança através da obscuridade com uma camada extra para ocultar informações confidenciais que um aplicativo pode usar para comprometer o dispositivo.

A postagem do blog se aprofunda em como a memória do Android, o RKP e os sistemas operacionais em geral funcionam, então condensei e simplifiquei para fornecer uma rápida visão geral do que foi descoberto. Recomendo que você leia o artigo completo se tiver tempo, pois é muito esclarecedor.


Exploração nº 1:

KASLR ou Randomização do layout do espaço de endereço do kernel é o processo de alterar a localização do código do kernel na memória em uma quantidade aleatória na inicialização. Cada vez que o dispositivo é inicializado, o kernel é carregado em um espaço de endereço diferente (área na memória). A idéia é tornar mais difícil encontrar onde o código do kernel está localizado para atacá-lo, porque após cada inicialização o código do kernel "muda" em uma quantidade aleatória na memória. Isto parece um grande passo para prevenir possíveis invasores, mas recentes pesquisar mostrou que você pode realmente derrotar isso sem exigir um bug ou vulnerabilidade de software, já que o KASLR é realmente muito difícil de implementar de forma robusta contra invasores locais.

No caso do software RKP, a capacidade de contornar o KASLR é na verdade mais simples do que a pesquisa mencionada acima. A memória de todos os dispositivos Android é referenciada por ponteiros e para proteger os dispositivos contra ataques, sempre que os dispositivos Android imprimem ou geram saída (seja para tela ou para arquivar para logs ou depuração), as referências dos ponteiros são anônimas, - tornando impossível descobrir para onde o ponteiro realmente aponta ao ler o saída.

Pense nos indicadores de memória como uma placa de rua que aponta para um local e pense no anonimato como uma forma de desfocar isso. Assim como na televisão, o anonimato é feito após a filmagem, o Android também aplica esse anonimato no momento da saída e somente se o anonimato estiver configurado corretamente, e o autor afirma que cada dispositivo que ele encontrou teve o anonimato de ponteiro configurado corretamente. Isso pode parecer muito difícil de quebrar, mas tudo que você precisa fazer é encontrar um único ponteiro (pense em uma placa de rua) que não tenha sido anonimizado (borrado) pelo desenvolvedor do kernel (cuidado, este não é um desenvolvedor comum de aplicativos Android) quando o ponteiro é gravado nos logs ou em outro local, por exemplo. tela ou um arquivo.

Portanto, se você encontrar um ponteiro que não foi anonimizado, poderá calcular a mudança aleatória de endereço do kernel como a diferença entre os dois. Curiosamente, o autor não conseguiu encontrar um ponteiro explorável no kernel, mas o encontrou dentro do RPK onde os desenvolvedores se esqueceram de anonimizar um ponteiro na saída de depuração (registro), que surgiu por meio de um erro de digitação. Para anonimizar os ponteiros no Android você tem que usar um código especial e acontece que os desenvolvedores do RPK usaram erroneamente um 'k' minúsculo em vez de um 'K' maiúsculo. Portanto, foi relativamente simples descobrir a quantidade de deslocamento aleatório do código do kernel e atacá-lo.


Exploração nº 2:

A próxima exploração é um pouco mais complexa: o Samsung Knox protege seu dispositivo aplicando um conjunto de regras à memória do dispositivo para impedir códigos maliciosos. As regras são as seguintes:

  1. Todas as páginas (código na memória), com exceção do código do kernel, são marcadas como "Privileged Execute Never" (o que significa que o código aqui nunca pode ser executado)
  2. As páginas de dados do kernel (dados usados ​​pelo programa na memória) nunca são marcadas como executáveis ​​(portanto, o código aqui nunca pode ser executado)
  3. As páginas de código do kernel (código na memória) nunca são marcadas como graváveis ​​(portanto, nenhum código malicioso pode alterá-lo)
  4. Todas as páginas do kernel são marcadas como somente leitura na tabela de tradução do estágio 2 (a tabela que fica entre o aplicativo e o kernel para evitar ainda mais que os aplicativos saibam sobre locais reais de memória)
  5. Todas as entradas de tradução de memória são marcadas como somente leitura para aplicativos.

Iremos nos concentrar na regra 3, pois foi aqui que o autor encontrou um problema com a implementação das regras acima. De fato, o RPK marca a memória do kernel como somente leitura, no entanto, como um descuido do KASL, foi descoberto um buraco, o que levou a escrevendo código na seção supostamente "somente leitura". Para ofuscar a localização do kernel no momento da inicialização, a memória é alocada para o kernel, mas essa quantidade de memória é muito maior que o segmento de texto do kernel. Ao alocar uma quantidade maior de memória, fica muito mais difícil encontrar o código real do kernel, que pode estar em qualquer lugar e, como vimos acima, ele é movido aleatoriamente a cada inicialização do dispositivo.

_text e _etext marcam o intervalo protegido

O autor conseguiu confirmar que a memória usada pelo kernel estava de fato marcada como "somente leitura", porém o restante dessa grande quantidade de memória usada para ocultar o kernel foi não marcado como "somente leitura". Isso ocorre porque o RKP protege apenas a região que contém o texto do kernel após a aplicação do slide KASLR.


Exploração #3

Na terceira exploração, o autor conseguiu acessar outra área da memória que também deveria ser restrita ao somente leitura. O RKP protege a memória e usa um Registro de configuração do hipervisor (HCR) para controlar as principais operações do kernel. O objetivo do HCR é permitir que operações válidas e reais do kernel acessem os registros e bloqueiem ataques maliciosos. Isso é feito verificando as chamadas feitas aos registradores que controlam os recursos de virtualização. O HCR é configurado para bloquear operações específicas que seriam tratadas normalmente, permitindo que o RKP escolha se permite ou não uma solicitação.

Nesta exploração o controle HCR foi não cobrindo dois registros isso acabou sendo muito importante. O autor se aprofundou no manual de referência do ARM e descobriu que o primeiro registro lhe permitia basicamente desligar o RKP para aplicativos. O "O Registro de Controle do Sistema para EL1 (SCTLR_EL1) fornece controle de nível superior sobre o sistema, incluindo o sistema de memória." Em um mundo perfeito, o aplicativo usaria a memória mapeada por meio do RKP para que o RKP pudesse controlar o que o aplicativo poderia acessar. Contudo, desligar este registro permitiu que o RKP será desativado retornando efetivamente o dispositivo ao modo como funcionava antes da instalação do RKP - o que significa que o dispositivo é mapeado para a memória física sem a segurança extra fornecida pelo RKP. Isso, por sua vez, significava que o autor poderia ler e gravar na memória que foi originalmente e corretamente bloqueada pelo software RKP.

O segundo registro perdido teve um efeito mais sutil, mas, em última análise, igualmente devastador para a segurança. O Registro de controle de tradução para EL1 O registro (TCR_EL1) está diretamente relacionado à quantidade de memória com a qual um aplicativo funciona, chamada de página. O RKP é codificado para um tamanho de página de 4 KB porque os kernels Linux AARCH64 (como Android) usam um tamanho de tradução de 4 KB. O registro em questão (TCR_EL1) configura os chipsets ARM para o tamanho da memória que será retornada. Acontece que este registro não é protegido pelo HCR e, portanto, um invasor pode alterá-lo como o autor alterou para um tamanho de página de 64 KB.

O que isto significa é que quando a solicitação é atendida pelo RKP, a quantidade real de memória acessível agora é de 64kb em vez de 4kb. A razão é que o chipset ARM ainda controla o tamanho da página e foi definido para 64kb pela exploração. Como o RKP protege a memória contra gravação, como parte das regras listadas na exploração nº 2, a memória ainda está realmente protegida. Mas aqui está o problema - como o RKP é codificado para 4kb, ele não muda para um tamanho de página de 64kb quando o registro é atualizado, então apenas os primeiros 4kb de memória estão protegidos permitindo que o invasor faça o que ele quiser com os 60kb restantes.


Exploração #4

A última exploração que o autor mostra é fazer referência à memória onde está o software RKP, para que o invasor possa atacar o próprio software RKP. Um truque para impedir esse tipo de ataque que os kernels do Linux também usam é desmapear seu programa do espaço de endereço da memória virtual para que nenhum aplicativo possa atacá-lo porque não pode referenciá-lo.

Lembre-se de que a memória envolve ponteiros e tabelas que mapeiam a memória física para a memória virtual. Como na defesa normal neste tipo de ataque, o RKP se desmapeia para que não possa ser atacado. No entanto, onde o kernel não fornece tais habilidades, o RKP permite que um pedaço de memória seja mapeado e marcado como leitura/gravação. A única verificação é que não é o próprio kernel subjacente, pois o RKP não faz nenhuma verificação para ver se os endereços que estão sendo solicitados para serem mapeados são a área onde o próprio RKP reside na memória. Basicamente, RCP permite-se ser remapeado de volta ao espaço de endereço que os aplicativos podem acessar e, como efeito colateral, o a memória é automaticamente marcada como leitura/gravação então um invasor agora pode usar a memória como quiser.


Conclusão

Um dos maiores problemas com as quatro explorações listadas acima é que o autor menciona o quão difícil seria executá-las devido à falta de funções no kernel básico do Android. Ironicamente, o hipervisor RKP seguro forneceu todas as ferramentas necessárias para realizar os ataques. Isso mostra que, às vezes, softwares bem-intencionados causam mais problemas do que resolvem e temos sorte de termos pessoas como Gal Beniamini disposto a colocar a mão na massa e testar se a documentação corresponde ao que o software realmente faz.

Embora essas explorações pareçam assustadoras e façam Knox parecer muito vulnerável, gostaria de assegurar a todos que todos esses problemas foram resolvidos. corrigido na atualização de janeiro da Samsung. Além disso, essas explorações exigem um conhecimento muito profundo dos processadores e da programação ARM, de modo que a barreira para entrar no uso dessas explorações é astronomicamente alta.


Fonte: Projeto Zero