Android Applications Basics

Aprenda a hackear a AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

Outras formas de apoiar o HackTricks:

Grupo de Segurança Try Hard


Modelo de Segurança Android

Existem duas camadas:

  • O SO, que mantém aplicativos instalados isolados uns dos outros.

  • O próprio aplicativo, que permite aos desenvolvedores expor certas funcionalidades e configurar as capacidades do aplicativo.

Separação de UID

Cada aplicativo é atribuído a um ID de Usuário específico. Isso é feito durante a instalação do aplicativo para que o aplicativo possa interagir apenas com arquivos de propriedade de seu ID de Usuário ou arquivos compartilhados. Portanto, apenas o próprio aplicativo, certos componentes do SO e o usuário root podem acessar os dados dos aplicativos.

Compartilhamento de UID

Dois aplicativos podem ser configurados para usar o mesmo UID. Isso pode ser útil para compartilhar informações, mas se um deles for comprometido, os dados de ambos os aplicativos serão comprometidos. Por isso, esse comportamento é desencorajado. Para compartilhar o mesmo UID, os aplicativos devem definir o mesmo valor android:sharedUserId em seus manifestos.

Isolamento

A Sandbox de Aplicativos Android permite executar cada aplicativo como um processo separado sob um ID de usuário separado. Cada processo tem sua própria máquina virtual, então o código de um aplicativo é executado isoladamente de outros aplicativos. A partir do Android 5.0(L), o SELinux é aplicado. Basicamente, o SELinux nega todas as interações de processos e então cria políticas para permitir apenas as interações esperadas entre eles.

Permissões

Quando você instala um aplicativo e ele solicita permissões, o aplicativo está pedindo as permissões configuradas nos elementos uses-permission no arquivo AndroidManifest.xml. O elemento uses-permission indica o nome da permissão solicitada dentro do atributo de nome. Ele também possui o atributo maxSdkVersion que impede a solicitação de permissões em versões superiores à especificada. Observe que os aplicativos Android não precisam solicitar todas as permissões no início, eles também podem solicitar permissões dinamicamente, mas todas as permissões devem ser declaradas no manifesto.

Quando um aplicativo expõe funcionalidades, ele pode limitar o acesso apenas a aplicativos que possuem uma permissão especificada. Um elemento de permissão possui três atributos:

  • O nome da permissão

  • O atributo permission-group, que permite agrupar permissões relacionadas.

  • O nível de proteção que indica como as permissões são concedidas. Existem quatro tipos:

  • Normal: Usado quando não há ameaças conhecidas ao aplicativo. O usuário não precisa aprovar.

  • Perigoso: Indica que a permissão concede ao aplicativo solicitante algum acesso elevado. Os usuários são solicitados a aprová-los.

  • Assinatura: Apenas aplicativos assinados pelo mesmo certificado que o exportando o componente podem receber permissão. Este é o tipo mais forte de proteção.

  • AssinaturaOuSistema: Apenas aplicativos assinados pelo mesmo certificado que o exportando o componente ou aplicativos em execução com acesso de nível de sistema podem receber permissões.

Aplicativos Pré-Instalados

Esses aplicativos geralmente são encontrados nos diretórios /system/app ou /system/priv-app e alguns deles são otimizados (você pode nem encontrar o arquivo classes.dex). Vale a pena verificar esses aplicativos porque às vezes eles estão rodando com muitas permissões (como root).

  • Os enviados com o ROM do AOSP (Projeto de Código Aberto Android)

  • Adicionados pelo fabricante do dispositivo

  • Adicionados pelo provedor de telefonia celular (se comprado deles)

Root

Para obter acesso root a um dispositivo Android físico, geralmente é necessário explorar 1 ou 2 vulnerabilidades que costumam ser específicas para o dispositivo e versão. Depois que o exploit funcionar, geralmente o binário su do Linux é copiado para uma localização especificada na variável de ambiente PATH do usuário, como /system/xbin.

Depois que o binário su estiver configurado, outro aplicativo Android é usado para interagir com o binário su e processar solicitações de acesso root como Superuser e SuperSU (disponível na Google Play Store).

Observe que o processo de root é muito perigoso e pode danificar gravemente o dispositivo

ROMs

É possível substituir o SO instalando um firmware personalizado. Fazendo isso, é possível estender a utilidade de um dispositivo antigo, contornar restrições de software ou obter acesso ao código Android mais recente. OmniROM e LineageOS são dois dos firmwares mais populares para usar.

Observe que nem sempre é necessário fazer root no dispositivo para instalar um firmware personalizado. Alguns fabricantes permitem o desbloqueio de seus carregadores de inicialização de maneira bem documentada e segura.

Implicações

Uma vez que um dispositivo é rooteado, qualquer aplicativo pode solicitar acesso como root. Se um aplicativo malicioso obtiver, ele terá acesso a quase tudo e poderá danificar o telefone.

Fundamentos de Aplicativos Android

  • O formato dos aplicativos Android é referido como formato de arquivo APK. É essencialmente um arquivo ZIP (renomeando a extensão do arquivo para .zip, o conteúdo pode ser extraído e visualizado).

  • Conteúdo do APK (Não exaustivo)

  • AndroidManifest.xml

  • resources.arsc/strings.xml

  • resources.arsc: contém recursos pré-compilados, como XML binário.

  • res/xml/files_paths.xml

  • META-INF/

  • Aqui é onde o Certificado está localizado!

  • classes.dex

  • Contém bytecode Dalvik, representando o código Java (ou Kotlin) compilado que o aplicativo executa por padrão.

  • lib/

  • Armazena bibliotecas nativas, segregadas por arquitetura de CPU em subdiretórios.

  • armeabi: código para processadores baseados em ARM

  • armeabi-v7a: código para processadores baseados em ARMv7 e superiores

  • x86: código para processadores X86

  • mips: código apenas para processadores MIPS

  • assets/

  • Armazena arquivos diversos necessários pelo aplicativo, potencialmente incluindo bibliotecas nativas adicionais ou arquivos DEX, às vezes usados por autores de malware para ocultar código adicional.

  • res/

  • Contém recursos que não são compilados em resources.arsc

Dalvik & Smali

Na desenvolvimento Android, Java ou Kotlin são usados para criar aplicativos. Em vez de usar o JVM como em aplicativos desktop, o Android compila esse código em Dalvik Executable (DEX) bytecode. Anteriormente, a máquina virtual Dalvik lidava com esse bytecode, mas agora, o Android Runtime (ART) assume em versões mais recentes do Android.

Para engenharia reversa, Smali se torna crucial. É a versão legível por humanos do bytecode DEX, atuando como linguagem de montagem ao traduzir o código-fonte em instruções de bytecode. Smali e baksmali se referem às ferramentas de montagem e desmontagem nesse contexto.

Intenções

Intenções são o principal meio pelo qual os aplicativos Android se comunicam entre seus componentes ou com outros aplicativos. Esses objetos de mensagem também podem transportar dados entre aplicativos ou componentes, semelhante à forma como as solicitações GET/POST são usadas em comunicações HTTP.

Portanto, uma Intenção é basicamente uma mensagem que é passada entre componentes. As Intenções podem ser direcionadas para componentes ou aplicativos específicos, ou podem ser enviadas sem um destinatário específico. Para ser simples, a Intenção pode ser usada:

  • Para iniciar uma Activity, normalmente abrindo uma interface de usuário para um aplicativo

  • Como transmissões para informar o sistema e aplicativos sobre alterações

  • Para iniciar, parar e comunicar-se com um serviço em segundo plano

  • Para acessar dados por meio de ContentProviders

  • Como callbacks para lidar com eventos

Se vulneráveis, Intenções podem ser usadas para realizar uma variedade de ataques.

Filtro de Intenção

Filtros de Intenção definem como uma atividade, serviço ou Receptor de Transmissão pode interagir com diferentes tipos de Intenções. Essencialmente, eles descrevem as capacidades desses componentes, como quais ações podem executar ou os tipos de transmissões que podem processar. O local principal para declarar esses filtros é dentro do arquivo AndroidManifest.xml, embora para Receptores de Transmissão, codificá-los também seja uma opção.

Os Filtros de Intenção são compostos por categorias, ações e filtros de dados, com a possibilidade de incluir metadados adicionais. Essa configuração permite que os componentes lidem com Intenções específicas que correspondam aos critérios declarados.

Um aspecto crítico dos componentes Android (atividades/serviços/provedores de conteúdo/receptores de transmissão) é sua visibilidade ou status público. Um componente é considerado público e pode interagir com outros aplicativos se for exportado com um valor de true ou se um Filtro de Intenção for declarado para ele no manifesto. No entanto, os desenvolvedores têm a opção de manter explicitamente esses componentes privados, garantindo que não interajam com outros aplicativos inadvertidamente. Isso é alcançado definindo o atributo exportado como false em suas definições de manifesto.

Além disso, os desenvolvedores têm a opção de garantir ainda mais o acesso a esses componentes exigindo permissões específicas. O atributo permissão pode ser definido para garantir que apenas aplicativos com a permissão designada possam acessar o componente, adicionando uma camada extra de segurança e controle sobre quem pode interagir com ele.

<activity android:name=".MyActivity" android:exported="false">
<!-- Intent filters go here -->
</activity>

Intenções Implícitas

As intenções são criadas programaticamente usando um construtor de Intenções:

Intent email = new Intent(Intent.ACTION_SEND, Uri.parse("mailto:"));

A Ação do intent previamente declarado é ACTION_SEND e o Extra é um Uri mailto (o Extra é a informação extra que o intent está esperando).

Este intent deve ser declarado dentro do manifesto como no exemplo a seguir:

<activity android:name="ShareActivity">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>

Um intent-filter precisa corresponder à action, data e category para receber uma mensagem.

O processo de "resolução de Intent" determina qual aplicativo deve receber cada mensagem. Esse processo considera o atributo de prioridade, que pode ser definido na declaração do intent-filter, e o com a prioridade mais alta será selecionado. Essa prioridade pode ser definida entre -1000 e 1000 e os aplicativos podem usar o valor SYSTEM_HIGH_PRIORITY. Se surgir um conflito, uma janela de "escolha" aparece para que o usuário possa decidir.

Intents Explícitos

Um intent explícito especifica o nome da classe que está mirando:

Intent downloadIntent = new (this, DownloadService.class):

Em outras aplicações, para acessar o intent previamente declarado, você pode usar:

Intent intent = new Intent();
intent.setClassName("com.other.app", "com.other.app.ServiceName");
context.startService(intent);

Intenções Pendentes

Estas permitem que outras aplicações realizem ações em nome da sua aplicação, utilizando a identidade e permissões da sua aplicação. Ao construir uma Intenção Pendente, deve ser especificada uma intenção e a ação a ser realizada. Se a intenção declarada não for Explícita (não declara qual intenção pode chamá-la), uma aplicação maliciosa poderá realizar a ação declarada em nome da aplicação da vítima. Além disso, se uma ação não for especificada, a aplicação maliciosa poderá realizar qualquer ação em nome da vítima.

Intenções de Transmissão

Ao contrário das intenções anteriores, que são recebidas apenas por uma aplicação, as intenções de transmissão podem ser recebidas por várias aplicações. No entanto, a partir da versão API 14, é possível especificar a aplicação que deve receber a mensagem usando Intent.setPackage.

Alternativamente, também é possível especificar uma permissão ao enviar a transmissão. A aplicação receptora precisará ter essa permissão.

Existem dois tipos de Transmissões: Normais (assíncronas) e Ordenadas (síncronas). A ordem é baseada na prioridade configurada dentro do receptor. Cada aplicação pode processar, retransmitir ou descartar a Transmissão.

É possível enviar uma transmissão usando a função sendBroadcast(intent, receiverPermission) da classe Context. Também é possível usar a função sendBroadcast do LocalBroadcastManager para garantir que a mensagem nunca saia da aplicação. Usando isso, nem será necessário exportar um componente receptor.

Transmissões Persistentes

Este tipo de Transmissões pode ser acessado muito tempo após serem enviadas. Estas foram descontinuadas no nível da API 21 e é recomendado não utilizá-las. Elas permitem que qualquer aplicação espie os dados, mas também os modifique.

Se encontrar funções contendo a palavra "persistentes" como sendStickyBroadcast ou sendStickyBroadcastAsUser, verifique o impacto e tente removê-las.

Nas aplicações Android, os links profundos são usados para iniciar uma ação (Intenção) diretamente através de um URL. Isso é feito declarando um esquema de URL específico dentro de uma atividade. Quando um dispositivo Android tenta acessar um URL com este esquema, a atividade especificada dentro da aplicação é iniciada.

O esquema deve ser declarado no arquivo AndroidManifest.xml:

[...]
<activity android:name=".MyActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="examplescheme" />
</intent-filter>
[...]

O esquema do exemplo anterior é exampleapp:// (note também a categoria BROWSABLE)

Em seguida, no campo de dados, você pode especificar o host e o caminho:

<data android:scheme="examplescheme"
android:host="example"
/>

Para acessá-lo a partir de um site, é possível definir um link como:

<a href="examplescheme://example/something">click here</a>
<a href="examplescheme://example/javascript://%250dalert(1)">click here</a>

Para encontrar o código que será executado no aplicativo, vá para a atividade chamada pelo deeplink e procure a função onNewIntent.

Saiba como chamar deep links sem usar páginas HTML.

AIDL - Linguagem de Definição de Interface Android

A Linguagem de Definição de Interface Android (AIDL) é projetada para facilitar a comunicação entre cliente e serviço em aplicativos Android por meio da comunicação entre processos (IPC). Como acessar diretamente a memória de outro processo não é permitido no Android, o AIDL simplifica o processo ao empacotar objetos em um formato compreendido pelo sistema operacional, facilitando assim a comunicação entre diferentes processos.

Conceitos Chave

  • Serviços Vinculados: Esses serviços utilizam o AIDL para IPC, permitindo que atividades ou componentes se vinculem a um serviço, façam solicitações e recebam respostas. O método onBind na classe do serviço é crucial para iniciar a interação, tornando-o uma área vital para revisão de segurança em busca de vulnerabilidades.

  • Messenger: Funcionando como um serviço vinculado, o Messenger facilita o IPC com foco no processamento de dados por meio do método onBind. É essencial inspecionar este método de perto em busca de qualquer manipulação insegura de dados ou execução de funções sensíveis.

  • Binder: Embora o uso direto da classe Binder seja menos comum devido à abstração do AIDL, é benéfico entender que o Binder atua como um driver de nível de kernel facilitando a transferência de dados entre os espaços de memória de diferentes processos. Para maior compreensão, um recurso está disponível em https://www.youtube.com/watch?v=O-UHvFjxwZ8.

Componentes

Estes incluem: Atividades, Serviços, Receptores de Transmissão e Provedores.

Atividade de Lançamento e outras atividades

Nos aplicativos Android, as atividades são como telas, mostrando diferentes partes da interface do usuário do aplicativo. Um aplicativo pode ter muitas atividades, cada uma apresentando uma tela única ao usuário.

A atividade de lançamento é a principal porta de entrada para um aplicativo, sendo lançada quando você toca no ícone do aplicativo. É definida no arquivo de manifesto do aplicativo com intenções MAIN e LAUNCHER específicas:

<activity android:name=".LauncherActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

Não todos os aplicativos precisam de uma atividade de lançamento, especialmente aqueles sem uma interface de usuário, como serviços em segundo plano.

As atividades podem ser disponibilizadas para outros aplicativos ou processos marcando-as como "exportadas" no manifesto. Essa configuração permite que outros aplicativos iniciem essa atividade:

<service android:name=".ExampleExportedService" android:exported="true"/>

No entanto, acessar uma atividade de outro aplicativo nem sempre é um risco de segurança. A preocupação surge se dados sensíveis estiverem sendo compartilhados de forma inadequada, o que poderia levar a vazamentos de informações.

O ciclo de vida de uma atividade começa com o método onCreate, configurando a interface do usuário e preparando a atividade para interação com o usuário.

Subclasse de Aplicativo

No desenvolvimento Android, um aplicativo tem a opção de criar uma subclasse da classe Application, embora não seja obrigatório. Quando tal subclasse é definida, ela se torna a primeira classe a ser instanciada dentro do aplicativo. O método attachBaseContext, se implementado nesta subclasse, é executado antes do método onCreate. Essa configuração permite uma inicialização precoce antes do restante do aplicativo começar.

public class MyApp extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
// Initialization code here
}

@Override
public void onCreate() {
super.onCreate();
// More initialization code
}
}

Serviços

Serviços são operadores em segundo plano capazes de executar tarefas sem uma interface de usuário. Essas tarefas podem continuar em execução mesmo quando os usuários alternam para diferentes aplicativos, tornando os serviços cruciais para operações de longa duração.

Os serviços são versáteis; eles podem ser iniciados de várias maneiras, sendo os Intents o método principal para lançá-los como ponto de entrada de um aplicativo. Uma vez que um serviço é iniciado usando o método startService, seu método onStart entra em ação e continua em execução até que o método stopService seja chamado explicitamente. Alternativamente, se o papel de um serviço depender de uma conexão ativa do cliente, o método bindService é usado para vincular o cliente ao serviço, envolvendo o método onBind para a passagem de dados.

Uma aplicação interessante dos serviços inclui a reprodução de música em segundo plano ou a busca de dados de rede sem prejudicar a interação do usuário com um aplicativo. Além disso, os serviços podem ser tornados acessíveis a outros processos no mesmo dispositivo por meio da exportação. Isso não é o comportamento padrão e requer configuração explícita no arquivo Android Manifest:

<service android:name=".ExampleExportedService" android:exported="true"/>

Receptores de Transmissão

Os receptores de transmissão atuam como ouvintes em um sistema de mensagens, permitindo que várias aplicações respondam às mesmas mensagens do sistema. Um aplicativo pode registrar um receptor de duas maneiras principais: através do Manifest do aplicativo ou dinamicamente dentro do código do aplicativo via API registerReceiver. No Manifest, as transmissões são filtradas com permissões, enquanto receptores registrados dinamicamente também podem especificar permissões durante o registro.

Filtros de intenção são cruciais em ambos os métodos de registro, determinando quais transmissões acionam o receptor. Uma vez que uma transmissão correspondente é enviada, o método onReceive do receptor é invocado, permitindo que o aplicativo reaja adequadamente, como ajustar o comportamento em resposta a um alerta de bateria fraca.

As transmissões podem ser assíncronas, alcançando todos os receptores sem ordem, ou síncronas, onde os receptores recebem a transmissão com base em prioridades definidas. No entanto, é importante observar o risco de segurança potencial, pois qualquer aplicativo pode se priorizar para interceptar uma transmissão.

Para entender a funcionalidade de um receptor, procure pelo método onReceive dentro de sua classe. O código deste método pode manipular a Intenção recebida, destacando a necessidade de validação de dados pelos receptores, especialmente em Transmissões Ordenadas, que podem modificar ou descartar a Intenção.

Provedor de Conteúdo

Os Provedores de Conteúdo são essenciais para compartilhar dados estruturados entre aplicativos, enfatizando a importância de implementar permissões para garantir a segurança dos dados. Eles permitem que aplicativos acessem dados de várias fontes, incluindo bancos de dados, sistemas de arquivos ou a web. Permissões específicas, como readPermission e writePermission, são cruciais para controlar o acesso. Além disso, o acesso temporário pode ser concedido por meio das configurações grantUriPermission no manifesto do aplicativo, aproveitando atributos como path, pathPrefix e pathPattern para um controle de acesso detalhado.

A validação de entrada é fundamental para evitar vulnerabilidades, como injeção de SQL. Os Provedores de Conteúdo suportam operações básicas: insert(), update(), delete() e query(), facilitando a manipulação e compartilhamento de dados entre aplicativos.

FileProvider, um Provedor de Conteúdo especializado, foca em compartilhar arquivos de forma segura. Ele é definido no manifesto do aplicativo com atributos específicos para controlar o acesso a pastas, indicadas por android:exported e android:resource apontando para configurações de pasta. É aconselhável ter cautela ao compartilhar diretórios para evitar expor inadvertidamente dados sensíveis.

Exemplo de declaração no manifesto para FileProvider:

<provider android:name="androidx.core.content.FileProvider"
android:authorities="com.example.myapp.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>

E um exemplo de especificação de pastas compartilhadas no filepaths.xml:

<paths>
<files-path path="images/" name="myimages" />
</paths>

Para mais informações, consulte:

WebViews

WebViews são como mini navegadores da web dentro de aplicativos Android, exibindo conteúdo tanto da web quanto de arquivos locais. Eles enfrentam riscos semelhantes aos dos navegadores regulares, mas existem maneiras de reduzir esses riscos por meio de configurações específicas.

O Android oferece dois principais tipos de WebView:

  • WebViewClient é ótimo para HTML básico, mas não suporta a função de alerta JavaScript, afetando como os ataques XSS podem ser testados.

  • WebChromeClient atua mais como a experiência completa do navegador Chrome.

Um ponto chave é que os navegadores WebView não compartilham cookies com o navegador principal do dispositivo.

Para carregar conteúdo, métodos como loadUrl, loadData, e loadDataWithBaseURL estão disponíveis. É crucial garantir que esses URLs ou arquivos sejam seguros para uso. As configurações de segurança podem ser gerenciadas por meio da classe WebSettings. Por exemplo, desativar JavaScript com setJavaScriptEnabled(false) pode prevenir ataques XSS.

A "Bridge" JavaScript permite que objetos Java interajam com JavaScript, exigindo que os métodos sejam marcados com @JavascriptInterface para segurança a partir do Android 4.2.

Permitir acesso ao conteúdo (setAllowContentAccess(true)) permite que WebViews acessem Provedores de Conteúdo, o que poderia ser um risco, a menos que os URLs de conteúdo sejam verificados como seguros.

Para controlar o acesso a arquivos:

  • Desativar o acesso a arquivos (setAllowFileAccess(false)) limita o acesso ao sistema de arquivos, com exceções para determinados ativos, garantindo que sejam usados apenas para conteúdo não sensível.

Outros Componentes de Aplicativos e Gerenciamento de Dispositivos Móveis

Assinatura Digital de Aplicativos

  • A assinatura digital é essencial para aplicativos Android, garantindo que sejam autenticamente autorizados antes da instalação. Esse processo utiliza um certificado para identificação do aplicativo e deve ser verificado pelo gerenciador de pacotes do dispositivo durante a instalação. Os aplicativos podem ser autoassinados ou certificados por uma AC externa, protegendo contra acessos não autorizados e garantindo que o aplicativo permaneça íntegro durante a entrega ao dispositivo.

Verificação de Aplicativos para Segurança Aprimorada

  • A partir do Android 4.2, um recurso chamado Verificar Apps permite que os usuários verifiquem a segurança dos aplicativos antes da instalação. Esse processo de verificação pode alertar os usuários sobre aplicativos potencialmente prejudiciais, ou até mesmo impedir a instalação de aplicativos particularmente maliciosos, aprimorando a segurança do usuário.

Gerenciamento de Dispositivos Móveis (MDM)

  • As soluções de MDM fornecem supervisão e segurança para dispositivos móveis por meio da API de Administração de Dispositivos. Elas exigem a instalação de um aplicativo Android para gerenciar e proteger dispositivos móveis de forma eficaz. As funções principais incluem impor políticas de senha, exigir criptografia de armazenamento e permitir a exclusão remota de dados, garantindo controle abrangente e segurança sobre dispositivos móveis.

// Example of enforcing a password policy with MDM
DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
ComponentName adminComponent = new ComponentName(context, AdminReceiver.class);

if (dpm.isAdminActive(adminComponent)) {
// Set minimum password length
dpm.setPasswordMinimumLength(adminComponent, 8);
}

Grupo de Segurança Try Hard

Aprenda hacking AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

Outras formas de apoiar o HackTricks:

Last updated