跳过内容

MASTG-TEST-0028: 测试深度链接

此测试即将更新

此测试目前可使用,但将作为新的 OWASP MASTG v2 指南 的一部分进行全面修订。

请提交 PR 来帮助我们:MASTG v1->v2 MASTG-TEST-0028:测试深度链接 (android)

发送反馈

概述

任何现有的深度链接(包括应用链接)都可能增加应用攻击面。这包括许多风险,例如链接劫持、敏感功能暴露等。

  • 在 Android 12(API 级别 31)之前,如果应用有任何不可验证的链接,可能会导致系统无法验证该应用的所有 Android 应用链接。
  • 从 Android 12(API 级别 31)开始,应用受益于减少的攻击面。除非目标应用已获准使用 Web Intent 中包含的特定域,否则通用 Web Intent 将解析为用户的默认浏览器应用。

必须枚举所有深度链接并验证其网站关联是否正确。必须充分测试它们执行的操作,尤其是所有输入数据,这些数据应被视为不可信,因此应始终进行验证。

这些来源的任何输入都不能被信任;必须对其进行验证和/或清理。验证确保仅处理应用期望的数据。如果未强制执行验证,则可以将任何输入发送到应用,这可能允许攻击者或恶意应用利用应用功能。

静态分析

检查 Android 操作系统版本

应用运行的 Android 版本也会影响使用深度链接的风险。检查 Android Manifest 以查看 minSdkVersion 是否为 31 或更高。

  • 在 Android 12(API 级别 31)之前,如果应用有任何不可验证的深度链接,可能会导致系统无法验证该应用的所有 Android 应用链接。
  • 从 Android 12(API 级别 31)开始,应用受益于减少的攻击面。除非目标应用已获准使用 Web Intent 中包含的特定域,否则通用 Web Intent 将解析为用户的默认浏览器应用。

检查 Android Manifest

您可以轻松确定是否定义了深度链接(带有或不带有自定义 URL 方案),方法是 探索应用包并检查 Android Manifest 文件,查找<intent-filter> 元素

  • 自定义 URL 方案:以下示例指定了一个具有名为 myapp:// 的自定义 URL 方案的深度链接。
<activity android:name=".MyUriActivity">
  <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="myapp" android:host="path" />
  </intent-filter>
</activity>
  • 深度链接:以下示例指定了一个同时使用 http://https:// 方案的深度链接,以及将激活它的主机和路径(在本例中,完整 URL 将为 https://www.myapp.com/my/app/path
<intent-filter>
  ...
  <data android:scheme="http" android:host="www.myapp.com" android:path="/my/app/path" />
  <data android:scheme="https" android:host="www.myapp.com" android:path="/my/app/path" />
</intent-filter>
  • 应用链接:如果 <intent-filter> 包含标志 android:autoVerify="true",这将导致 Android 系统连接到声明的 android:host,尝试访问数字资产链接文件,以便验证应用链接只有在验证成功后,深度链接才能被视为应用链接。
<intent-filter android:autoVerify="true">

在列出深度链接时,请记住同一 <intent-filter> 中的 <data> 元素实际上会合并在一起,以考虑其组合属性的所有变体。

<intent-filter>
  ...
  <data android:scheme="https" android:host="www.example.com" />
  <data android:scheme="app" android:host="open.my.app" />
</intent-filter>

这似乎只支持 https://www.example.comapp://open.my.app。但是,它实际上支持

  • https://www.example.com
  • app://open.my.app
  • app://www.example.com
  • https://open.my.app

使用 Dumpsys

使用 adb 运行以下命令,该命令将显示所有方案

adb shell dumpsys package com.example.package

使用 Android“应用链接验证”测试器

使用Android“应用链接验证”测试器脚本列出所有深度链接(list-all)或仅列出应用链接(list-applinks

python3 deeplink_analyser.py -op list-all -apk ~/Downloads/example.apk

.MainActivity

app://open.my.app
app://www.example.com
https://open.my.app
https://www.example.com

检查正确的网站关联

即使深度链接包含 android:autoVerify="true" 属性,也必须实际验证它们才能被视为应用链接。您应该测试任何可能阻止完全验证的错误配置。

自动验证

使用Android“应用链接验证”测试器脚本获取所有应用链接的验证状态 (verify-applinks)。请参阅此处的示例。

仅在 Android 12(API 级别 31)或更高版本上

您可以使用 adb 测试验证逻辑,无论应用是否面向 Android 12(API 级别 31)。此功能允许您

您还可以查看验证结果。例如

adb shell pm get-app-links com.example.package

com.example.package:
    ID: 01234567-89ab-cdef-0123-456789abcdef
    Signatures: [***]
    Domain verification state:
      example.com: verified
      sub.example.com: legacy_failure
      example.net: verified
      example.org: 1026

可以通过运行 adb shell dumpsys package com.example.package 找到相同的信息(仅在 Android 12(API 级别 31)或更高版本上)。

手动验证

本节详细介绍了验证过程失败或未实际触发的几个(可能有很多)原因。有关更多信息,请参阅Android 开发者文档和白皮书“衡量 Android 移动深度链接的不安全性”

检查数字资产链接文件

  • 检查缺少数字资产链接文件
    • 尝试在域的 /.well-known/ 路径中找到它。示例:https://www.example.com/.well-known/assetlinks.json
    • 或尝试 https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=www.example.com
  • 检查通过 HTTP 提供的有效的数字资产链接文件。
  • 检查通过 HTTPS 提供的无效数字资产链接文件。例如
    • 该文件包含无效的 JSON。
    • 该文件不包括目标应用的包。

检查重定向

为了增强应用安全性,如果服务器设置重定向(例如 http://example.comhttps://example.comexample.comwww.example.com),系统将不验证应用的任何 Android 应用链接

检查子域

如果 Intent 过滤器列出了具有不同子域的多个主机,则每个域上必须有一个有效的数字资产链接文件。例如,以下 Intent 过滤器包括 www.example.commobile.example.com 作为接受的 Intent URL 主机。

<application>
  <activity android:name=”MainActivity”>
    <intent-filter android:autoVerify="true">
      <action android:name="android.intent.action.VIEW" />
      <category android:name="android.intent.category.DEFAULT" />
      <category android:name="android.intent.category.BROWSABLE" />
      <data android:scheme="https" />
      <data android:scheme="https" />
      <data android:host="www.example.com" />
      <data android:host="mobile.example.com" />
    </intent-filter>
  </activity>
</application>

为了使深度链接正确注册,必须在 https://www.example.com/.well-known/assetlinks.jsonhttps://mobile.example.com/.well-known/assetlinks.json 上都发布有效的数字资产链接文件。

检查通配符

如果主机名包含通配符(例如 *.example.com),您应该能够在根主机名找到有效的数字资产链接文件:https://example.com/.well-known/assetlinks.json

检查处理方法

即使正确验证了深度链接,也应仔细分析处理方法的逻辑。特别注意用于传输数据(由用户或任何其他应用外部控制)的深度链接。

首先,从 Android Manifest <activity> 元素中获取 Activity 的名称,该元素定义了目标 <intent-filter>,并搜索getIntentgetData 的使用情况。当执行逆向工程时,这种查找这些方法的一般方法可以跨大多数应用使用,并且是尝试了解应用如何使用深度链接并处理任何外部提供的输入数据以及是否可能受到任何类型的滥用的关键。

以下示例是从使用 jadx 反编译的 Kotlin 应用示例片段。从静态分析中我们知道它支持深度链接 deeplinkdemo://load.html/ 作为 com.mstg.deeplinkdemo.WebViewActivity 的一部分。

// snippet edited for simplicity
public final class WebViewActivity extends AppCompatActivity {
    private ActivityWebViewBinding binding;

    public void onCreate(Bundle savedInstanceState) {
        Uri data = getIntent().getData();
        String html = data == null ? null : data.getQueryParameter("html");
        Uri data2 = getIntent().getData();
        String deeplink_url = data2 == null ? null : data2.getQueryParameter("url");
        View findViewById = findViewById(R.id.webView);
        if (findViewById != null) {
            WebView wv = (WebView) findViewById;
            wv.getSettings().setJavaScriptEnabled(true);
            if (deeplink_url != null) {
                wv.loadUrl(deeplink_url);
            ...

您可以简单地跟踪 deeplink_url String 变量,并查看 wv.loadUrl 调用的结果。这意味着攻击者可以完全控制加载到 WebView 的 URL(如上所示,具有 测试 WebViews 中的 JavaScript 执行)。

同一个 WebView 也可能呈现攻击者控制的参数。在这种情况下,以下深度链接有效负载将触发 WebView 上下文中的反射型跨站点脚本 (XSS)

deeplinkdemo://load.html?attacker_controlled=<svg onload=alert(1)>

但是还有许多其他可能性。请务必查看以下部分以了解更多关于预期情况以及如何测试不同场景的信息

此外,我们建议搜索和阅读公开报告(搜索词:"deep link*"|"deeplink*" site:https://hackerone.com/reports/)。例如

动态分析

在这里,您将使用静态分析中的深度链接列表来迭代并确定每个处理程序方法和处理的数据(如果有)。您将首先启动 Frida hook,然后开始调用深度链接。

以下示例假定目标应用接受此深度链接:deeplinkdemo://load.html。但是,我们还不知道相应的处理程序方法,也不知道它可能接受的参数。

[步骤 1] Frida Hooking

您可以使用 Frida CodeShare 中的脚本“Android Deep Link Observer”来监控所有调用的深度链接,这些链接触发对 Intent.getData 的调用。您还可以使用该脚本作为基础,根据手头的用例包括您自己的修改。在这种情况下,我们将堆栈跟踪包含在脚本中,因为我们对调用 Intent.getData 的方法感兴趣。

[步骤 2] 调用深度链接

现在您可以使用 adbActivity Manager (am) 调用任何深度链接,后者将在 Android 设备中发送 Intent。例如

adb shell am start -W -a android.intent.action.VIEW -d "deeplinkdemo://load.html/?message=ok#part1"

Starting: Intent { act=android.intent.action.VIEW dat=deeplinkdemo://load.html/?message=ok }
Status: ok
LaunchState: WARM
Activity: com.mstg.deeplinkdemo/.WebViewActivity
TotalTime: 210
WaitTime: 217
Complete

当使用“http/https”模式或如果其他已安装的应用支持相同的自定义 URL 模式时,这可能会触发歧义对话框。您可以包含包名称使其成为显式 Intent。

此调用将记录以下内容

[*] Intent.getData() was called
[*] Activity: com.mstg.deeplinkdemo.WebViewActivity
[*] Action: android.intent.action.VIEW

[*] Data
- Scheme: deeplinkdemo://
- Host: /load.html
- Params: message=ok
- Fragment: part1

[*] Stacktrace:

android.content.Intent.getData(Intent.java)
com.mstg.deeplinkdemo.WebViewActivity.onCreate(WebViewActivity.kt)
android.app.Activity.performCreate(Activity.java)
...
com.android.internal.os.ZygoteInit.main(ZygoteInit.java)

在这种情况下,我们精心制作了深度链接,包括任意参数 (?message=ok) 和片段 (#part1)。我们仍然不知道它们是否正在被使用。上面的信息揭示了有用的信息,您现在可以使用这些信息来逆向工程应用。请参阅“检查处理程序方法”部分,了解您应该考虑的事项。

  • 文件:WebViewActivity.kt
  • 类:com.mstg.deeplinkdemo.WebViewActivity
  • 方法:onCreate

有时您甚至可以利用您知道与您的目标应用交互的其他应用。您可以对应用进行逆向工程(例如,提取所有字符串并过滤掉那些包含目标深度链接的字符串,在前面的示例中为 deeplinkdemo:///load.html),或者将它们用作触发器,同时像之前讨论的那样挂钩应用。