Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions src/mobile-pentesting/android-app-pentesting/intent-injection.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,71 @@ Run on-device: the inspector is Python-based and works in Termux or rooted phone

---

## Intent:// → content:// pivot via legacy URI grant (Android ≤ 13)

A powerful real-world chain on Android ≤ 13 combines:
- A browser (or any app) that accepts intent: URLs, parses them with Intent.parseUri(...), and then calls startActivity(intent) after a user prompt
- A victim app exposing a FileProvider (android:exported="false" is fine) with android:grantUriPermissions="true" for a narrowly scoped path (e.g., <cache-path name="sync" path="sync" />)
- Legacy framework behavior that auto-adds FLAG_GRANT_READ_URI_PERMISSION when the Intent carries certain text extras (e.g., EXTRA_TEXT or EXTRA_HTML_TEXT)

This enables a single-tap exfiltration of files addressed by content:// URIs owned by the victim app, even though the victim’s provider is non-exported and only grants via URI permissions.

Key idea (Android ≤ 13)
- If the sender Intent includes text/htmlText extras, the framework may implicitly add FLAG_GRANT_READ_URI_PERMISSION on startActivity(), causing the target to receive a temporary read grant for the Intent data URI.
- If the Intent’s data is a content://<victim.authority>/... URI served by a FileProvider with grantUriPermissions enabled, the receiving component can open it via ContentResolver.

When this bites
- Browsers that implement shouldOverrideUrlLoading → Intent.parseUri(...) → startActivity(intent) for intent: scheme URLs (after a one-tap “Open external app” dialog).
- Victim app stores sensitive artifacts in a FileProvider-mapped folder (e.g., cacheDir/sync) and expects to only share them via explicit grants.

Hunting checklist
- In browser/host APKs: search for Intent.parseUri(...), shouldOverrideUrlLoading, and NonHttp/“app link” code paths that end in context.startActivity(intent).
- In victim APKs: look for FileProvider declarations with grantUriPermissions=true and narrow paths in @xml/filepaths or provider_paths (paths, cache-path, files-path...). Identify exact content:// URIs that map to sensitive files.

PoC (launch via web, 1-tap social engineering)
- HTML payload auto-redirects the victim to an intent: URL whose data is the target content://, and sets a component= to an attacker-exported Activity. Include a TEXT extra to trigger the legacy auto-grant on Android ≤ 13:

```html
<meta http-equiv="refresh" content="1;url=
intent://com.duckduckgo.mobile.android.provider/sync/Sync%20Data%20Recovery%20-%20DuckDuckGo.pdf#Intent;
scheme=content;action=android.intent.action.SEND;
component=com.attacker.duckduckgopoc/.StealFileActivity;
S.android.intent.extra.TEXT=sync;end" />
```

Receiver (attacker side)
```java
// StealFileActivity.java (excerpt)
Uri fileToSteal = getIntent().getData();
try (InputStream in = getContentResolver().openInputStream(fileToSteal);
OutputStream out = new FileOutputStream(new File(getCacheDir(), "stolenfile"))) {
byte[] buf = new byte[8192];
int n; while ((n = in.read(buf)) != -1) out.write(buf, 0, n);
}
```

Preconditions
- Device on Android 13 or earlier
- Browser processes intent: URLs and calls startActivity() (one-tap confirmation is OK)
- Victim app exposes a FileProvider with grantUriPermissions and the sensitive file lives under a mapped path

Notes and gotchas
- Intent-scheme URLs cannot embed Serializable extras; don’t rely on invoking non-exported Activities that require Serializable input. Prefer targeting your own exported Activity to read the granted content URI.
- Even with android:exported="false" on the FileProvider and narrow paths, unintended grants can still happen if a cooperating component (the browser) originates the Intent.

Defensive guidance (app/browser)
- If you must handle intent: URLs, reject or strictly validate ones carrying content:// data URIs. Deny-list your own authorities (e.g., content://<your.pkg>.provider/...).
- Strip dangerous extras; clear URI grant flags unless explicitly required. Avoid startActivity() on untrusted Intent.parseUri(...) without a strict allowlist.
- Do not store high-value artifacts in provider-mapped cache paths; encrypt-at-rest or place them outside provider mappings.

Platform guidance
- Prefer updated Android versions where this legacy auto-grant behavior no longer applies. On legacy devices, be explicit and minimal about URI grants.

---

## Intent Redirection (CWE-926) – finding and exploiting


Pattern
- An exported entry point (Activity/Service/Receiver) reads an incoming Intent and forwards it internally or externally without validating source/data, e.g.:
- `startActivity(getIntent())`
Expand Down Expand Up @@ -203,6 +266,9 @@ Mitigations (developer checklist)

## References

- [Don't Leave Me Outdated! – Intent scheme to FileProvider pivot (Android ≤13)](https://tuxplorer.com/posts/dont-leave-me-outdated/)
- [AOSP Intent.java (Android 13) – legacy auto-grant behavior reference](https://cs.android.com/android/platform/superproject/+/android13-release:frameworks/base/core/java/android/content/Intent.java)

- [Android – Access to app-protected components](https://blog.oversecured.com/Android-Access-to-app-protected-components/)
- [Samsung S24 Exploit Chain Pwn2Own 2024 Walkthrough](https://medium.com/@happyjester80/samsung-s24-exploit-chain-pwn2own-2024-walkthrough-c7a3da9a7a26)
- [Pwn2Own Ireland 2024 – Samsung S24 attack chain (whitepaper)](https://maliciouserection.com/2025/05/13/pwn2own-ireland-2024-samsung-s24-attack-chain-whitepaper.html)
Expand Down