Android Applications Basics

Suporte ao HackTricks

Modelo de Segurança Android

Existem duas camadas:

  • O SO, que mantém as aplicações instaladas isoladas umas das outras.

  • A aplicação em si, que permite que os desenvolvedores exponham certas funcionalidades e configurem as capacidades da aplicação.

Separação de UID

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

Compartilhamento de UID

Duas aplicações podem ser configuradas para usar o mesmo UID. Isso pode ser útil para compartilhar informações, mas se uma delas for comprometida, os dados de ambas as aplicações serão comprometidos. É por isso que esse comportamento é desencorajado. Para compartilhar o mesmo UID, as aplicações devem definir o mesmo valor android:sharedUserId em seus manifests.

Sandbox

O Sandbox de Aplicações Android permite executar cada aplicação 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 app é executado em isolamento de outros apps. A partir do Android 5.0(L), o SELinux é aplicado. Basicamente, o SELinux negou todas as interações de processos e, em seguida, criou políticas para permitir apenas as interações esperadas entre eles.

Permissões

Quando você instala um app e ele pede permissões, o app está solicitando 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 name. Ele também possui o atributo maxSdkVersion que para de solicitar permissões em versões superiores à especificada. Note que as aplicações android não precisam pedir todas as permissões no início, elas também podem pedir permissões dinamicamente, mas todas as permissões devem ser declaradas no manifesto.

Quando um app expõe funcionalidade, ele pode limitar o acesso apenas a apps que tenham uma permissão específica. Um elemento de permissão tem 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 app. O usuário não é obrigado a aprová-lo.

  • Dangerous: Indica que a permissão concede à aplicação solicitante algum acesso elevado. Os usuários são solicitados a aprová-las.

  • Signature: Apenas apps assinados pelo mesmo certificado que o que exporta o componente podem receber permissão. Este é o tipo mais forte de proteção.

  • SignatureOrSystem: Apenas apps assinados pelo mesmo certificado que o que exporta o componente ou apps executando com acesso a nível de sistema podem receber permissões.

Aplicações Pré-Instaladas

Esses apps 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). Essas aplicações valem a pena serem verificadas porque às vezes estão executando com muitas permissões (como root).

  • As que vêm com o AOSP (Android OpenSource Project) ROM

  • Adicionadas pelo fabricante do dispositivo

  • Adicionadas pelo provedor de telefonia (se compradas deles)

Rooting

Para obter acesso root em um dispositivo android físico, você geralmente precisa explorar 1 ou 2 vulnerabilidades que costumam ser específicas para o dispositivo e versão. Uma vez que a exploração tenha funcionado, geralmente o binário Linux su é copiado para um local especificado na variável de ambiente PATH do usuário, como /system/xbin.

Uma vez que o binário su está configurado, outro app 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).

Note que o processo de rooting é muito perigoso e pode danificar severamente 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 ganhar acesso ao código Android mais recente. OmniROM e LineageOS são dois dos firmwares mais populares para usar.

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

Implicações

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

Fundamentos de Aplicações Android

  • O formato das aplicações 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údos 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 que o Certificado está localizado!

  • classes.dex

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

  • lib/

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

  • armeabi: código para processadores baseados em ARM

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

  • x86: código para processadores X86

  • mips: código apenas para processadores MIPS

  • assets/

  • Armazena arquivos diversos necessários pelo app, 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

No desenvolvimento Android, Java ou Kotlin é usado para criar apps. Em vez de usar a JVM como em apps de desktop, o Android compila esse código em bytecode Executável Dalvik (DEX). Anteriormente, a máquina virtual Dalvik lidava com esse bytecode, mas agora, o Android Runtime (ART) assume em versões mais novas do Android.

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

Intents

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

Assim, um Intent é basicamente uma mensagem que é passada entre componentes. Intents podem ser direcionados a componentes ou apps específicos, ou podem ser enviados sem um destinatário específico. Para ser simples, o Intent pode ser usado:

  • Para iniciar uma Activity, tipicamente abrindo uma interface de usuário para um app

  • Como transmissões para informar o sistema e apps sobre mudanças

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

  • Para acessar dados via ContentProviders

  • Como callbacks para lidar com eventos

Se vulneráveis, Intents podem ser usados para realizar uma variedade de ataques.

Filtro de Intent

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

Filtros de Intent são compostos por categorias, ações e filtros de dados, com a possibilidade de incluir metadados adicionais. Essa configuração permite que componentes lidem com Intents específicos que correspondem 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 apps se for exported com um valor de true ou se um Filtro de Intent for declarado para ele no manifesto. No entanto, há uma maneira para os desenvolvedores manterem esses componentes privados, garantindo que não interajam com outros apps involuntariamente. Isso é alcançado definindo o atributo exported como false em suas definições de manifesto.

Além disso, os desenvolvedores têm a opção de proteger ainda mais o acesso a esses componentes exigindo permissões específicas. O atributo permission pode ser definido para impor que apenas apps 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

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

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

A Ação da intenção declarada anteriormente é ACTION_SEND e o Extra é um mailto Uri (o Extra é a informação adicional que a intenção está esperando).

Esta intenção deve ser declarada dentro do manifesto como no seguinte exemplo:

<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 à ação, dados e categoria para receber uma mensagem.

O processo de "resolução de Intent" determina qual aplicativo deve receber cada mensagem. Este processo considera o atributo de prioridade, que pode ser definido na declaração do intent-filter, e aquele 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 um conflito surgir, uma janela "chooser" aparece para que o usuário possa decidir.

Intents Explícitos

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

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

Em outros aplicativos, para acessar a intenção previamente declarada, você pode usar:

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

Pending Intents

Esses permitem que outros aplicativos realizem ações em nome do seu aplicativo, usando a identidade e permissões do seu app. Ao construir um Pending Intent, deve-se especificar um intent e a ação a ser realizada. Se o intent declarado não for Explícito (não declarar qual intent pode chamá-lo), um aplicativo malicioso poderia realizar a ação declarada em nome do aplicativo vítima. Além disso, se uma ação não for especificada, o aplicativo malicioso poderá fazer qualquer ação em nome da vítima.

Broadcast Intents

Ao contrário dos intents anteriores, que são recebidos apenas por um app, os broadcast intents podem ser recebidos por múltiplos apps. No entanto, a partir da versão da API 14, é possível especificar o app que deve receber a mensagem usando Intent.setPackage.

Alternativamente, também é possível especificar uma permissão ao enviar o broadcast. O app receptor precisará ter essa permissão.

Existem dois tipos de Broadcasts: Normal (assíncrono) e Ordenado (síncrono). A ordem é baseada na prioridade configurada dentro do elemento receptor. Cada app pode processar, retransmitir ou descartar o Broadcast.

É possível enviar um broadcast usando a função sendBroadcast(intent, receiverPermission) da classe Context. Você também pode usar a função sendBroadcast do LocalBroadCastManager que garante que a mensagem nunca saia do app. Usando isso, você não precisará nem exportar um componente receptor.

Sticky Broadcasts

Esse tipo de Broadcasts pode ser acessado muito tempo depois de serem enviados. Esses foram descontinuados no nível da API 21 e é recomendado não usá-los. Eles permitem que qualquer aplicativo capture os dados, mas também os modifique.

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

Em aplicativos Android, deep links são usados para iniciar uma ação (Intent) diretamente através de uma URL. Isso é feito declarando um URL scheme específico dentro de uma atividade. Quando um dispositivo Android tenta acessar uma URL com esse esquema, a atividade especificada dentro do aplicativo é 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)

Então, no campo de dados, você pode especificar o host e path:

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

Para acessá-lo pela web, é 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 App, vá para a atividade chamada pelo deeplink e procure a função onNewIntent.

Aprenda a 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 de comunicação entre processos (IPC). Como o acesso à memória de outro processo diretamente não é permitido no Android, o AIDL simplifica o processo ao marshalling de 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 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 é crítico para iniciar a interação, marcando-o como uma área vital para revisão de segurança em busca de vulnerabilidades.

  • Messenger: Operando como um serviço vinculado, o Messenger facilita a IPC com foco no processamento de dados através do método onBind. É essencial inspecionar este método de perto para qualquer manipulação de dados insegura 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 uma compreensão mais aprofundada, um recurso está disponível em https://www.youtube.com/watch?v=O-UHvFjxwZ8.

Componentes

Estes incluem: Atividades, Serviços, Receptores de Broadcast e Provedores.

Atividade de Lançamento e outras atividades

Em aplicativos Android, 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 para o usuário.

A atividade de lançamento é o principal portal para um aplicativo, lançada quando você toca no ícone do aplicativo. Ela é definida no arquivo de manifesto do aplicativo com intents específicas MAIN e LAUNCHER:

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

Nem todos os aplicativos precisam de uma atividade de inicialização, 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 "exported" 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 pode 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 antecipada antes que o restante da aplicação comece.

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
}
}

Services

Services são operativos 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 mudam para diferentes aplicativos, tornando os serviços cruciais para operações de longa duração.

Os serviços são versáteis; podem ser iniciados de várias maneiras, sendo 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 de cliente ativa, o método bindService é usado para vincular o cliente ao serviço, ativando o método onBind para a passagem de dados.

Uma aplicação interessante de 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 através da exportação. Este não é o comportamento padrão e requer configuração explícita no arquivo Android Manifest:

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

Broadcast Receivers

Broadcast receivers atuam como ouvintes em um sistema de mensagens, permitindo que múltiplos aplicativos 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 a API registerReceiver. No Manifest, as transmissões são filtradas com permissões, enquanto os receptores registrados dinamicamente também podem especificar permissões no momento do registro.

Filtros de Intent 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 de acordo, como ajustando o comportamento em resposta a um alerta de bateria baixa.

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 notar o potencial risco de segurança, já que qualquer aplicativo pode priorizar a si mesmo para interceptar uma transmissão.

Para entender a funcionalidade de um receptor, procure o método onReceive dentro de sua classe. O código desse método pode manipular o Intent recebido, destacando a necessidade de validação de dados pelos receptores, especialmente em Ordered Broadcasts, que podem modificar ou descartar o Intent.

Content Provider

Content Providers 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 através das configurações grantUriPermission no manifest do aplicativo, aproveitando atributos como path, pathPrefix e pathPattern para controle de acesso detalhado.

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

FileProvider, um Content Provider especializado, foca em compartilhar arquivos de forma segura. Ele é definido no manifest do aplicativo com atributos específicos para controlar o acesso a pastas, denotados por android:exported e android:resource apontando para configurações de pastas. Cuidado é aconselhado ao compartilhar diretórios para evitar expor dados sensíveis inadvertidamente.

Exemplo de declaração de manifest 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 em filepaths.xml:

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

Para mais informações, consulte:

WebViews

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

O Android oferece dois tipos principais 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 essas URLs ou arquivos sejam seguros para uso. As configurações de segurança podem ser gerenciadas através da classe WebSettings. Por exemplo, desabilitar JavaScript com setJavaScriptEnabled(false) pode prevenir ataques XSS.

O "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 Content Providers, o que pode ser um risco, a menos que as URLs de conteúdo sejam verificadas como seguras.

Para controlar o acesso a arquivos:

  • Desabilitar o acesso a arquivos (setAllowFileAccess(false)) limita o acesso ao sistema de arquivos, com exceções para certos 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 é obrigatória para aplicativos Android, garantindo que sejam autenticamente criados antes da instalação. Este 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 CA externa, protegendo contra acesso não autorizado e garantindo que o aplicativo permaneça inalterado durante sua entrega ao dispositivo.

Verificação de Aplicativos para Segurança Aprimorada

  • A partir do Android 4.2, um recurso chamado Verificar Aplicativos permite que os usuários verifiquem a segurança dos aplicativos antes da instalação. Este 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)

  • Soluções MDM fornecem supervisão e segurança para dispositivos móveis através da API de Administração de Dispositivos. Elas necessitam da instalação de um aplicativo Android para gerenciar e proteger dispositivos móveis de forma eficaz. As funções principais incluem imposição de políticas de senha, exigência de criptografia de armazenamento, e permissão para limpeza remota de dados, garantindo controle e segurança abrangentes 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);
}
Supporte o HackTricks

Last updated