Android

29 jun, 2016

Protegendo a pilha de mídia

Publicidade

Este artigo é do Android Security Team. Ele foi escrito por Dan Austin e Jeff Vander Stoep. A tradução foi feita pela Redação iMasters com autorização. 

Para ajudar a tornar o Android mais seguro, nós encorajamos e recompensamos pesquisadores que descobrem vulnerabilidades. Em 2015, vários bugs no libstagefright do mediaserver foram revelados para o Google. Nós lançamos atualizações para esses problemas com nossos boletins de segurança de agosto e setembro de 2015.

Além de resolver problemas em uma base mensal, nós também temos trabalhado em novos recursos de segurança projetados para melhorar o modelo de segurança existente e proporcionar defesa adicional em profundidade. Essas medidas de defesa tentam alcançar dois objetivos:

  • Prevenção: evitar que bugs se transformem em vulnerabilidades
  • Contenção: proteger o sistema ao retirar privilégios e isolar componentes que lidam com conteúdo não confiável

Prevenção

A maioria das vulnerabilidades encontradas no libstagefright foi heaps overflow resultantes de integer overflows sem sinal. Um número de integer overflows no libstagefright permitiu que um atacante alocasse um buffer com menos espaço do que o necessário para os dados de entrada, resultando em um buffer overflow no heap.

O resultado de um integer overflow sem sinal está bem definido, mas o comportamento que se segue podem ser inesperado ou inseguro. Em contraste, integer overflows assinalados são considerados um comportamento indefinido em C/C++, o que significa que o resultado de um overflow não é garantido, e o autor do compilador pode escolher o comportamento resultante – tipicamente o que é mais rápido ou mais simples. Nós adicionamos alterações do compilador que são projetadas para fornecer padrões mais seguros para ambos os integer overflows – sem sinal e com sinal.

O UndefinedBehaviorSanitizer (UBSan) faz parte do conjunto de ferramentas do compilador LLVM/Clang que detecta comportamento indefinido ou não intencional. O UBSan pode verificar se há vários tipos de comportamento indefinidos e sem segurança, incluindo integer overflows sem sinal e com sinal. Essas verificações adicionam código ao executável resultante, testando condições de integer overflow durante o tempo de execução. Por exemplo, a figura 1 mostra o código-fonte para a função parseChunk no componente MPEG4Extractor do libstagefright depois que o patch fornecido pelo pesquisador original foi aplicado. A modificação, que está contida na caixa preta abaixo, aparece para evitar que integer overflows ocorram. Infelizmente, enquanto SIZE_MAX e size são valores de 32 bits, chunk_size é um valor de 64 bits, resultando em uma verificação incompleta e o potencial integer overflow. Na linha dentro da caixa vermelha, a adição de size e chunk_size pode resultam em um integer overflow e na criação de um buffer menor do que os elementos size. O memcpy subsequente poderia, então, levar a uma corrupção de memória explorável, como size + chunk_size poderiam ser menores do que size, que é destacado na caixa azul. A mecânica de um potencial vetor exploit para essa vulnerabilidade é explicada com mais detalhes pelo Project Zero.

image01

Figura 1. Código-fonte demonstrando um sutil integer overflow sem sinal.

A figura 2 compara o conjunto gerado a partir do segmento de código acima com uma segunda versão compilada com sanitização de integer ativada. A operação add que resulta no integer overflow está contida na caixa vermelha.

Na versão unsanitized, size (r6) e chunk_size (r7) são adicionados juntos, potencialmente resultando em overvlow de r0 e sendo menor que size. Em seguida, o buffer é alocado com o tamanho especificado em r0, e o tamanho dos bytes é copiado para ele. Se r0 é inferior a R6, isso resulta em corrupção de memória.

Na versão sanitizada, size (r7) e chunk_size (r5) são adicionados juntos, com o resultado armazenado em r0. Mais tarde, r0 é verificado contra r7; se r0 é menor do que r7, como indicado pelo código de condição CC, r3 é definido como 1. Se r3 é 1, e o bit de transporte foi definido, então um integer overflow ocorreu, e abortar é acionado, evitando danos na memória.

Note que a verificação incompleta fornecida no patch não foi incluída na figura 2. O overflow ocorre na operação de adição da alocação de buffer. Essa adição desencadeia uma verificação de sanitização inteiro, o que transforma essa falha explorável em um abort inofensivo.

image02

Figura 2. Comparando a saída unsanitized e sanitizada do compilador.

Enquanto os integer sanitizers foram feitos originalmente para atuarem como ferramentas de higiene de código, eles efetivamente impedem a maioria das vulnerabilidades libstagefright relatadas. Acionar as verificações de integer overflow foi apenas o primeiro passo. Impedir que o tempo de execução fosse abortado ao encontrar e corrigir integer overflows, a maioria não explorável, representou um grande esforço da equipe de mídia do Android. A maioria dos overflows descobertos foi corrigida, e aqueles que permanecem (principalmente por razões de desempenho) foram verificados e marcados como seguros para evitar a anulação de tempo de execução.

No Android N, a detecção de integer overflow sem sinal e com sinal é habilitada em toda a pilha de mídia, incluindo libstagefright. Isso torna mais difícil explorar integer overflows, e também ajuda a evitar futuras adições ao Android a partir da introdução de novos bugs de integer overflow.

Contenção

Para Android M e anteriores, o processo mediaserver no Android foi responsável pela maioria das tarefas relacionadas à mídia. Isso significava que era necessário acesso a todas as permissões necessárias por essas responsabilidades e, embora o mediaserver tenha rodado em sua própria sandbox, ele ainda tinha acesso a uma grande quantidade de recursos e capacidades. É por isso que os erros libstagefright, a partir de 2015, foram significativos – o mediaserver podia acessar vários recursos importantes em um dispositivo Android, incluindo câmera, microfone, gráficos, telefone, Bluetooth e Internet.

Uma análise de causa raiz mostrou que os bugs libstagefright ocorreram principalmente no código responsável pela análise de formatos de arquivo e codecs de mídia. Isso não é surpreendente – analisar formatos de arquivo complexos e codecs ao se tentar otimizar a velocidade é difícil, e o grande número de casos limites faz com que tal código seja suscetível a entradas malformadas acidentais e maliciosos.

No entanto, analisadores de mídia não requerem acesso à maioria das permissões privilegiadas detidas pelo mediaserver. Devido a isso, a equipe de mídia re-arquitetou o mediaserver no Android N para melhor aderir ao princípio do menor privilégio. A figura 3 ilustra como o mediaserver monolítico e suas permissões foram divididos, usando as seguintes heurísticas:

  • código de análise movido em sandboxes não privilegiadas que têm poucas ou nenhuma permissão
  • componentes que requerem permissões sensíveis movidos em sandboxes separadas que só concedem acesso aos recursos específicos que o componente necessita. Por exemplo, apenas o cameraserver pode acessar a câmera, apenas o audioserver pode acessar Bluetooth, e apenas o drmserver podem acessar recursos de DRM.

image03

Figura 3. Como o mediaserver e suas permissões foram divididos no Android N.

Comparar o potencial impacto dos bugs libstagefright no Android N e em versões mais antigas demonstra o valor dessa estratégia. Ganhar execução de código no libstagefright prévia garante acesso a todas as permissões e recursos disponíveis para o processo de mediaserver monolítico, incluindo driver gráfico, controlador da câmera, ou soquetes, que apresentam uma superfície rica de ataque do kernel.

No Android N, o libstagefright é executado dentro da sandbox mediacodec com acesso a muito poucas permissões. Acessos a câmera, microfone, fotos, telefone, Bluetooth e Internet, bem como o carregamento de código dinâmico, são permitidos pelo SELinux. Interação com o kernel é ainda mais restrito pelo seccomp. Isso significa que comprometer o libstagefright concederia ao atacante o acesso a um número significativamente menor de permissões e também atenuaria a escalação de privilégios, reduzindo a superfície de ataque exposta pelo kernel.

Conclusão

O projeto de proteção de mídia é um esforço contínuo focado em mover funcionalidades para sandboxes menos privilegiadas e reduzir ainda mais as permissões concedidas a essas sandboxes. Enquanto as técnicas discutidas aqui foram aplicadas ao framework de mídia do Android, elas são adequadas em toda a base de código do Android. Essas técnicas de proteção – e outras – estão sendo aplicadas ativamente a componentes adicionais dentro do Android.

Como sempre, agradecemos o feedback sobre o nosso trabalho e sugestões para saber como podemos melhorar o Android são bem-vindas. Contacte-nos em security@android.com.

***

Este artigo é do Android Security Team. Ele foi escrito por Dan Austin e Jeff Vander Stoep. A tradução foi feita pela Redação iMasters com autorização. Acesse o original em http://android-developers.blogspot.com.br/2016/05/hardening-media-stack.html.