Análise de vulnerabilidades do Evil Parcel

1. Introdução


Em meados de abril, publicamos notícias sobre o cavalo de Troia Android.InfectionAds.1 , que explorava várias vulnerabilidades críticas no Android. Um deles, o CVE-2017-13156 (também conhecido como Janus ), permite que o malware infecte arquivos APK sem danificar a assinatura digital. O outro é o CVE-2017-13315. Ele concede ao cavalo de Troia privilégios estendidos, para que ele possa instalar e desinstalar aplicativos independentemente do usuário. Uma análise detalhada do Android.InfectionAds.1 está disponível em nossa biblioteca de vírus ; enquanto estivermos aqui, abordaremos a vulnerabilidade CVE-2017-13315 e veremos o que ela faz.

CVE-2017-13315 pertence ao grupo de vulnerabilidades chamado EvilParcel. Eles são encontrados em várias classes de sistema Android. Os erros nessas classes permitem substituir informações durante a troca de dados entre aplicativos e o sistema. Os malwares que exploram as vulnerabilidades do EvilParcel recebem privilégios mais altos e tornam-se capazes do seguinte:

  • instalar e remover aplicativos com quaisquer permissões sem confirmação dos usuários;
  • infectar o software instalado no dispositivo e substituir originais limpos por cópias infectadas quando usadas em conjunto com outras vulnerabilidades;
  • redefinindo o PIN da tela de bloqueio em dispositivos Android.

A partir de agora, conhecemos 7 vulnerabilidades desse tipo:
  • CVE-2017-0806 (erro na classe GateKeeperResponse), publicado em outubro de 2017;
  • CVE-2017-13286 (erro na classe OutputConfiguration, publicada em abril de 2018;
  • CVE-2017-13287 (erro na classe VerifyCredentialResponse), publicada em abril de 2018;
  • CVE-2017-13288 (erro na classe PeriodicAdvertizingReport), publicada em abril de 2018;
  • CVE-2017-13289 (erro na classe ParcelableRttResults), publicada em abril de 2018;
  • CVE-2017-13311 (erro na classe SparseMappingTable), publicada em maio de 2018;
  • CVE-2017-13315 (erro na classe DcParamObject), publicado em maio de 2018.

Todos eles representam uma ameaça para os dispositivos que executam o Android 5.0 - 8.1 sem a atualização de segurança de maio de 2018 (ou posterior) instalada.

Pré-requisitos para vulnerabilidades do EvilParcel


Vamos ver como as vulnerabilidades do EvilParcel podem aparecer. Primeiro de tudo, precisamos examinar alguns recursos dos aplicativos Android. Todos os programas Android interagem entre si, bem como com o sistema operacional, enviando e recebendo objetos Intent. Esses objetos podem conter um número arbitrário de pares de valores-chave dentro de um objeto Bundle.

Ao transferir um Intent, um objeto Bundle é convertido (serializado) em uma matriz de bytes agrupada no Parcel e depois desserializado automaticamente após a leitura de chaves e valores de um Bundle serializado.

No Bundle, a chave é a string e o valor pode ser quase qualquer coisa. Por exemplo, pode ser um tipo primitivo, uma sequência ou um contêiner com tipos ou sequências primitivas. Também pode ser um objeto Parcelable.

Assim, o Bundle pode conter um objeto de qualquer tipo que implemente a interface Parcelable. Para isso, precisamos implementar os métodos writeToParcel () e createFromParcel () para serializar e desserializar o objeto.

Para ilustrar nosso ponto, vamos criar um pacote serial simples. Escreveremos um código que coloca três pares de valores-chave no pacote e o serializa:


Figura 1. Estrutura de um objeto de pacote configurável serializado

Observe os recursos específicos da serialização de pacote configurável:

  • todos os pares de valores-chave são gravados sequencialmente;
  • o tipo de valor é indicado antes de cada valor (13 para a matriz de bytes, 1 para o número inteiro, 0 para a cadeia, etc.);
  • o tamanho dos dados de comprimento variável é indicado antes dos dados (comprimento da string, número de bytes da matriz);
  • todos os valores são alinhados em 4 bytes.


Todas as chaves e valores são gravados no Bundle sequencialmente, de modo que, ao acessar qualquer chave ou valor de um objeto Bundle serializado, o último desserialize completamente, inicializando também todos os objetos Parcelable contidos.

Então, qual poderia ser o problema? O problema é que algumas classes de sistema que implementam o Parcelable podem conter erros nos métodos createFromParcel () e writeToParcel (). Nessas classes, o número de bytes lidos em createFromParcel () será diferente do número de bytes gravados em writeToParcel (). Se você colocar um objeto dessa classe dentro de um Bundle, os limites do objeto dentro do Bundle serão alterados após a nova serialização. Isso cria as condições para explorar uma vulnerabilidade do EvilParcel.

Vamos ver um exemplo de uma classe que contém este erro:

class Demo implements Parcelable { byte[] data; public Demo() { this.data = new byte[0]; } protected Demo(Parcel in) { int length = in.readInt(); data = new byte[length]; if (length > 0) { in.readByteArray(data); } } public static final Creator<Demo> CREATOR = new Creator<Demo>() { @Override public Demo createFromParcel(Parcel in) { return new Demo(in); } }; @Override public void writeToParcel(Parcel parcel, int i) { parcel.writeInt(data.length); parcel.writeByteArray(data); } } 

Se o tamanho da matriz de dados for 0, ao criar um objeto, um int (4 bytes) será lido em createFromParcel () e dois int (8 bytes) serão gravados em writeToParcel (). O primeiro int será gravado chamando explicitamente writeInt. O segundo int será gravado ao chamar writeByteArray (), pois o comprimento da matriz é sempre escrito antes da matriz no Parcel (consulte a Figura 1).

Situações em que o tamanho da matriz de dados é igual a 0 são bastante raras. Mas mesmo quando isso acontece, o programa continua funcionando, se você transmitir apenas um objeto serializado por vez (no nosso exemplo, o objeto Demo). Portanto, esses erros tendem a passar despercebidos.

Agora tentaremos colocar um objeto Demo com comprimento zero da matriz no Bundle:


Figura 2. O resultado da adição de um objeto Demo de comprimento zero ao Bundle

Serializamos o objeto:


Figura 3. O objeto Bundle após serialização

Agora vamos tentar desserializar:


Figura 4. O objeto Bundle após desserialização

O que nós ganhamos? Vamos dar uma olhada no fragmento Parcel:


Figura 5. Estrutura do pacote após desserialização de bundle

Nas Figuras 4 e 5, vemos que, em vez de dois int, um int foi lido no método createFromParcel durante a desserialização. Portanto, todos os valores subseqüentes do pacote foram lidos incorretamente. O valor 0x0 em 0x60 foi lido como o comprimento da próxima chave. O valor 0x1 em 0x64 foi lido como uma chave. O valor 0x31 em 0x68 foi lido como um tipo de valor. O pacote não possui valores com o tipo 0x31, portanto, readFromParcel () reporta minuciosamente uma exceção.

Como isso pode ser usado na vida real e se tornar uma vulnerabilidade? Vamos ver! O erro acima nas classes do sistema Parcelable permite a criação de bundles que podem diferir durante a primeira e desserialização repetida. Para demonstrar isso, modificaremos o exemplo anterior:

 Parcel data = Parcel.obtain(); data.writeInt(3); // 3 entries data.writeString("vuln_class"); data.writeInt(4); // value is Parcelable data.writeString("com.drweb.testbundlemismatch.Demo"); data.writeInt(0); // data.length data.writeInt(1); // key length -> key value data.writeInt(6); // key value -> value is long data.writeInt(0xD); // value is bytearray -> low(long) data.writeInt(-1); // bytearray length dummy -> high(long) int startPos = data.dataPosition(); data.writeString("hidden"); // bytearray data -> hidden key data.writeInt(0); // value is string data.writeString("Hi there"); // hidden value int endPos = data.dataPosition(); int triggerLen = endPos - startPos; data.setDataPosition(startPos - 4); data.writeInt(triggerLen); // overwrite dummy value with the real value data.setDataPosition(endPos); data.writeString("A padding"); data.writeInt(0); // value is string data.writeString("to match pair count"); int length = data.dataSize(); Parcel bndl = Parcel.obtain(); bndl.writeInt(length); bndl.writeInt(0x4C444E42); // bundle magic bndl.appendFrom(data, 0, length); bndl.setDataPosition(0); 

Esse código cria um pacote serializado que contém uma classe vulnerável. Agora vamos ver o que obtemos depois de executar este código:


Figura 6. Criando um pacote com uma classe vulnerável

Após a primeira desserialização, este Bundle conterá as seguintes chaves:


Figura 7. Após a desserialização de um Bundle com uma classe vulnerável

Agora vamos serializar o pacote novamente, desserializá-lo novamente e examinar a lista de chaves:


Figura 8. Resultado da resserialização e desserialização de um pacote com uma classe vulnerável

O que nós vemos? O Bundle agora contém a chave Oculta (com o valor da sequência "Olá!"), Que não existia antes. Vamos analisar o fragmento Parcel deste pacote para ver por que isso aconteceu:


Figura 9. Estrutura do pacote de um objeto Bundle com uma classe vulnerável após dois ciclos de serialização e desserialização

É aqui que podemos ver todo o ponto das vulnerabilidades do EvilParcel. Podemos criar especificamente um Bundle que conterá uma classe vulnerável. Alterar os limites desta classe permitirá a colocação de qualquer objeto neste pacote; por exemplo, um Intent, que só aparecerá no Bundle após a segunda desserialização. Isso ajuda a ocultar um Intent dos mecanismos de segurança do SO.

Explorando o EvilParcel


O Android.InfectionAds.1 explorou o CVE-2017-13315 para instalar e remover software de forma independente dos proprietários do dispositivo. Mas como

Em 2013, o erro 7699048 , também conhecido como Launch AnyWhere, foi descoberto. Ele permitiu que aplicativos de terceiros iniciassem atividades arbitrárias em nome de um usuário do sistema mais privilegiado. Veja o diagrama abaixo para o mecanismo de ação:


Figura 10. Operação do erro 7699048

Um aplicativo de exploração pode usar essa vulnerabilidade para implementar o serviço Autenticador de conta, projetado para adicionar novas contas ao sistema operacional. O erro 7699048 ajuda a explorar as atividades de inicialização para instalar, remover, substituir aplicativos, além de redefinir o PIN ou o bloqueio de padrão e causar muito mais problemas.

Google Inc. eliminou essa violação ao proibir o lançamento de atividades arbitrárias do AccountManager. Agora, o AccountManager permite apenas o lançamento de atividades originárias do mesmo aplicativo. Para esse fim, ele verifica e combina a assinatura digital do software que iniciou a atividade com a assinatura do aplicativo em que a atividade está localizada. É assim:

 if (result != null && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) { /* * The Authenticator API allows third party authenticators to * supply arbitrary intents to other apps that they can run, * this can be very bad when those apps are in the system like * the System Settings. */ int authenticatorUid = Binder.getCallingUid(); long bid = Binder.clearCallingIdentity(); try { PackageManager pm = mContext.getPackageManager(); ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId); int targetUid = resolveInfo.activityInfo.applicationInfo.uid; if (PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authenticatorUid, targetUid)) { throw new SecurityException( "Activity to be started with KEY_INTENT must " + "share Authenticator's signatures"); } } finally { Binder.restoreCallingIdentity(bid); } } 

Parece que o problema foi resolvido, mas não é tão fácil quanto tudo isso. Verificou-se que a vulnerabilidade conhecida, EvilParcel CVE-2017-13315, fornece uma solução alternativa! Como já sabemos, após corrigir o Launch AnyWhere, o sistema verifica a assinatura digital do aplicativo. Se for verificado com êxito, o Bundle será transferido para IAccountManagerResponse.onResult (). Ao mesmo tempo, onResult () é chamado pelo mecanismo IPC, para que o Bundle seja serializado novamente. Ao implementar onResult (), acontece o seguinte:

 /** Handles the responses from the AccountManager */ private class Response extends IAccountManagerResponse.Stub { public void onResult(Bundle bundle) { Intent intent = bundle.getParcelable(KEY_INTENT); if (intent != null && mActivity != null) { // since the user provided an Activity we will silently start intents // that we see mActivity.startActivity(intent); // leave the Future running to wait for the real response to this request } //<.....> } //<.....> } 

Em seguida, o Bundle extrai a chave de intenção e a atividade é iniciada sem nenhuma verificação.
Portanto, para iniciar uma atividade arbitrária com privilégios do sistema, você só precisa criar um Bundle com o campo Intent oculto na primeira desserialização e aparecendo durante a desserialização repetida.
Como já sabemos, as vulnerabilidades do EvilParcel podem realmente executar esta tarefa.

No momento, todas as vulnerabilidades conhecidas desse tipo foram corrigidas nas classes Parcelable vulneráveis. No entanto, novas classes vulneráveis ​​podem aparecer no futuro. A implementação de pacotes e o mecanismo para adicionar novas contas ainda são os mesmos de antes. Eles ainda nos permitem criar essa exploração exata ao detectar classes Parcelable vulneráveis, antigas ou novas. Além disso, essas classes ainda são implementadas manualmente, e o programador deve garantir que o comprimento do objeto Parcelable serializado permaneça o mesmo, o que é um fator humano com tudo o que implica. No entanto, esperamos que haja o menor número possível de erros e as vulnerabilidades do EvilParcel não sejam uma ameaça para os usuários do Android.

Você pode verificar se há vulnerabilidades no EvilParcel em seu dispositivo móvel usando o Dr.Web Security Space para Android. O Auditor de Segurança interno relatará os problemas detectados e recomendará maneiras de eliminá-los.

Source: https://habr.com/ru/post/pt457610/


All Articles