Engenharia reversa: Um forte aliado nos testes de segurança

Diferentemente dos outros artigos aqui no blog, trago a vocês uma análise mais manual e subjetiva de um projeto. Espero ajudar os analistas de testes a apontar brechas de segurança expostas por um aplicativo Android através do uso da engenharia reversa.

Não sou nenhum especialista em segurança, mas apenas um desenvolvedor que já vivenciou alguns problemas de segurança que podem acabar com um produto. É dever do time (analistas de teste e programadores) trabalhar em conjunto para garantir a qualidade e, por sua, vez a segurança.

Para começarmos, precisamos introduzir alguns conceitos. Começaremos por “O que é engenharia reversa?”

Em uma visão mais abrangente, é o processo de reverter a montagem de um objeto e revelar seus princípios tecnológicos ou, de um modo mais simples, revelar as peças no qual é constituído. Após desmontar e termos a visão dessas peças, podemos ver todos segredos que foram utilizado para a montagem. Dessa forma, introduzimos o nosso segundo conceito: dados sensíveis.

Dados sensíveis são as informações tanto do objeto desmontado quanto do usuário que, se uma vez expostas, podem trazer consequências financeiras, pessoais e até mesmo jurídicas.

Para contextualizar, segue alguns exemplos de dados sensíveis em Aplicativo Android.

  • Senhas pessoais.
  • Dados bancários.
  • Chave de acesso a API.
  • Fotos pessoais.

Precisamos também conhecer o processo de construção de um aplicativo Android. Vejamos a imagem abaixo:

 

1

Fluxo de construção de um aplicativo Android

  1. O desenvolvedor escreve o código na linguagem Java.
  2. Através do compilador Javac (Java compiler) os arquivos são transformados em bytecodes com a extensão .class.
  3. Após isso a ferramenta Dex converte os bytecodes Java para os bytecodes legíveis a máquina virtual do Android chamada Dalvik com a extensão .dex (Dalvik executables).

Todos os Arquivos Dex juntamente com os arquivos de recursos e descritores, são empacotados em um APK (Android application package) . Um APK nada mais é que um arquivo compactado. Se você renomear o arquivo para .zip, você verá a estrutura apresentada na figura abaixo:

2

Conteúdo de um APK

Agora que já sabemos um pouco da teoria, vamos a prática. Iremos realizar a engenharia reversa de um APK que desenvolvi para exemplificar alguns conceitos. O aplicativo é bem simples e se chama IsItSafe. Ele está disponível em código aberto e pode ser encontrado em: https://github.com/lionrick/isitsafe.

O projeto apresenta três classes chamadas KeyGuardian(Nº)  que ilustram o armazenamento de uma chave de acesso a um serviço (API), que será nosso dado sensível ao aplicativo pois, nas mãos de terceiros, essa chave poderia ser usada indevidamente. Um exemplo seria fazer uso do Dropbox com a identidade alheia, ou mesmo utilizar o backend (serviço por de trás do aplicativo) sem a apropriada permissão.

Para começar, precisaremos do APK; qualquer aplicativo instalado no dispositivo pode ser extraído de forma de um APK. Você pode fazer isso de inúmeras maneiras: a mais simples é utilizando o aplicativo de backup chamado App backup & restore encontrado em: https://play.google.com/store/apps/details?id=mobi.infolife.appbackup. Para o exemplo, basta baixar no Github: https://github.com/lionrick/isitsafe/blob/master/isitsafe.apk. Se preferir, você pode baixar o código fonte, executar a aplicação e depois realizar o backup, mas lhe aviso que é o caminho mais longo.

Precisamos agora das seguintes ferramentas para dar prosseguimento o nosso exemplo:

        Dex2Jar – Responsável em reverter os arquivos .dex para .class

        (https://github.com/pxb1988/dex2jar)

         JD-GUI – Responsável em reverter os arquivos .class para .java

(http://jd.benow.ca/)

O processo de instalação é bastante simples, basicamente consiste em baixar o arquivo e descompactar, qualquer dúvida a documentação de ambos os projetos são bastantes consistentes.

Vamos executar agora três passos que irão reverter o processo de construção de um aplicativo, dessa forma, realizando a engenharia reversa propriamente dita.

Passo 1 – Renomeie o arquivo apk para .zip e apenas extraia os arquivos para uma pasta para facilitar o acesso ao arquivos .dex.

Passo 2 – Abra o prompt de comandos, acesse a pasta do dex2jar (Se preferir, adicione ao path do sistema) e utilize o Dex2Jar com o comando:

/>  d2j-dex2jar <classes.dex>

3

Arquivo dex

 

Passo 3 – Agora basta utilizar o Java decompiler (JD-GUI) executando o arquivo  JD-GUI<versão>.jar, uma vez aberto, basta selecionar o arquivo apresentado na saída do Dex2Jar (geralmente nomeado como classes-dex2jar.jar e localizado na pasta raiz do Dex2Jar).

Vamos agora a algumas conclusões.

KeyGuardian 1

4

Engenharia reversa na classe KeyGuardian1.

 

O primeiro guardião da chave é a implementação mais comum e menos segura que encontramos. Como apresentado na figura acima, o desenvolvedor apenas definiu as chaves de acessos como constantes no código. É bem simples encontrar esse tipo de problema, pois mesmo que o projeto utilize um ofuscador (Ferramenta para embaralhar ou confundir o código para a leitura humana), o texto da constante não pode ser ofuscado, sendo assim uma brecha de segurança grave.

KeyGuardian 2

5

Engenharia reversa na classe KeyGuardian2.

 

 Nessa tentativa, o guardião tenta esconder a chave nos arquivos de recursos do Android. Também podemos considerar um erro bastante comum, existe ferramentas como o APKTool (http://ibotpeaches.github.io/Apktool/) que conseguimos visualizar todos os recursos, como textos, imagens, arquivos JSON e etc. Uma alternativa é armazenar de forma criptografada e utilizar a desencriptação em tempo de execução.

KeyGuardian 3

6

Engenharia reversa na classe KeyGuardian3.

 

O terceiro e último guardião apresenta uma opção mais viável, a chave é armazenada em código nativo (Escrito em C e localizado na pasta jni no projeto IsItSafe referenciado) o que dificulta a engenharia reversa se utilizarmos essas ferramentas populares listadas aqui no artigo, porém utilizar código nativo pode apresentar problemas de compatibilidade e o custo de desenvolvimento é maior.

Conclusões

 Os exemplos apresentado acima não foram ofuscados e apresentam apenas uma hipótese de vulnerabilidade. A ofuscação é essencial e a primeira barreira que seu aplicativo terá. É bem comum encontrar aplicativos sem nenhuma ofuscação e é explicado pelos impactos que trazem para o processo de desenvolvimento, como, por exemplo, ferramentas como o Crashlytics (https://fabric.io/kits/android/crashlytics) simplesmente ficam inutilizadas, pois nenhuma classe será legível no reporte da falha (é preciso que a ferramenta dê suporte a subir o arquivo de mapa de ofuscação).

Existem outras ferramentas e muito mais poderosas que conseguem dissecar seu aplicativo. É importante lembrar que segurança é um conjunto de procedimentos e boas práticas, não adianta confiar apenas em um procedimento.

Essa análise é complexa se feita apenas pelo time de qualidade. Uma boa prática é reunir todos os envolvidos, levantar todos os dados sensíveis do aplicativo e discutir uma forma equilibrada entre o quão sensível é o dado e os recursos (pessoas, custo e tempo) disponíveis.

Referências:

https://developer.android.com/training/articles/security-tips.html

http://www.technotalkative.com/part-9-reverse-engineering-android-applications/

http://code.tutsplus.com/articles/how-to-secure-an-android-app–cms-26385

https://github.com/lionrick/isitsafe

 

por Ricardo Santos