Android Applications Basics
Last updated
Last updated
Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE) Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
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.
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.
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.
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.
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 protection-level 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.
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)
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.
É 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.
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.
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
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 são o meio principal 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.
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.
Intenções são criadas programaticamente usando um construtor de Intenção:
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:
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.
Um intent explícito especifica o nome da classe que está sendo direcionada:
Em outros aplicativos, para acessar a intenção previamente declarada, você pode usar:
Esses permitem que outros aplicativos realizem ações em nome do seu aplicativo, usando a identidade e permissões do seu app. Para 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.
Diferente 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.
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
:
O esquema do exemplo anterior é examplescheme://
(note também a categoria BROWSABLE
)
Então, no campo de dados, você pode especificar o host e path:
Para acessá-lo pela web, é possível definir um link como:
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.
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.
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.
Estes incluem: Atividades, Serviços, Receptores de Broadcast e Provedores.
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:
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:
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.
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.
Services 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 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:
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 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:
E um exemplo de especificação de pastas compartilhadas em filepaths.xml
:
Para mais informações, consulte:
WebViews são como mini navegadores da 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 do 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 o JavaScript com setJavaScriptEnabled(false)
pode prevenir ataques XSS.
O "Bridge" do JavaScript permite que objetos Java interajam com o 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.
A assinatura digital é obrigatória para aplicativos Android, garantindo que sejam autenticamente criados 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 CA externa, protegendo contra acesso não autorizado e garantindo que o aplicativo permaneça inalterado durante sua entrega ao dispositivo.
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. 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.
Soluções de MDM fornecem supervisão e segurança para dispositivos móveis através 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 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.
Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE) Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)