# iOS 渗透测试
使用 [**Trickest**](https://trickest.com/?utm\_campaign=hacktrics\&utm\_medium=banner\&utm\_source=hacktricks) 轻松构建并**自动化工作流程**,由世界**最先进**的社区工具提供支持。 立即获取访问权限: {% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}
从零开始学习 AWS 渗透测试,成为 htARTE (HackTricks AWS 红队专家) 其他支持 HackTricks 的方式: * 如果您希望在 HackTricks 中看到您的**公司广告**或**下载 HackTricks 的 PDF**,请查看[**订阅计划**](https://github.com/sponsors/carlospolop)! * 获取[**官方 PEASS & HackTricks 商品**](https://peass.creator-spring.com) * 发现[**PEASS 家族**](https://opensea.io/collection/the-peass-family),我们独家的 [**NFTs**](https://opensea.io/collection/the-peass-family) 收藏 * **加入** 💬 [**Discord 群组**](https://discord.gg/hRep4RUj7f) 或 [**telegram 群组**](https://t.me/peass) 或在 **Twitter** 🐦 上**关注**我 [**@carlospolopm**](https://twitter.com/carlospolopm)**。** * **通过向** [**HackTricks**](https://github.com/carlospolop/hacktricks) 和 [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github 仓库提交 PR 来**分享您的渗透测试技巧**。
## iOS 基础 {% content-ref url="ios-basics.md" %} [ios-basics.md](ios-basics.md) {% endcontent-ref %} ## 测试环境 在此页面中,您可以找到有关**iOS 模拟器**、**仿真器**和**越狱**的信息: {% content-ref url="ios-testing-environment.md" %} [ios-testing-environment.md](ios-testing-environment.md) {% endcontent-ref %} ## 初始分析 ### 基本的 iOS 测试操作 在测试过程中**将会建议执行多种操作**(连接设备、读取/写入/上传/下载文件、使用一些工具等)。因此,如果您不知道如何执行这些操作,请**开始阅读页面**: {% content-ref url="basic-ios-testing-operations.md" %} [basic-ios-testing-operations.md](basic-ios-testing-operations.md) {% endcontent-ref %} {% hint style="info" %} 对于以下步骤,**应该已经在设备上安装了应用程序**,并且已经获得了应用程序的**IPA 文件**。 阅读[基本的 iOS 测试操作](basic-ios-testing-operations.md)页面以了解如何做到这一点。 {% endhint %} ### 基本静态分析 建议使用工具 [**MobSF**](https://github.com/MobSF/Mobile-Security-Framework-MobSF) 对 IPA 文件执行自动静态分析。 识别二进制文件中存在的**保护措施**: * **PIE (位置独立可执行文件)**:启用时,应用程序每次启动时都会加载到一个随机内存地址,使其初始内存地址更难预测。 ```bash otool -hv | grep PIE # 应该包含 PIE 标志 ``` * **栈金丝雀**:为了验证栈的完整性,在调用函数之前会在栈上放置一个‘金丝雀’值,并在函数结束后再次验证。 ```bash otool -I -v | grep stack_chk # 应该包含符号:stack_chk_guard 和 stack_chk_fail ``` * **ARC (自动引用计数)**:为了防止常见的内存损坏缺陷 ```bash otool -I -v | grep objc_release # 应该包含 _objc_release 符号 ``` * **加密的二进制文件**:二进制文件应该是加密的 ```bash otool -arch all -Vl | grep -A5 LC_ENCRYPT # cryptid 应该是 1 ``` **识别敏感/不安全函数** * **弱哈希算法** ```bash # 在 iOS 设备上 otool -Iv | grep -w "_CC_MD5" otool -Iv | grep -w "_CC_SHA1" # 在 linux 上 grep -iER "_CC_MD5" grep -iER "_CC_SHA1" ``` * **不安全的随机函数** ```bash # 在 iOS 设备上 otool -Iv | grep -w "_random" otool -Iv | grep -w "_srand" otool -Iv | grep -w "_rand" # 在 linux 上 grep -iER "_random" grep -iER "_srand" grep -iER "_rand" ``` * **不安全的‘Malloc’函数** ```bash # 在 iOS 设备上 otool -Iv | grep -w "_malloc" # 在 linux 上 grep -iER "_malloc" ``` * **不安全且易受攻击的函数** ```bash # 在 iOS 设备上 otool -Iv | grep -w "_gets" otool -Iv | grep -w "_memcpy" otool -Iv | grep -w "_strncpy" otool -Iv | grep -w "_strlen" otool -Iv | grep -w "_vsnprintf" otool -Iv | grep -w "_sscanf" otool -Iv | grep -w "_strtok" otool -Iv | grep -w "_alloca" otool -Iv | grep -w "_sprintf" otool -Iv | grep -w "_printf" otool -Iv | grep -w "_vsprintf" # 在 linux 上 grep -R "_gets" grep -iER "_memcpy" grep -iER "_strncpy" grep -iER "_strlen" grep -iER "_vsnprintf" grep -iER "_sscanf" grep -iER "_strtok" grep -iER "_alloca" grep -iER "_sprintf" grep -iER "_printf" grep -iER "_vsprintf" ``` ### 基本动态分析 查看 [**MobSF**](https://github.com/MobSF/Mobile-Security-Framework-MobSF) 执行的动态分析。您需要浏览不同的视图并与它们互动,但它将会挂钩几个类并做其他事情,并在您完成后准备报告。 ### 列出已安装的应用程序 当针对已安装在设备上的应用程序时,您首先需要确定您想要分析的应用程序的正确捆绑标识符。您可以使用 `frida-ps -Uai` 获取所有应用程序(`-a`)当前安装(`-i`)在连接的 USB 设备(`-U`)上: ```bash $ frida-ps -Uai PID Name Identifier ---- ------------------- ----------------------------------------- 6847 Calendar com.apple.mobilecal 6815 Mail com.apple.mobilemail - App Store com.apple.AppStore - Apple Store com.apple.store.Jolly - Calculator com.apple.calculator - Camera com.apple.camera - iGoat-Swift OWASP.iGoat-Swift ``` ### 基本枚举与挂钩 学习如何**枚举应用程序的组件**以及如何使用objection轻松**挂钩方法和类**: {% content-ref url="ios-hooking-with-objection.md" %} [ios-hooking-with-objection.md](ios-hooking-with-objection.md) {% endcontent-ref %} ### IPA 结构 `.ipa` 文件是**压缩的** **包**,因此您可以将扩展名更改为 `.zip` 并**解压缩**它们。一个**完整的** **打包**应用程序准备安装通常被称为**Bundle**。\ 解压缩后,您应该会看到 `.app`,一个包含其余资源的压缩档案。 * `Info.plist`:包含一些应用程序特定配置的文件。 * `_CodeSignature/` 包含一个 plist 文件,对包中的所有文件进行签名。 * `Assets.car`:另一个包含资产(图标)的压缩档案。 * `Frameworks/` 包含应用程序的本机库,如 .dylib 或 .framework 文件。 * `PlugIns/` 可能包含应用程序扩展名为 .appex 的文件(示例中未出现)。 * [`Core Data`](https://developer.apple.com/documentation/coredata):用于保存应用程序的永久数据以供离线使用,缓存临时数据,并为您的应用程序添加撤销功能在单个设备上。为了在单个 iCloud 帐户中的多个设备上同步数据,Core Data 会自动将您的架构镜像到 CloudKit 容器中。 * [`PkgInfo`](https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPRuntimeConfig/Articles/ConfigApplications.html):`PkgInfo` 文件是指定应用程序或包的类型和创建者代码的另一种方式。 * **en.lproj, fr.proj, Base.lproj**:是包含特定语言资源的语言包,以及在不支持语言的情况下的默认资源。 iOS 应用程序定义 UI 有多种方式:_storyboard_、_nib_ 或 _xib_ 文件。 **Info.plist** 信息属性列表或 `Info.plist` 是 iOS 应用程序的主要信息来源。它由一个结构化文件组成,包含描述应用程序基本配置信息的**键值**对。实际上,所有捆绑的可执行文件(应用程序扩展、框架和应用程序)都**预期有**一个 `Info.plist` 文件。您可以在 [**Apple Developer Documentation**](https://developer.apple.com/documentation/bundleresources/information_property_list?language=objc) 中找到所有可能的键。 该文件可能以 **XML 或二进制 (bplist)** 格式格式化。您可以使用一个简单的命令将其**转换为 XML** 格式: * 在 macOS 上使用 `plutil`,这是一个与 macOS 10.2 及以上版本自带的工具(目前没有官方在线文档): ```bash $ plutil -convert xml1 Info.plist ``` * 在 Linux 上: ```bash $ apt install libplist-utils $ plistutil -i Info.plist -o Info_xml.plist ``` 以下是一些信息和相应关键词的非详尽列表,您可以通过检查文件或使用 `grep -i Info.plist` 在 `Info.plist` 文件中轻松搜索: * 应用程序权限目的字符串:`UsageDescription` * 自定义 URL 方案:`CFBundleURLTypes` * 导出/导入 _自定义文档类型_:`UTExportedTypeDeclarations` / `UTImportedTypeDeclarations` * 应用程序传输安全性 (ATS) 配置:`NSAppTransportSecurity` 请参考提到的章节以了解更多关于如何测试这些点的信息。 **数据路径** 在 iOS 上,**系统应用程序可以在 `/Applications`** 目录中找到,而**用户安装的** 应用程序可在 **`/private/var/containers/`** 下找到。然而,仅通过导航文件系统来找到正确的文件夹并非易事,因为**每个应用程序都会被分配一个随机的 128 位 UUID**(通用唯一标识符)作为其目录名称。 为了轻松获取用户安装的应用程序的安装目录信息,您可以使用 **objection 的命令 `env`**,它还会向您显示应用程序的所有目录信息: ```bash OWASP.iGoat-Swift on (iPhone: 11.1.2) [usb] # env Name Path ----------------- ------------------------------------------------------------------------------------------- BundlePath /var/containers/Bundle/Application/3ADAF47D-A734-49FA-B274-FBCA66589E67/iGoat-Swift.app CachesDirectory /var/mobile/Containers/Data/Application/8C8E7EB0-BC9B-435B-8EF8-8F5560EB0693/Library/Caches DocumentDirectory /var/mobile/Containers/Data/Application/8C8E7EB0-BC9B-435B-8EF8-8F5560EB0693/Documents LibraryDirectory /var/mobile/Containers/Data/Application/8C8E7EB0-BC9B-435B-8EF8-8F5560EB0693/Library ``` 您还可以在 **`/private/var/containers`** 中搜索应用名称: ```bash find /private/var/containers -name "Progname*" ``` 或使用 **`ps`** 和 **`lsof`**: ```bash ps -ef | grep -i lsof -p | grep -i "/containers" | head -n 1 ``` 如您所见,应用程序有两个主要位置: * **Bundle 目录** (`/var/containers/Bundle/Application/3ADAF47D-A734-49FA-B274-FBCA66589E67/`)。 * **Data 目录** (`/var/mobile/Containers/Data/Application/8C8E7EB0-BC9B-435B-8EF8-8F5560EB0693/`)。 这些文件夹包含在应用程序安全评估期间必须仔细检查的信息(例如,在分析存储的数据以查找敏感数据时)。 **Bundle 目录:** * **AppName.app** * 这是之前在 IPA 中看到的应用程序 Bundle,它包含必要的应用程序数据、静态内容以及应用程序的编译后二进制文件。 * 该目录对用户可见,但**用户无法写入**。 * 该目录的内容**不会被备份**。 * 该文件夹的内容用于**验证代码签名**。 **Data 目录:** * **Documents/** * 包含所有用户生成的数据。应用程序最终用户启动这些数据的创建。 * 对用户可见且**用户可以写入**。 * 该目录的内容**会被备份**。 * 应用程序可以通过设置 `NSURLIsExcludedFromBackupKey` 来禁用路径。 * **Library/** * 包含所有**非用户特定的文件**,例如**缓存**、**偏好设置**、**cookies** 和属性列表(plist)配置文件。 * iOS 应用程序通常使用 `Application Support` 和 `Caches` 子目录,但应用程序可以创建自定义子目录。 * **Library/Caches/** * 包含**半持久的缓存文件**。 * 对用户不可见且**用户无法写入**。 * 该目录的内容**不会被备份**。 * 当应用程序未运行且存储空间不足时,操作系统可能会自动删除该目录的文件。 * **Library/Application Support/** * 包含运行应用程序所必需的**持久文件**。 * 对**用户不可见**且用户无法写入。 * 该目录的内容**会被备份**。 * 应用程序可以通过设置 `NSURLIsExcludedFromBackupKey` 来禁用路径。 * **Library/Preferences/** * 用于存储即使在应用程序重启后也能**持久存在的属性**。 * 信息以未加密的形式保存在应用程序沙盒中名为 \[BUNDLE_ID].plist 的 plist 文件内。 * 使用 `NSUserDefaults` 存储的所有键/值对都可以在此文件中找到。 * **tmp/** * 使用此目录编写在应用程序启动之间**不需要持久存在的临时文件**。 * 包含非持久的缓存文件。 * 对用户**不可见**。 * 该目录的内容不会被备份。 * 当应用程序未运行且存储空间不足时,操作系统可能会自动删除该目录的文件。 让我们更仔细地看看 iGoat-Swift 的应用程序 Bundle(.app)目录,它位于 Bundle 目录内(`/var/containers/Bundle/Application/3ADAF47D-A734-49FA-B274-FBCA66589E67/iGoat-Swift.app`): ```bash OWASP.iGoat-Swift on (iPhone: 11.1.2) [usb] # ls NSFileType Perms NSFileProtection ... Name ------------ ------- ------------------ ... -------------------------------------- Regular 420 None ... rutger.html Regular 420 None ... mansi.html Regular 420 None ... splash.html Regular 420 None ... about.html Regular 420 None ... LICENSE.txt Regular 420 None ... Sentinel.txt Regular 420 None ... README.txt ``` ### 二进制逆向 在 `.app` 文件夹中,你会找到一个名为 `` 的二进制文件。这是将要**执行**的文件。你可以使用 **`otool`** 工具对二进制文件进行基本检查: ```bash otool -Vh DVIA-v2 #Check some compilation attributes magic cputype cpusubtype caps filetype ncmds sizeofcmds flags MH_MAGIC_64 ARM64 ALL 0x00 EXECUTE 65 7112 NOUNDEFS DYLDLINK TWOLEVEL WEAK_DEFINES BINDS_TO_WEAK PIE otool -L DVIA-v2 #Get third party libraries DVIA-v2: /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.1) /usr/lib/libsqlite3.dylib (compatibility version 9.0.0, current version 274.6.0) /usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.11) @rpath/Bolts.framework/Bolts (compatibility version 1.0.0, current version 1.0.0) [...] ``` **检查应用是否加密** 查看以下命令是否有输出: ```bash otool -l | grep -A 4 LC_ENCRYPTION_INFO ``` **拆解二进制文件** 拆解文本段: ```bash otool -tV DVIA-v2 DVIA-v2: (__TEXT,__text) section +[DDLog initialize]: 0000000100004ab8 sub sp, sp, #0x60 0000000100004abc stp x29, x30, [sp, #0x50] ; Latency: 6 0000000100004ac0 add x29, sp, #0x50 0000000100004ac4 sub x8, x29, #0x10 0000000100004ac8 mov x9, #0x0 0000000100004acc adrp x10, 1098 ; 0x10044e000 0000000100004ad0 add x10, x10, #0x268 ``` 打印示例应用程序的**Objective-C 段**,可以使用: ```bash otool -oV DVIA-v2 DVIA-v2: Contents of (__DATA,__objc_classlist) section 00000001003dd5b8 0x1004423d0 _OBJC_CLASS_$_DDLog isa 0x1004423a8 _OBJC_METACLASS_$_DDLog superclass 0x0 _OBJC_CLASS_$_NSObject cache 0x0 __objc_empty_cache vtable 0x0 data 0x1003de748 flags 0x80 instanceStart 8 ``` 为了获得更紧凑的Objective-C代码,您可以使用[**class-dump**](http://stevenygard.com/projects/class-dump/): ```bash class-dump some-app // // Generated by class-dump 3.5 (64 bit). // // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard. // #pragma mark Named Structures struct CGPoint { double _field1; double _field2; }; struct CGRect { struct CGPoint _field1; struct CGSize _field2; }; struct CGSize { double _field1; double _field2; }; ``` 然而,拆解二进制文件的最佳选择是:[**Hopper**](https://www.hopperapp.com/download.html?) 和 [**IDA**](https://www.hex-rays.com/products/ida/support/download_freeware/)。
使用 [**Trickest**](https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks) 轻松构建并**自动化工作流程**,由世界上**最先进**的社区工具提供支持。 立即获取访问权限: {% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %} ## 数据存储 要了解 iOS 如何在设备中存储数据,请阅读此页面: {% content-ref url="ios-basics.md" %} [ios-basics.md](ios-basics.md) {% endcontent-ref %} {% hint style="warning" %} 以下存储信息的地方应该在**安装应用程序后立即检查**,在**检查应用程序的所有功能后**,甚至在**从一个用户注销并登录到另一个用户后**也要检查。 目标是找到应用程序的**未受保护的敏感信息**(密码、令牌),当前用户和之前登录用户的信息。 {% endhint %} ### Plist **plist** 文件是结构化的 XML 文件,**包含键值对**。它是一种存储持久数据的方式,因此有时你可能会在这些文件中发现**敏感信息**。建议在安装应用程序后以及密集使用后检查这些文件,以查看是否写入了新数据。 在 plist 文件中持久化数据的最常见方式是通过使用 **NSUserDefaults**。这个 plist 文件保存在应用程序沙盒中的 **`Library/Preferences/.plist`** [`NSUserDefaults`](https://developer.apple.com/documentation/foundation/nsuserdefaults) 类提供了一个与默认系统交互的编程接口。默认系统允许应用程序根据**用户偏好**自定义其行为。由 `NSUserDefaults` 保存的数据可以在应用程序包中查看。这个类将**数据**存储在**plist** **文件**中,但它旨在用于少量数据。 这些数据不能再通过可信计算机直接访问,但可以通过执行**备份**来访问。 你可以使用 objection 的 `ios nsuserdefaults get` **转储**使用 **`NSUserDefaults`** 保存的信息。 要找到应用程序使用的所有 plist,你可以访问 `/private/var/mobile/Containers/Data/Application/{APPID}` 并运行: ```bash find ./ -name "*.plist" ``` 文件可能是**XML或二进制(bplist)**格式。您可以使用一个简单的命令将其**转换为XML**格式: * 在macOS上使用`plutil`,这是一个自macOS 10.2及以上版本自带的工具(目前没有官方在线文档): ```bash $ plutil -convert xml1 Info.plist ``` * 在Linux上: ```bash $ apt install libplist-utils $ plistutil -i Info.plist -o Info_xml.plist ``` * 在一个objection的会话中: ```bash ios plist cat /private/var/mobile/Containers/Data/Application/AF1F534B-1B8F-0825-ACB21-C0301AB7E56D/Library/Preferences/com.some.package.app.plist ``` ### Core Data [`Core Data`](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreData/nsfetchedresultscontroller.html#//apple_ref/doc/uid/TP40001075-CH8-SW1) 是一个框架,用于管理应用程序中对象的模型层。[Core Data可以使用SQLite作为其持久存储](https://cocoacasts.com/what-is-the-difference-between-core-data-and-sqlite/),但该框架本身不是数据库。\ 默认情况下,CoreData不会加密其数据。然而,可以向CoreData添加额外的加密层。更多详情请查看[GitHub仓库](https://github.com/project-imas/encrypted-core-data)。 您可以在路径`/private/var/mobile/Containers/Data/Application/{APPID}/Library/Application Support`中找到应用程序的SQLite Core Data信息。 **如果您能打开SQLite并访问敏感信息,那么您发现了一个配置错误。** {% code title="来自iGoat的代码" %} ```objectivec -(void)storeDetails { AppDelegate * appDelegate = (AppDelegate *)(UIApplication.sharedApplication.delegate); NSManagedObjectContext *context =[appDelegate managedObjectContext]; User *user = [self fetchUser]; if (user) { return; } user = [NSEntityDescription insertNewObjectForEntityForName:@"User" inManagedObjectContext:context]; user.email = CoreDataEmail; user.password = CoreDataPassword; NSError *error; if (![context save:&error]) { NSLog(@"Error in saving data: %@", [error localizedDescription]); }else{ NSLog(@"data stored in core data"); } } ``` {% endcode %} ### YapDatabase [YapDatabase](https://github.com/yapstudios/YapDatabase) 是一个建立在 SQLite 之上的键/值存储库。\ 由于 Yap 数据库是 sqlite 数据库,您可以使用上一节中提出的命令找到它们。 ### 其他 SQLite 数据库 应用程序创建自己的 sqlite 数据库是很常见的。它们可能会在这些数据库上**存储** **敏感** **数据**,并且留下未加密的数据。因此,检查应用程序目录内的每一个数据库总是很有意义的。所以请前往保存数据的应用程序目录(`/private/var/mobile/Containers/Data/Application/{APPID}`) ```bash find ./ -name "*.sqlite" -or -name "*.db" ``` ### Firebase 实时数据库 它可以被应用程序开发者利用来**存储和同步数据到一个 NoSQL 云托管数据库**。数据以 JSON 形式存储,并实时同步到每个连接的客户端,即使应用程序离线,数据也依然可用。 您可以在这里找到如何检查配置不当的 Firebase 数据库的方法: {% content-ref url="../../network-services-pentesting/pentesting-web/buckets/firebase-database.md" %} [firebase-database.md](../../network-services-pentesting/pentesting-web/buckets/firebase-database.md) {% endcontent-ref %} ### Realm 数据库 [Realm Objective-C](https://realm.io/docs/objc/latest/) 和 [Realm Swift](https://realm.io/docs/swift/latest/) 并非由苹果提供,但它们仍然值得注意。它们**存储所有内容未加密,除非配置启用了加密**。 您可以在 `/private/var/mobile/Containers/Data/Application/{APPID}` 找到这些数据库。 ```bash iPhone:/private/var/mobile/Containers/Data/Application/A079DF84-726C-4AEA-A194-805B97B3684A/Documents root# ls default.realm default.realm.lock default.realm.management/ default.realm.note| $ find ./ -name "*.realm*" ``` ```markdown 您可以使用工具 [**Realm Studio**](https://github.com/realm/realm-studio) 来打开这些数据库文件。 以下示例演示了如何在Realm数据库中使用加密: ``` ```swift // Open the encrypted Realm file where getKey() is a method to obtain a key from the Keychain or a server let config = Realm.Configuration(encryptionKey: getKey()) do { let realm = try Realm(configuration: config) // Use the Realm as normal } catch let error as NSError { // If the encryption key is wrong, `error` will say that it's an invalid database fatalError("Error opening realm: \(error)") } ``` ### Couchbase轻量级数据库 [Couchbase Lite](https://github.com/couchbase/couchbase-lite-ios) 是一个轻量级的、嵌入式的、面向文档的(NoSQL)数据库引擎,可以进行同步。它为iOS和macOS提供了原生编译。 检查可能存在的couchbase数据库,在 `/private/var/mobile/Containers/Data/Application/{APPID}/Library/Application Support/` 路径下。 ### Cookies iOS将应用的cookies存储在每个应用文件夹内的 **`Library/Cookies/cookies.binarycookies`** 中。然而,开发者有时决定将它们保存在 **keychain** 中,因为上述的 **cookie文件可以在备份中被访问**。 要检查cookies文件,你可以使用[**这个python脚本**](https://github.com/mdegrazia/Safari-Binary-Cookie-Parser) 或使用objection的 **`ios cookies get`.**\ **你也可以使用objection** 将这些文件转换为JSON格式并检查数据。 ```bash ...itudehacks.DVIAswiftv2.develop on (iPhone: 13.2.3) [usb] # ios cookies get --json [ { "domain": "highaltitudehacks.com", "expiresDate": "2051-09-15 07:46:43 +0000", "isHTTPOnly": "false", "isSecure": "false", "name": "username", "path": "/", "value": "admin123", "version": "0" } ] ``` ### 缓存 默认情况下,NSURLSession 会将数据(如 **HTTP 请求和响应存储在 Cache.db** 数据库中)。如果缓存了令牌、用户名或任何其他敏感信息,这个数据库可能包含**敏感数据**。要查找缓存的信息,请打开应用的数据目录(`/var/mobile/Containers/Data/Application/`),然后转到 `/Library/Caches/`。**WebKit 缓存也存储在 Cache.db** 文件中。**Objection** 可以使用命令 `sqlite connect Cache.db` 打开并与数据库交互,因为它是一个**普通的 SQLite 数据库**。 **建议禁用缓存这些数据**,因为请求或响应中可能包含敏感信息。下面的列表显示了实现这一目标的不同方法: 1. 建议在注销后删除缓存的响应。这可以通过 Apple 提供的名为 [`removeAllCachedResponses`](https://developer.apple.com/documentation/foundation/urlcache/1417802-removeallcachedresponses) 的方法来完成。您可以按如下方式调用此方法: `URLCache.shared.removeAllCachedResponses()` 此方法将从 Cache.db 文件中删除所有缓存的请求和响应。 2. 如果您不需要使用 cookies 的优势,建议只使用 URLSession 的 [.ephemeral](https://developer.apple.com/documentation/foundation/urlsessionconfiguration/1410529-ephemeral) 配置属性,这将禁用保存 cookies 和缓存。 [Apple 文档](https://developer.apple.com/documentation/foundation/urlsessionconfiguration/1410529-ephemeral): `一个临时会话配置对象类似于默认会话配置(见 default),除了相应的会话对象不会将缓存、凭证存储或任何会话相关数据存储到磁盘。相反,会话相关数据存储在 RAM 中。临时会话将数据写入磁盘的唯一时间是当您告诉它将 URL 的内容写入文件时。` 3. 也可以通过将缓存策略设置为 [.notAllowed](https://developer.apple.com/documentation/foundation/urlcache/storagepolicy/notallowed) 来禁用缓存。它将禁用以任何方式存储缓存,无论是在内存中还是在磁盘上。 ### 快照 每当您按下主屏幕按钮时,iOS **会对当前屏幕进行快照**,以便能够更平滑地过渡到应用程序。然而,如果当前屏幕中存在**敏感** **数据**,它将被**保存**在**图像**中(这**持续** **跨越** **重启**)。这些快照也可以通过双击主屏幕按钮在应用之间切换时访问。 除非 iPhone 被越狱,否则**攻击者**需要有**访问** **设备** **未锁定**的权限才能看到这些截图。默认情况下,最后一个快照存储在应用程序的沙盒中的 `Library/Caches/Snapshots/` 或 `Library/SplashBoard/Snapshots` 文件夹中(从 iOS 7.0 开始,受信任的计算机无法访问文件系统)。 防止这种不良行为的一种方法是在使用 `ApplicationDidEnterBackground()` 函数进行快照之前放置一个空白屏幕或删除敏感数据。 以下是一个设置默认截图的样本修复方法。 Swift: ```swift private var backgroundImage: UIImageView? func applicationDidEnterBackground(_ application: UIApplication) { let myBanner = UIImageView(image: #imageLiteral(resourceName: "overlayImage")) myBanner.frame = UIScreen.main.bounds backgroundImage = myBanner window?.addSubview(myBanner) } func applicationWillEnterForeground(_ application: UIApplication) { backgroundImage?.removeFromSuperview() } ``` Objective-C: ``` @property (UIImageView *)backgroundImage; - (void)applicationDidEnterBackground:(UIApplication *)application { UIImageView *myBanner = [[UIImageView alloc] initWithImage:@"overlayImage.png"]; self.backgroundImage = myBanner; self.backgroundImage.bounds = UIScreen.mainScreen.bounds; [self.window addSubview:myBanner]; } - (void)applicationWillEnterForeground:(UIApplication *)application { [self.backgroundImage removeFromSuperview]; } ``` 这将在应用程序进入后台时将背景图片设置为`overlayImage.png`。它可以防止敏感数据泄露,因为`overlayImage.png`将始终覆盖当前视图。 ### 钥匙串 工具如 [**Keychain-Dumper**](https://github.com/ptoomey3/Keychain-Dumper) 可用于转储钥匙串(设备必须越狱)。\ 您也可以使用 [**Objection**](https://github.com/sensepost/objection) 中的 `ios keychain dump`**。** **NSURLCredential** **NSURLCredential** 是将**用户名和密码存储在钥匙串中**的完美类。无需烦恼 NSUserDefaults 或任何钥匙串包装器。\ 一旦用户登录,您可以将他的用户名和密码存储到钥匙串: ```swift NSURLCredential *credential; credential = [NSURLCredential credentialWithUser:username password:password persistence:NSURLCredentialPersistencePermanent]; [[NSURLCredentialStorage sharedCredentialStorage] setCredential:credential forProtectionSpace:self.loginProtectionSpace]; ``` 您可以使用 **Objection** 的 `ios nsurlcredentialstorage dump` 来导出这些秘密信息。 ## 自定义键盘/键盘缓存 从iOS 8.0开始,苹果允许为iOS安装自定义扩展,如自定义键盘。\ 已安装的键盘可以通过 **设置** > **通用** > **键盘** > **键盘** 管理。\ 自定义键盘可以用来**嗅探** **按键操作**并将它们发送到攻击者服务器。但请注意,**需要网络连接的自定义键盘会通知用户。**\ 此外,**用户可以切换到不同的**(更值得信赖的)**键盘**来输入凭据。 此外,**应用程序可以阻止用户在应用内使用自定义键盘**(或至少对应用的敏感部分)。 {% hint style="warning" %} 如果您认为用户不需要第三方键盘,建议不允许使用第三方键盘。 {% endhint %} 请注意,由于自动更正和自动建议功能,如果没有将属性 **secureTextEntry** 设置为 **true** 或 **autoCorrectionType** 设置为 **UITextAutoCorrectionTypeNo**,默认的iOS键盘会在缓存文件中捕获并存储每个非标准单词。\ 默认情况下,键盘会在应用程序沙盒的 `Library/Keyboard/{locale}-dynamic-text.dat` 文件或 `/private/var/mobile/Library/Keyboard/dynamic-text.dat` 中**存储这个缓存**。然而,它可能会将数据保存在其他地方。\ 可以在 _**设置**_ > _**通用**_ > _**重置**_ > _**重置键盘字典**_ 中重置缓存。 {% hint style="info" %} 因此,**始终检查这些文件**并搜索可能的**敏感** **信息**。\ **拦截网络流量**是另一种检查自定义键盘是否将按键操作发送到远程服务器的方法。 {% endhint %} [UITextInputTraits 协议](https://developer.apple.com/reference/uikit/uitextinputtraits) 用于键盘缓存。UITextField、UITextView 和 UISearchBar 类自动支持此协议,并提供以下属性: * `var autocorrectionType: UITextAutocorrectionType` 决定在打字时是否启用自动更正。当启用自动更正时,文本对象会跟踪未知单词并建议合适的替换词,除非用户覆盖替换,否则会自动替换输入的文本。此属性的默认值是 `UITextAutocorrectionTypeDefault`,对于大多数输入方法来说,会启用自动更正。 * `var secureTextEntry: BOOL` 决定是否禁用文本复制和文本缓存,并隐藏 `UITextField` 输入的文本。此属性的默认值是 `NO`。 **要在代码中识别此行为:** * 搜索源代码中的类似实现,例如 ```objectivec textObject.autocorrectionType = UITextAutocorrectionTypeNo; textObject.secureTextEntry = YES; ``` * 在Xcode的`Interface Builder`中打开xib和storyboard文件,并在`Attributes Inspector`中验证适当对象的`Secure Text Entry`和`Correction`的状态。 应用程序必须防止敏感信息输入到文本字段中的缓存。您可以通过编程禁用缓存,使用`textObject.autocorrectionType = UITextAutocorrectionTypeNo`指令在所需的UITextFields、UITextViews和UISearchBars中实现。对于应该掩码的数据,如PIN码和密码,设置`textObject.secureTextEntry`为`YES`。 ```objectivec UITextField *textField = [ [ UITextField alloc ] initWithFrame: frame ]; textField.autocorrectionType = UITextAutocorrectionTypeNo; ``` ## **日志** 调试代码最常见的方法是使用日志记录,应用程序**可能会在日志中打印敏感信息**。\ 在iOS 6及以下版本中,日志是全球可读的(恶意应用程序可以读取其他应用程序的日志并从中提取敏感信息)。**如今,应用程序只能访问它们自己的日志**。 然而,拥有**物理** **访问**权限的**攻击者**可以将设备连接到计算机并**读取日志**(请注意,如果应用程序被卸载,由应用程序写入磁盘的日志不会被删除)。 建议**浏览应用程序的所有屏幕**,与**每个**用户界面元素和**功能**进行**交互**,在所有文本字段中提供输入文本,并**查看日志**,寻找暴露的**敏感** **信息**。 使用以下关键词检查应用程序源代码中的预定义和自定义日志记录语句: * 对于预定义和内置函数: * NSLog * NSAssert * NSCAssert * fprintf * 对于自定义函数: * Logging * Logfile **监控系统日志** 许多应用程序会将信息性(并且可能是敏感的)消息记录到控制台日志中。日志还包含崩溃报告和其他有用信息。 您可以使用这些工具: ```bash idevice_id --list # To find the device ID idevicesyslog -u (| grep ) # To get the device logs ``` 您可以通过以下方式通过Xcode **设备**窗口收集控制台日志: 1. 启动Xcode。 2. 将您的设备连接到主机电脑。 3. 选择 **窗口** -> **设备与模拟器**。 4. 在设备窗口的左侧部分点击您已连接的iOS设备。 5. 复现问题。 6. 点击位于设备窗口右上角的 **打开控制台** 按钮,在单独的窗口中查看控制台日志。 ![](<../../.gitbook/assets/image (466) (2) (2) (2) (2) (2) (2) (2) (3) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (15).png>) 您还可以按照在访问设备Shell中解释的方法连接到设备Shell,通过 **apt-get** 安装 **socat** 并运行以下命令: ```bash iPhone:~ root# socat - UNIX-CONNECT:/var/run/lockdown/syslog.sock ======================== ASL is here to serve you > watch OK Jun 7 13:42:14 iPhone chmod[9705] : MS:Notice: Injecting: (null) [chmod] (1556.00) Jun 7 13:42:14 iPhone readlink[9706] : MS:Notice: Injecting: (null) [readlink] (1556.00) Jun 7 13:42:14 iPhone rm[9707] : MS:Notice: Injecting: (null) [rm] (1556.00) Jun 7 13:42:14 iPhone touch[9708] : MS:Notice: Injecting: (null) [touch] (1556.00) ... ```
\ 使用 [**Trickest**](https://trickest.com/?utm\_campaign=hacktrics\&utm\_medium=banner\&utm\_source=hacktricks) 来轻松构建并**自动化工作流程**,这些工作流程由世界上**最先进的**社区工具提供支持。\ 立即获取访问权限: {% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %} ## 备份 iOS 包含自动备份功能,可以创建存储在设备上的数据的副本。您可以使用 iTunes(直到 macOS Catalina)或 Finder(从 macOS Catalina 开始)从主机计算机**创建 iOS 备份**,或通过 iCloud 备份功能。在这两种情况下,备份几乎包括 iOS 设备上存储的所有数据,除了像 Apple Pay 信息和 Touch ID 设置这样的高度敏感数据。 由于 iOS 会备份已安装的应用及其数据,一个明显的担忧是应用存储的**敏感用户数据**是否可能**通过备份无意中泄露**。另一个较不明显的担忧是,是否可以通过修改备份来更改应用行为,以此来篡改用于保护数据或限制应用功能的**敏感配置设置**。这两种担忧都是有效的,这些漏洞在当今大量应用中被证实存在。 在安装了移动应用的设备上的备份将包括 [应用的私有目录](https://developer.apple.com/library/content/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html#//apple\_ref/doc/uid/TP40010672-CH2-SW12) 中的所有子目录(`Library/Caches/` 除外)和文件。\ 因此,**避免在应用的私有目录或子目录中的任何文件或文件夹内以明文存储敏感数据**。 尽管 `Documents/` 和 `Library/Application Support/` 中的所有文件默认情况下都会被备份,但您可以通过调用 `NSURL setResourceValue:forKey:error:` 并使用 `NSURLIsExcludedFromBackupKey` 键来[从备份中排除文件](https://developer.apple.com/library/content/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html#//apple\_ref/doc/uid/TP40010672-CH2-SW28)。\ 您可以使用 [NSURLIsExcludedFromBackupKey](https://developer.apple.com/reference/foundation/nsurl#//apple\_ref/c/data/NSURLIsExcludedFromBackupKey) 和 [CFURLIsExcludedFromBackupKey](https://developer.apple.com/reference/corefoundation/cfurl-rd7#//apple\_ref/c/data/kCFURLIsExcludedFromBackupKey) 文件系统属性来排除备份中的文件和目录。 {% hint style="warning" %} 因此,在检查应用的备份时,您应该检查是否可以访问**任何敏感信息**,以及是否可以通过**修改备份的某些设置**来**修改应用的任何敏感行为**并恢复备份 {% endhint %} **如何测试** 首先**创建设备的备份**(您可以使用 Finder 进行),并找出备份存储的位置。官方 Apple 文档将帮助您[找到您的 iPhone、iPad 和 iPod touch 的备份](https://support.apple.com/en-us/HT204215)。 一旦您找到了设备的备份(`/Users/carlos.martin/Library/Application Support/MobileSync/Backup/{deviceID}`),您可以开始使用 grep 等工具,或使用像 [iMazing](https://imazing.com) 这样的工具来寻找敏感信息。 要识别备份是否加密,您可以从位于备份目录根目录的 "Manifest.plist" 文件中检查名为 "IsEncrypted" 的键。以下示例显示了表明备份已加密的配置: ```markup ... Date 2021-03-12T17:43:33Z IsEncrypted ... ``` ```markdown 如果您需要处理加密备份,可以在 [DinoSec 的 GitHub 仓库](https://github.com/dinosec/iphone-dataprotection/tree/master/python_scripts)中找到一些 Python 脚本,例如 **backup_tool.py** 和 **backup_passwd.py**,它们是一个很好的起点。但请注意,它们可能不适用于最新的 iTunes/Finder 版本,可能需要调整。 您还可以使用工具 [**iOSbackup**](https://pypi.org/project/iOSbackup/) 来轻松读取和提取密码加密的 iOS 备份中的文件。 **如何修改行为** 在开源比特币钱包应用 [Bither](https://github.com/bither/bither-ios) 中,您会看到可以配置 PIN 来锁定 UI。\ 这个 PIN 存储在名为 `net.bither.plist` 的文件中的 **pin_code** **键** 中。\ 如果您从备份中清除这个键并恢复备份,您将能够访问钱包。 ## 测试内存中的敏感数据 敏感信息终将存储在内存中。目标是确保这些信息尽可能短暂地暴露。 要调查应用程序的内存,请首先创建一个 **内存转储**。或者,您也可以使用例如调试器之类的工具**实时分析内存**。无论您使用哪种方法,这都是一个非常容易出错的过程,因为转储提供了已执行函数留下的数据,您可能会错过执行关键步骤。此外,除非您知道您正在寻找的数据的足迹(它的确切值或其格式),否则在分析过程中忽视数据是非常容易的。例如,如果应用程序根据随机生成的对称密钥进行加密,除非您通过其他方式找到其值,否则您不太可能在内存中发现该密钥。 **检索和分析内存转储** 无论您是使用越狱设备还是非越狱设备,都可以使用 [objection](https://github.com/sensepost/objection) 和 [Fridump](https://github.com/Nightbringer21/fridump) 来转储应用程序的进程内存。 在内存被转储后(例如到一个名为 "memory" 的文件),根据您正在寻找的数据的性质,您将需要一系列不同的工具来处理和分析该内存转储。例如,如果您关注的是字符串,执行命令 `strings` 或 `rabin2 -zz` 来提取这些字符串可能就足够了。 ``` ```bash # using strings $ strings memory > strings.txt # using rabin2 $ rabin2 -ZZ memory > strings.txt ``` 在您喜欢的编辑器中打开 `strings.txt` 并仔细研究以识别敏感信息。 但是,如果您想检查其他类型的数据,您可能更愿意使用 radare2 及其搜索功能。有关搜索命令(`/?`)的更多信息和选项列表,请参阅 radare2 的帮助。以下仅显示其中的一部分: ```bash $ r2 [0x00000000]> /? Usage: /[!bf] [arg] Search stuff (see 'e??search' for options) |Use io.va for searching in non virtual addressing spaces | / foo\x00 search for string 'foo\0' | /c[ar] search for crypto materials | /e /E.F/i match regular expression | /i foo search for string 'foo' ignoring case | /m[?][ebm] magicfile search for magic, filesystems or binary headers | /v[1248] value look for an `cfg.bigendian` 32bit value | /w foo search for wide string 'f\0o\0o\0' | /x ff0033 search for hex string | /z min max search for strings of given size ... ``` **运行时内存分析** 通过使用 [**r2frida**](https://github.com/nowsecure/r2frida),您可以在应用运行时分析和检查内存,而无需进行转储。例如,您可以运行之前的 r2frida 搜索命令,并搜索内存中的字符串、十六进制值等。在执行此操作时,请记住,在使用 `r2 frida://usb//` 启动会话后,要在搜索命令(以及任何其他 r2frida 特定命令)前加上反斜杠 `\`。 ## 破损的加密技术 ### 糟糕的密钥管理流程 一些开发者将敏感数据保存在本地存储中,并使用代码中硬编码/可预测的密钥进行加密。这样做是不应该的,因为一些逆向工程可能允许攻击者提取机密信息。 ### 使用不安全和/或已弃用的算法 开发者不应使用**已弃用的算法**来执行授权**检查**、**存储**或**发送**数据。这些算法包括:RC4、MD4、MD5、SHA1... 如果使用**哈希**来存储密码,例如,应使用带盐的抗暴力破解**哈希**。 ### 检查 主要的检查是找出您是否可以在代码中找到**硬编码**的密码/秘密,或者这些是否是**可预测的**,以及代码是否使用了某种**弱**的**加密**算法。 值得注意的是,您可以使用 **objection** 自动**监控**一些**加密** **库**: ```swift ios monitor crypt ``` ```markdown 有关iOS加密API和库访问的**更多信息**,请访问 [https://mobile-security.gitbook.io/mobile-security-testing-guide/ios-testing-guide/0x06e-testing-cryptography](https://mobile-security.gitbook.io/mobile-security-testing-guide/ios-testing-guide/0x06e-testing-cryptography) ## 本地认证 测试人员应该意识到**本地认证应始终在远程端点强制执行**或基于加密原语。如果认证过程没有返回数据,攻击者可以轻松绕过本地认证。 [**Local Authentication framework**](https://developer.apple.com/documentation/localauthentication) 提供了一组API,供开发人员向用户扩展认证对话框。在连接到远程服务的上下文中,可以(并且推荐)利用 [keychain](https://developer.apple.com/library/content/documentation/Security/Conceptual/keychainServConcepts/01introduction/introduction.html) 来实现本地认证。 **指纹ID** 传感器由 [SecureEnclave安全协处理器](https://www.blackhat.com/docs/us-16/materials/us-16-Mandt-Demystifying-The-Secure-Enclave-Processor.pdf) 操作,并且不会将指纹数据暴露给系统的任何其他部分。除了Touch ID,苹果还推出了_Face ID_:允许基于面部识别的认证。 开发人员有两个选项可以整合Touch ID/Face ID认证: * `LocalAuthentication.framework` 是一个高级API,可用于**通过Touch ID认证用户**。应用程序无法访问与注册指纹相关的任何数据,只会通知认证是否成功。 * `Security.framework` 是一个较低级别的API,用于访问 [keychain services](https://developer.apple.com/documentation/security/keychain\_services)。如果您的应用程序需要**用生物识别认证保护一些秘密数据**,这是一个安全的选项,因为访问控制是在系统级别管理的,不容易被绕过。`Security.framework` 有一个C API,但有几个 [开源封装可用](https://www.raywenderlich.com/147308/secure-ios-user-data-keychain-touch-id),使得访问keychain像访问NSUserDefaults一样简单。 {% hint style="danger" %} 请注意,使用`LocalAuthentication.framework` 或 `Security.framework`,将是一个可以被攻击者绕过的控制,因为它只返回一个布尔值,没有数据可以继续进行。有关更多详细信息,请参见 [Don't touch me that way, by David Lindner et al](https://www.youtube.com/watch?v=XhXIHVGCFFM)。 {% endhint %} ### 本地认证框架 开发人员可以通过使用 **`LAContext`** 类的 **`evaluatePolicy`** 函数来显示**认证提示**。两个可用的策略定义了可接受的认证形式: * `deviceOwnerAuthentication`(Swift) 或 `LAPolicyDeviceOwnerAuthentication`(Objective-C):如果可用,用户将被提示进行Touch ID认证。如果没有激活Touch ID,则会请求设备密码。如果未启用设备密码,策略评估将失败。 * `deviceOwnerAuthenticationWithBiometrics` (Swift) 或 `LAPolicyDeviceOwnerAuthenticationWithBiometrics`(Objective-C):认证限制为生物识别,用户将被提示Touch ID。 **`evaluatePolicy` 函数返回一个布尔值**,指示用户是否已成功认证。这意味着它可以被轻松绕过(见下文) ### 使用Keychain的本地认证 **iOS keychain API可以(并且应该)用来实现本地认证**。在此过程中,应用程序将一个秘密认证令牌或另一块标识用户的秘密数据存储在keychain中。为了认证到远程服务,用户必须使用他们的密码或指纹解锁keychain,以获取秘密数据。 keychain允许使用特殊的`SecAccessControl`属性保存项目,该属性将仅在用户通过Touch ID认证(或密码,如果属性参数允许此类回退)后允许从keychain访问项目。 在以下示例中,我们将字符串"test\_strong\_password"保存到keychain。该字符串只能在当前设备上访问,同时设置了密码(`kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly` 参数)并且仅在当前注册的指纹通过Touch ID认证后(`SecAccessControlCreateFlags.biometryCurrentSet` 参数): {% tabs %} {% tab title="Swift" %} ``` ```swift // 1. create AccessControl object that will represent authentication settings var error: Unmanaged? guard let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, SecAccessControlCreateFlags.biometryCurrentSet, &error) else { // failed to create AccessControl object return } // 2. define keychain services query. Pay attention that kSecAttrAccessControl is mutually exclusive with kSecAttrAccessible attribute var query: [String: Any] = [:] query[kSecClass as String] = kSecClassGenericPassword query[kSecAttrLabel as String] = "com.me.myapp.password" as CFString query[kSecAttrAccount as String] = "OWASP Account" as CFString query[kSecValueData as String] = "test_strong_password".data(using: .utf8)! as CFData query[kSecAttrAccessControl as String] = accessControl // 3. save item let status = SecItemAdd(query as CFDictionary, nil) if status == noErr { // successfully saved } else { // error while saving } ``` {% endtab %} {% tab title="Objective-C" %} ```objectivec // 1. create AccessControl object that will represent authentication settings CFErrorRef *err = nil; SecAccessControlRef sacRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlUserPresence, err); // 2. define keychain services query. Pay attention that kSecAttrAccessControl is mutually exclusive with kSecAttrAccessible attribute NSDictionary* query = @{ (_ _bridge id)kSecClass: (__bridge id)kSecClassGenericPassword, (__bridge id)kSecAttrLabel: @"com.me.myapp.password", (__bridge id)kSecAttrAccount: @"OWASP Account", (__bridge id)kSecValueData: [@"test_strong_password" dataUsingEncoding:NSUTF8StringEncoding], (__bridge id)kSecAttrAccessControl: (__bridge_transfer id)sacRef }; // 3. save item OSStatus status = SecItemAdd((__bridge CFDictionaryRef)query, nil); if (status == noErr) { // successfully saved } else { // error while saving } ``` {% endtab %} {% endtabs %} 现在我们可以从钥匙串请求保存的项目。钥匙串服务将向用户展示认证对话框,并根据是否提供了合适的指纹,返回数据或nil。 {% tabs %} {% tab title="Swift" %} ```swift // 1. define query var query = [String: Any]() query[kSecClass as String] = kSecClassGenericPassword query[kSecReturnData as String] = kCFBooleanTrue query[kSecAttrAccount as String] = "My Name" as CFString query[kSecAttrLabel as String] = "com.me.myapp.password" as CFString query[kSecUseOperationPrompt as String] = "Please, pass authorisation to enter this area" as CFString // 2. get item var queryResult: AnyObject? let status = withUnsafeMutablePointer(to: &queryResult) { SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0)) } if status == noErr { let password = String(data: queryResult as! Data, encoding: .utf8)! // successfully received password } else { // authorization not passed } ``` {% endtab %} {% tab title="Objective-C" %} ```objectivec // 1. define query NSDictionary *query = @{(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword, (__bridge id)kSecReturnData: @YES, (__bridge id)kSecAttrAccount: @"My Name1", (__bridge id)kSecAttrLabel: @"com.me.myapp.password", (__bridge id)kSecUseOperationPrompt: @"Please, pass authorisation to enter this area" }; // 2. get item CFTypeRef queryResult = NULL; OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &queryResult); if (status == noErr){ NSData* resultData = ( __bridge_transfer NSData* )queryResult; NSString* password = [[NSString alloc] initWithData:resultData encoding:NSUTF8StringEncoding]; NSLog(@"%@", password); } else { NSLog(@"Something went wrong"); } ``` {% endtab %} {% endtabs %} ### 检测 通过分析应用二进制文件中共享动态库的列表,也可以检测到框架的使用情况。这可以通过使用 `otool` 来完成: ```bash $ otool -L .app/ ``` 如果应用程序中使用了`LocalAuthentication.framework`,输出将包含以下两行(记住`LocalAuthentication.framework`在底层使用了`Security.framework`): ```bash /System/Library/Frameworks/LocalAuthentication.framework/LocalAuthentication /System/Library/Frameworks/Security.framework/Security ``` ### 本地认证框架绕过 #### Objection [**Objection 生物识别绕过**](https://github.com/sensepost/objection/wiki/Understanding-the-iOS-Biometrics-Bypass) 可用于绕过LocalAuthentication。Objection **使用Frida来植入`evaluatePolicy`函数,使其即使在未成功进行认证的情况下也返回`True`**。使用`ios ui biometrics_bypass`命令来绕过不安全的生物识别认证。Objection将注册一个作业,该作业将替换`evaluatePolicy`的结果。它在Swift和Objective-C实现中都有效。 ```bash ...itudehacks.DVIAswiftv2.develop on (iPhone: 13.2.3) [usb] # ios ui biometrics_bypass (agent) Registering job 3mhtws9x47q. Type: ios-biometrics-disable ...itudehacks.DVIAswiftv2.develop on (iPhone: 13.2.3) [usb] # (agent) [3mhtws9x47q] Localized Reason for auth requirement: Please authenticate yourself (agent) [3mhtws9x47q] OS authentication response: false (agent) [3mhtws9x47q] Marking OS response as True instead (agent) [3mhtws9x47q] Biometrics bypass hook complete ``` #### Frida 以下是一个使用 **`evaluatePolicy`** 的例子,来自 [DVIA-v2 应用程序](https://github.com/prateek147/DVIA-v2): ```swift +(void)authenticateWithTouchID { LAContext *myContext = [[LAContext alloc] init]; NSError *authError = nil; NSString *myLocalizedReasonString = @"Please authenticate yourself"; if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) { [myContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:myLocalizedReasonString reply:^(BOOL success, NSError *error) { if (success) { dispatch_async(dispatch_get_main_queue(), ^{ [TouchIDAuthentication showAlert:@"Authentication Successful" withTitle:@"Success"]; }); } else { dispatch_async(dispatch_get_main_queue(), ^{ [TouchIDAuthentication showAlert:@"Authentication Failed !" withTitle:@"Error"]; }); } }]; } else { dispatch_async(dispatch_get_main_queue(), ^{ [TouchIDAuthentication showAlert:@"Your device doesn't support Touch ID or you haven't configured Touch ID authentication on your device" withTitle:@"Error"]; }); } } ``` 为了绕过本地认证,我们需要编写一个Frida脚本来**绕过**上述的 _**evaluatePolicy**_ 检查。正如你在上面粘贴的代码片段中看到的,**evaluatePolicy** 使用了一个**回调**来决定**结果**。因此,实现这个黑客攻击的最简单方法是拦截那个回调,并确保它总是返回_**success=1**。 ```swift // from https://securitycafe.ro/2022/09/05/mobile-pentesting-101-bypassing-biometric-authentication/ if(ObjC.available) { console.log("Injecting..."); var hook = ObjC.classes.LAContext["- evaluatePolicy:localizedReason:reply:"]; Interceptor.attach(hook.implementation, { onEnter: function(args) { var block = new ObjC.Block(args[4]); const callback = block.implementation; block.implementation = function (error, value) { console.log("Changing the result value to true") const result = callback(1, null); return result; }; }, }); } else { console.log("Objective-C Runtime is not available!"); } ``` ``` frida -U -f com.highaltitudehacks.DVIAswiftv2 --no-pause -l fingerprint-bypass-ios.js ``` ## 通过 IPC 暴露敏感功能 ### 自定义 URI 处理程序 / 深层链接 / 自定义方案 {% content-ref url="ios-custom-uri-handlers-deeplinks-custom-schemes.md" %} [ios-custom-uri-handlers-deeplinks-custom-schemes.md](ios-custom-uri-handlers-deeplinks-custom-schemes.md) {% endcontent-ref %} ### 通用链接 {% content-ref url="ios-universal-links.md" %} [ios-universal-links.md](ios-universal-links.md) {% endcontent-ref %} ### UIActivity 分享 {% content-ref url="ios-uiactivity-sharing.md" %} [ios-uiactivity-sharing.md](ios-uiactivity-sharing.md) {% endcontent-ref %} ### UIPasteboard {% content-ref url="ios-uipasteboard.md" %} [ios-uipasteboard.md](ios-uipasteboard.md) {% endcontent-ref %} ### 应用扩展 {% content-ref url="ios-app-extensions.md" %} [ios-app-extensions.md](ios-app-extensions.md) {% endcontent-ref %} ### WebViews {% content-ref url="ios-webviews.md" %} [ios-webviews.md](ios-webviews.md) {% endcontent-ref %} ### 序列化和编码 {% content-ref url="ios-serialisation-and-encoding.md" %} [ios-serialisation-and-encoding.md](ios-serialisation-and-encoding.md) {% endcontent-ref %} ## 网络通信 检查是否有**未加密**的通信发生非常重要,同时也要确保应用程序正确**验证服务器的 TLS 证书**。\ 要检查这些问题,你可以使用像 **Burp** 这样的代理: {% content-ref url="burp-configuration-for-ios.md" %} [burp-configuration-for-ios.md](burp-configuration-for-ios.md) {% endcontent-ref %} ### 主机名检查 验证 TLS 证书时的一个常见问题是检查证书是否由**受信任的 CA**签名,但**不检查**证书的**主机名**是否是正在访问的主机名。\ 为了使用 Burp 检查这个问题,在 iPhone 上信任 Burp CA 后,你可以**使用 Burp 为不同的主机名创建一个新证书**并使用它。如果应用程序仍然可以工作,那么,它可能存在漏洞。 ### 证书锁定 如果一个应用程序正确使用了 SSL Pinning,那么应用程序只会在证书是预期中的证书时工作。在测试应用程序时,**这可能是一个问题,因为 Burp 会提供它自己的证书。**\ 为了在越狱设备中绕过这种保护,你可以安装应用程序 [**SSL Kill Switch**](https://github.com/nabla-c0d3/ssl-kill-switch2) 或安装 [**Burp Mobile Assistant**](https://portswigger.net/burp/documentation/desktop/mobile/config-ios-device) 你也可以使用 **objection** 的 `ios sslpinning disable` ## 杂项 * 在 **`/System/Library`** 中,你可以找到手机中由系统应用程序使用的框架 * 用户从 App Store 安装的应用程序位于 **`/User/Applications`** 内 * 而 **`/User/Library`** 包含用户级应用程序保存的数据 * 你可以访问 **`/User/Library/Notes/notes.sqlite`** 来阅读应用程序内保存的笔记。 * 在已安装应用程序的文件夹内(**`/User/Applications//`**),你可以找到一些有趣的文件: * **`iTunesArtwork`**:应用程序使用的图标 * **`iTunesMetadata.plist`**:在 App Store 中使用的应用信息 * **`/Library/*`**:包含偏好设置和缓存。在 **`/Library/Cache/Snapshots/*`** 中,你可以找到在将应用程序发送到后台之前对其执行的快照。 ### 热修复/强制更新 开发者可以远程**立即修补他们应用程序的所有安装**,而无需重新提交应用程序到 App Store 并等待它被批准。\ 通常使用 [**JSPatch**](https://github.com/bang590/JSPatch) 来实现这一目的。但也有其他选项,如 [Siren](https://github.com/ArtSabintsev/Siren) 和 [react-native-appstore-version-checker](https://www.npmjs.com/package/react-native-appstore-version-checker)。\ **这是一个可能被恶意第三方 SDK 滥用的危险机制,因此建议检查用于自动更新的方法(如果有的话)并对其进行测试。** 你可以尝试下载应用程序的旧版本来实现这一目的。 ### 第三方 第三方 SDK 的一个问题是,对 SDK 提供的功能**没有细粒度控制**。你可以使用 SDK 并拥有所有功能(包括诊断泄露和不安全的 HTTP 连接),或者不使用它。此外,通常应用程序开发者无法**修补** SDK 中的漏洞。\ 此外,一些 SDK 一旦被社区非常信任,就开始**包含恶意软件**。 此外,这些服务提供的功能可能涉及**跟踪服务以监控用户使用应用程序时的行为**,销售横幅广告,或改善用户体验。第三方服务的缺点是开发者不知道通过第三方库执行的代码的详细信息。因此,不应向服务发送超出必要的信息,也不应泄露敏感信息。 缺点是,**开发者不详细了解通过第三方库执行的代码**,因此放弃了可见性。因此,应确保只向服务发送所需的信息,并且不泄露敏感信息。 大多数第三方服务有两种实现方式: * 使用独立库 * 使用完整的 SDK 发送给第三方服务的所有数据都应该是匿名的,以防止暴露 PII(个人可识别信息),这将允许第三方识别用户账户。 你可以通过运行 **`otool`** 对应用程序(并**对**每个共享**库运行**它以找到更多使用的共享库)来找到应用程序使用的**库**。 ## **参考资料** * [https://mobile-security.gitbook.io/mobile-security-testing-guide/ios-testing-guide/0x06b-basic-security-testing#information-gathering](https://mobile-security.gitbook.io/mobile-security-testing-guide/ios-testing-guide/0x06b-basic-security-testing#information-gathering) * [iOS & Mobile App Pentesting - INE](https://my.ine.com/CyberSecurity/courses/089d060b/ios-mobile-app-pentesting) ## 更多信息 * [https://github.com/ivRodriguezCA/RE-iOS-Apps/](https://github.com/ivRodriguezCA/RE-iOS-Apps/) IOS 免费课程([https://syrion.me/blog/ios-swift-antijailbreak-bypass-frida/](https://syrion.me/blog/ios-swift-antijailbreak-bypass-frida/)) * [https://www.sans.org/reading-room/whitepapers/testing/ipwn-apps-pentesting-ios-applications-34577](https://www.sans.org/reading-room/whitepapers/testing/ipwn-apps-pentesting-ios-applications-34577) * [https://www.slideshare.net/RyanISI/ios-appsecurityminicourse](https://www.slideshare.net/RyanISI/ios-appsecurityminicourse) * [https://github.com/prateek147/DVIA](https://github.com/prateek147/DVIA) * [https://github.com/prateek147/DVIA-v2](https://github.com/prateek147/DVIA-v2) * [https://github.com/OWASP/MSTG-Hacking-Playground%20](https://github.com/OWASP/MSTG-Hacking-Playground) * OWASP iGoat [_https://github.com/OWASP/igoat_](https://github.com/OWASP/igoat) <<< Objective-C 版本 [_https://github.com/OWASP/iGoat-Swift_](https://github.com/OWASP/iGoat-Swift) <<< Swift 版本 * [https://github.com/authenticationfailure/WheresMyBrowser.iOS](https://github.com/authenticationfailure/WheresMyBrowser.iOS) * [https://github.com/nabla-c0d3/ssl-kill-switch2](https://github.com/nabla-c0d3/ssl-kill-switch2)
\ 使用 [**Trickest**](https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks) 轻松构建并**自动化工作流程**,由世界上**最先进的**社区工具提供支持。\ 立即获取访问权限: {% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}
从零开始学习 AWS 黑客攻击,成为 htARTE (HackTricks AWS 红队专家) 支持 HackTricks 的其他方式: * 如果你想在 HackTricks 中看到你的**公司广告**或**下载 HackTricks 的 PDF 版本**,请查看[**订阅计划**](https://github.com/sponsors/carlospolop)! * 获取[**官方 PEASS & HackTricks 商品**](https://peass.creator-spring.com) * 发现[**PEASS 家族**](https://opensea.io/collection/the-peass-family),我们独家的 [**NFTs**](https://opensea.io/collection/the-peass-family) 收藏 * **加入** 💬 [**Discord 群组**](https://discord.gg/hRep4RUj7f) 或 [**telegram 群组**](https://t.me/peass) 或在 **Twitter** 🐦 上**关注**我 [**@carlospolopm**](https://twitter.com/carlospolopm)**。** * **通过向** [**HackTricks**](https://github.com/carlospolop/hacktricks) 和 [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github 仓库提交 PR 来分享你的黑客技巧。