iOS Pentesting

使用Trickest可以轻松构建和自动化工作流程,使用全球最先进的社区工具。 立即获取访问权限:

从零开始学习AWS黑客技术,成为专家 htARTE(HackTricks AWS Red Team Expert)

支持HackTricks的其他方式:

iOS基础知识

测试环境

在此页面中,您可以找到关于iOS模拟器模拟器越狱的信息:

初始分析

基本iOS测试操作

在测试过程中会建议执行多个操作(连接到设备、读取/写入/上传/下载文件、使用一些工具...)。因此,如果您不知道如何执行这些操作,请开始阅读页面

在以下步骤中,应在设备上安装应用程序并已获取应用程序的IPA文件。 阅读基本iOS测试操作页面以了解如何执行此操作。

基本静态分析

建议使用工具MobSF对IPA文件执行自动静态分析。

识别二进制文件中存在的保护措施

  • PIE(位置无关可执行文件):启用后,应用程序每次启动时都会加载到随机内存地址,使其更难以预测其初始内存地址。

otool -hv <app-binary> | grep PIE   # 应包含PIE标志
  • 堆栈Canaries:为了验证堆栈的完整性,在调用函数之前在堆栈上放置一个“canary”值,并在函数结束时再次验证。

otool -I -v <app-binary> | grep stack_chk   # 应包含符号:stack_chk_guard和stack_chk_fail
  • ARC(自动引用计数):防止常见的内存损坏缺陷

otool -I -v <app-binary> | grep objc_release   # 应包含_objc_release符号
  • 加密二进制文件:二进制文件应该是加密的

otool -arch all -Vl <app-binary> | grep -A5 LC_ENCRYPT   # cryptid应为1

识别敏感/不安全函数

  • 弱哈希算法

# 在iOS设备上
otool -Iv <app> | grep -w "_CC_MD5"
otool -Iv <app> | grep -w "_CC_SHA1"

# 在Linux上
grep -iER "_CC_MD5"
grep -iER "_CC_SHA1"
  • 不安全的随机函数

# 在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’函数

# 在iOS设备上
otool -Iv <app> | grep -w "_malloc"

# 在Linux上
grep -iER "_malloc"
  • 不安全和易受攻击的函数

# 在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"

基本动态分析

查看MobSF执行的动态分析。您需要浏览不同视图并与其交互,但它将挂接多个类并执行其他操作,并在完成后准备报告。

列出已安装的应用程序

使用命令frida-ps -Uai来确定已安装应用程序的捆绑标识符

$ 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 轻松挂钩方法和类

IPA 结构

IPA 文件的结构本质上是一个压缩包。通过将其扩展名更改为 .zip,可以对其进行解压以显示其内容。在这个结构中,Bundle代表一个完全打包好准备安装的应用程序。在其中,您会找到一个名为 <NAME>.app 的目录,其中包含应用程序的资源。

  • Info.plist:此文件保存应用程序的特定配置详细信息。

  • _CodeSignature/:此目录包含一个包含签名的 plist 文件,确保捆绑包中所有文件的完整性。

  • Assets.car:存储像图标这样的资产文件的压缩存档。

  • Frameworks/:此文件夹包含应用程序的本机库,可能是 .dylib.framework 文件的形式。

  • PlugIns/:这可能包括应用程序的扩展,称为 .appex 文件,尽管它们并不总是存在。

  • Core Data:用于保存应用程序的永久数据以供离线使用、缓存临时数据,并在单个设备上为应用程序添加撤销功能。为了在单个 iCloud 帐户中跨多个设备同步数据,Core Data 会自动将您的模式镜像到 CloudKit 容器中。

  • PkgInfoPkgInfo 文件是指定应用程序或捆绑包的类型和创建者代码的另一种方式。

  • en.lproj, fr.proj, Base.lproj:是包含这些特定语言资源的语言包,以及在不支持某种语言时的默认资源。

  • 安全性_CodeSignature/ 目录通过数字签名验证所有捆绑文件的完整性,在应用程序的安全性方面起着关键作用。

  • 资产管理Assets.car 文件使用压缩来高效管理图形资产,这对于优化应用程序性能并减小其整体大小至关重要。

  • 框架和插件:这些目录突显了 iOS 应用程序的模块化,允许开发人员包含可重用的代码库(Frameworks/)并扩展应用功能(PlugIns/)。

  • 本地化:该结构支持多种语言,通过为特定语言包含资源,有助于全球应用程序覆盖范围。

Info.plist

Info.plist 作为 iOS 应用程序的基石,以键-值对的形式封装关键配置数据。这个文件不仅对应用程序是必需的,对于打包在其中的应用程序扩展和框架也是必需的。它以 XML 或二进制格式结构化,并包含从应用程序权限到安全配置等关键信息。要详细探索可用键,可以参考Apple 开发人员文档

对于希望以更易访问的格式处理此文件的人,可以通过 macOS 上的 plutil(在版本 10.2 及更高版本中本地可用)或 Linux 上的 plistutil 轻松实现 XML 转换。转换的命令如下:

  • 对于 macOS

$ plutil -convert xml1 Info.plist
  • 对于Linux系统

$ apt install libplist-utils
$ plistutil -i Info.plist -o Info_xml.plist

Info.plist文件可能泄露的大量信息中,值得注意的条目包括应用程序权限字符串(UsageDescription)、自定义URL schemes(CFBundleURLTypes)以及App Transport Security的配置(NSAppTransportSecurity)。这些条目以及其他条目,如导出/导入的自定义文档类型(UTExportedTypeDeclarations / UTImportedTypeDeclarations),可以通过检查文件或使用简单的grep命令轻松定位:

$ grep -i <keyword> Info.plist

数据路径

在iOS环境中,目录专门用于系统应用用户安装的应用。系统应用位于/Applications目录下,而用户安装的应用则放置在/var/mobile/containers/Data/Application/目录下。这些应用被分配一个称为128位UUID的唯一标识符,由于目录名称的随机性,手动定位应用文件夹的任务变得具有挑战性。

由于iOS中的应用必须进行沙箱化,每个应用还将在**$HOME/Library/Containers目录下拥有一个以应用的CFBundleIdentifier**命名的文件夹。

然而,这两个文件夹(数据和容器文件夹)都有一个名为**.com.apple.mobile_container_manager.metadata.plist**的文件,该文件在键MCMetadataIdentifier中链接了这两个文件。

为了便于发现用户安装的应用的安装目录,objection工具提供了一个有用的命令env。这个命令会显示有关所讨论应用的详细目录信息。以下是如何使用这个命令的示例:

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中搜索应用程序名称:

find /private/var/containers -name "Progname*"

命令诸如 pslsof 也可用于识别应用程序的进程和列出打开的文件,从而提供有关应用程序活动目录路径的见解:

ps -ef | grep -i <app-name>
lsof -p <pid> | grep -i "/containers" | head -n 1

Bundle directory:

  • Bundle目录:

  • AppName.app

  • 这是应用程序包,如之前在IPA中看到的,其中包含基本应用程序数据、静态内容以及应用程序的编译二进制文件。

  • 该目录对用户可见,但用户无法对其进行写入

  • 该目录中的内容不会被备份

  • 该文件夹的内容用于验证代码签名

Data directory:

  • Documents/

  • 包含所有用户生成的数据。应用程序最终用户启动创建这些数据。

  • 对用户可见,用户可以对其进行写入

  • 该目录中的内容已备份

  • 应用程序可以通过设置NSURLIsExcludedFromBackupKey来禁用路径。

  • Library/

  • 包含所有非特定于用户的文件,如缓存首选项cookies和属性列表(plist)配置文件。

  • iOS应用程序通常使用Application SupportCaches子目录,但应用程序可以创建自定义子目录。

  • Library/Caches/

  • 包含半持久性缓存文件

  • 对用户不可见,用户无法对其进行写入

  • 该目录中的内容不会被备份

  • 当应用程序未运行且存储空间不足时,操作系统可能会自动删除该目录中的文件。

  • Library/Application Support/

  • 包含运行应用程序所需的持久性文件

  • 对用户不可见,用户无法对其进行写入。

  • 该目录中的内容已备份

  • 应用程序可以通过设置NSURLIsExcludedFromBackupKey来禁用路径。

  • Library/Preferences/

  • 用于存储属性,这些属性可以在应用程序重新启动后持久存在

  • 信息以未加密的方式保存在应用程序沙箱中的名为[BUNDLE_ID].plist的plist文件中。

  • 使用NSUserDefaults存储的所有键/值对都可以在此文件中找到。

  • tmp/

  • 使用此目录来写入不需要在应用程序启动之间持久存在临时文件

  • 包含非持久性缓存文件。

  • 对用户不可见。

  • 该目录中的内容不会被备份。

  • 当应用程序未运行且存储空间不足时,操作系统可能会自动删除该目录中的文件。

让我们更仔细地查看iGoat-Swift的应用程序包(.app)目录,位于Bundle目录内(/var/containers/Bundle/Application/3ADAF47D-A734-49FA-B274-FBCA66589E67/iGoat-Swift.app):

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 对二进制文件进行基本检查:

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)
[...]

检查应用是否已加密

查看是否有任何输出:

otool -l <app-binary> | grep -A 4 LC_ENCRYPTION_INFO

反汇编二进制文件

反汇编文本部分:

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段,可以使用:

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 工具:

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;
};

然而,拆解二进制文件的最佳选项是:HopperIDA

使用 Trickest 来轻松构建和自动化工作流程,利用全球最先进的社区工具。 立即获取访问权限:

数据存储

要了解 iOS 如何在设备中存储数据,请阅读此页面:

应该在安装应用程序后立即检查以下存储信息的位置,在检查应用程序的所有功能后,甚至在从一个用户注销并登录到另一个用户后。 目标是查找应用程序(密码、令牌)、当前用户和先前登录用户的未受保护的敏感信息

Plist

plist 文件是结构化的 XML 文件,包含键值对。这是一种存储持久数据的方式,因此有时您可能会在这些文件中找到敏感信息。建议在安装应用程序后和在大量使用应用程序后检查这些文件,以查看是否写入了新数据。

在 plist 文件中持久保存数据的最常见方式是通过使用NSUserDefaults。此 plist 文件保存在应用程序沙盒中的**Library/Preferences/<appBundleID>.plist**

NSUserDefaults 类提供了与默认系统交互的编程接口。默认系统允许应用程序根据用户偏好自定义其行为。由 NSUserDefaults 保存的数据可以在应用程序包中查看。此类将数据存储在plist 文件中,但应该与少量数据一起使用。

这些数据无法直接通过受信任的计算机访问,但可以通过备份访问。

您可以使用 objection 的 ios nsuserdefaults get 转储使用 NSUserDefaults 保存的信息。

要查找应用程序使用的所有 plist,可以访问 /private/var/mobile/Containers/Data/Application/{APPID} 并运行:

find ./ -name "*.plist"

要将文件从**XML或二进制(bplist)**格式转换为XML,可以根据您的操作系统使用不同的方法:

对于 macOS 用户: 使用 plutil 命令。这是 macOS(10.2+)中的一个内置工具,专门用于此目的:

$ plutil -convert xml1 Info.plist

对于Linux用户: 首先安装libplist-utils,然后使用plistutil来转换您的文件:

$ apt install libplist-utils
$ plistutil -i Info.plist -o Info_xml.plist

在 Objection 会话中: 用于分析移动应用程序的特定命令允许您直接转换 plist 文件:

ios plist cat /private/var/mobile/Containers/Data/Application/<Application-UUID>/Library/Preferences/com.some.package.app.plist

Core Data

Core Data 是一个用于管理应用程序中对象模型层的框架。Core Data可以使用SQLite作为其持久存储,但该框架本身不是数据库。 CoreData默认不加密其数据。然而,可以向CoreData添加额外的加密层。查看GitHub Repo获取更多详细信息。

您可以在路径/private/var/mobile/Containers/Data/Application/{APPID}/Library/Application Support中找到应用程序的SQLite Core Data信息。

如果您可以打开SQLite并访问敏感信息,则表示您发现了配置错误。

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 是建立在 SQLite 之上的键/值存储。 由于 Yap 数据库是 SQLite 数据库,您可以使用上一节中提到的命令找到它们。

其他 SQLite 数据库

应用程序通常会创建自己的 SQLite 数据库。它们可能在其中存储敏感数据,并且未加密。因此,检查应用程序目录中的每个数据库始终是一件有趣的事情。因此,请转到保存数据的应用程序目录 (/private/var/mobile/Containers/Data/Application/{APPID})。

find ./ -name "*.sqlite" -or -name "*.db"

Firebase实时数据库

开发人员可以通过Firebase实时数据库在NoSQL云托管数据库存储和同步数据。数据以JSON格式存储,并实时同步到所有连接的客户端。

您可以在此处找到如何检查配置错误的Firebase数据库:

Realm数据库

Realm Objective-CRealm Swift 提供了一个强大的数据存储替代方案,这是苹果没有提供的。默认情况下,它们以未加密的方式存储数据,可以通过特定配置进行加密。

这些数据库位于:/private/var/mobile/Containers/Data/Application/{APPID}。要查看这些文件,可以使用如下命令:

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工具。

要在Realm数据库中实现加密,可以使用以下代码片段:

// 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被描述为一种轻量级嵌入式数据库引擎,遵循面向文档(NoSQL)的方法。设计为原生支持iOSmacOS,它提供了无缝同步数据的能力。

要识别设备上可能存在的Couchbase数据库,应检查以下目录:

ls /private/var/mobile/Containers/Data/Application/{APPID}/Library/Application Support/

Cookies

iOS将应用程序的cookies存储在每个应用程序文件夹中的**Library/Cookies/cookies.binarycookies中。然而,开发人员有时会决定将它们保存在钥匙串中,因为上述cookie文件可以在备份中访问**。

要检查cookies文件,您可以使用此Python脚本或使用objection的**ios cookies get**。 您还可以使用objection将这些文件转换为JSON格式并检查数据。

...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/<UUID>)并转到/Library/Caches/<Bundle Identifier>WebKit缓存也存储在Cache.db文件中。Objection可以使用命令sqlite connect Cache.db打开并与数据库交互,因为它是一个普通的SQLite数据库

建议禁用缓存这些数据,因为请求或响应中可能包含敏感信息。下面的列表显示了实现此目的的不同方法:

  1. 建议在注销后删除缓存的响应。可以使用苹果提供的名为removeAllCachedResponses的方法来执行此操作。您可以按如下方式调用此方法:

URLCache.shared.removeAllCachedResponses()

此方法将从Cache.db文件中删除所有缓存的请求和响应。 2. 如果您不需要使用cookies的优势,建议只使用URLSession的.ephemeral配置属性,这将禁用保存cookies和缓存。

苹果文档:

一个临时会话配置对象类似于默认会话配置(请参阅default),不同之处在于相应的会话对象不会将缓存、凭据存储或任何会话相关数据存储到磁盘。相反,会话相关数据存储在RAM中。临时会话仅在您告诉它将URL的内容写入文件时才会将数据写入磁盘。 3. 通过将缓存策略设置为.notAllowed也可以禁用缓存。这将禁止以任何方式存储缓存,无论是在内存中还是在磁盘上。

快照

每当您按下主屏幕按钮时,iOS都会拍摄当前屏幕的快照,以便能够更流畅地切换到应用程序。然而,如果当前屏幕中存在敏感数据,它将被保存图像中(这将持续 重启)。这些快照也可以通过双击主屏幕来访问,以在应用程序之间切换。

除非iPhone已越狱,否则攻击者需要解锁设备才能查看这些屏幕截图。默认情况下,最后一个快照存储在应用程序的沙盒中的Library/Caches/Snapshots/Library/SplashBoard/Snapshots文件夹中(受信任的计算机无法从iOX 7.0访问文件系统)。

防止这种不良行为的一种方法是在拍摄快照之前放置空白屏幕或删除敏感数据,使用ApplicationDidEnterBackground()函数。

以下是一个设置默认屏幕截图的示例修复方法。

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之类的工具,适用于越狱设备。此外,Objection提供了ios keychain dump命令,用于类似的目的。

存储凭据

NSURLCredential类非常适合直接将敏感信息存储在钥匙串中,无需使用NSUserDefault或其他包装器。要在登录后存储凭据,可以使用以下Swift代码:

NSURLCredential *credential;
credential = [NSURLCredential credentialWithUser:username password:password persistence:NSURLCredentialPersistencePermanent];
[[NSURLCredentialStorage sharedCredentialStorage] setCredential:credential forProtectionSpace:self.loginProtectionSpace];

自定义键盘和键盘缓存

从 iOS 8.0 开始,用户可以安装自定义键盘扩展,可在 设置 > 通用 > 键盘 > 键盘 下进行管理。虽然这些键盘提供了扩展功能,但它们存在记录按键和向外部服务器传输数据的风险,尽管用户会收到有关需要网络访问的键盘的通知。应用程序可以并且应该限制对自定义键盘用于输入敏感信息。

安全建议:

  • 建议禁用第三方键盘以增强安全性。

  • 注意默认 iOS 键盘的自动更正和自动建议功能,这些功能可能会将敏感信息存储在 Library/Keyboard/{locale}-dynamic-text.dat/private/var/mobile/Library/Keyboard/dynamic-text.dat 中的缓存文件中。应定期检查这些缓存文件以查找敏感数据。建议通过 设置 > 通用 > 重置 > 重置键盘字典 来重置键盘字典以清除缓存数据。

  • 拦截网络流量可以揭示自定义键盘是否远程传输按键。

防止文本字段缓存

UITextInputTraits 协议 提供了管理自动更正和安全文本输入的属性,对于防止敏感信息缓存至关重要。例如,通过禁用自动更正和启用安全文本输入,可以实现:

textObject.autocorrectionType = UITextAutocorrectionTypeNo;
textObject.secureTextEntry = YES;

另外,开发人员应确保文本字段,特别是用于输入诸如密码和个人识别码(PIN)等敏感信息的字段,通过将 autocorrectionType 设置为 UITextAutocorrectionTypeNo 并将 secureTextEntry 设置为 YES 来禁用缓存。

UITextField *textField = [[UITextField alloc] initWithFrame:frame];
textField.autocorrectionType = UITextAutocorrectionTypeNo;

日志

调试代码通常涉及使用日志。存在风险,因为日志可能包含敏感信息。在iOS 6及更早版本中,日志对所有应用程序都是可访问的,存在敏感数据泄漏的风险。现在,应用程序被限制只能访问自己的日志

尽管存在这些限制,拥有解锁设备的物理访问权限的攻击者仍然可以通过将设备连接到计算机并读取日志来利用这一点。需要注意的是,即使在应用程序卸载后,日志仍然保留在磁盘上。

为了减轻风险,建议与应用程序进行全面交互,探索其所有功能和输入,以确保没有意外记录敏感信息。

在审查应用程序源代码以查找潜在泄漏时,查找使用关键字如NSLogNSAssertNSCAssertfprintf等内置函数的预定义自定义日志记录语句,以及任何提及LoggingLogfile的自定义实现。

监控系统日志

应用程序记录各种可能敏感的信息。要监控这些日志,可以使用工具和命令,如:

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 可以提供实时日志监控:

iPhone:~ root# socat - UNIX-CONNECT:/var/run/lockdown/syslog.sock

跟随命令观察日志活动,这对于诊断问题或识别日志中潜在数据泄漏非常宝贵。


使用 Trickest 可轻松构建并通过全球最先进的社区工具自动化工作流程。 立即获取访问权限:

备份

自动备份功能已集成到 iOS 中,通过 iTunes(在 macOS Catalina 之前)、Finder(从 macOS Catalina 开始)或 iCloud 可轻松创建设备数据副本。这些备份几乎包含所有设备数据,但不包括高度敏感的元素,如 Apple Pay 详细信息和 Touch ID 配置。

安全风险

备份中包含已安装的应用及其数据会引发潜在数据泄漏问题,备份修改可能会改变应用功能的风险。建议不要在明文中存储敏感信息,以减轻这些风险。

从备份中排除文件

Documents/Library/Application Support/ 中的文件会被默认备份。开发人员可以使用 NSURL setResourceValue:forKey:error:NSURLIsExcludedFromBackupKey 从备份中排除特定文件或目录。这一做法对于保护敏感数据不被包含在备份中至关重要。

漏洞测试

要评估应用的备份安全性,首先通过 Finder 创建一个备份,然后根据苹果官方文档中的指导找到备份位置。分析备份以查找可能影响应用行为的敏感数据或配置。

可以使用命令行工具或应用程序如 iMazing 查找敏感信息。对于加密备份,可以通过检查备份根目录中的 "Manifest.plist" 文件中的 "IsEncrypted" 键来确认是否加密。

<?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.pybackup_passwd.py,尽管可能需要调整以与最新的iTunes/Finder版本兼容。另一个访问受密码保护备份中文件的选择是iOSbackup工具

修改应用程序行为

通过备份修改来改变应用程序行为的示例可在Bither比特币钱包应用中找到,其中UI锁定PIN存储在net.bither.plist中的pin_code键下。从plist中移除此键并恢复备份将删除PIN要求,提供无限制访问。

敏感数据的内存测试总结

处理存储在应用程序内存中的敏感信息时,限制此数据的暴露时间至关重要。有两种主要方法来调查内存内容:创建内存转储实时分析内存。这两种方法都有各自的挑战,包括在转储过程或分析过程中可能错过关键数据的风险。

检索和分析内存转储

对于越狱和非越狱设备,诸如objectionFridump之类的工具允许转储应用程序的进程内存。一旦转储完成,分析这些数据需要使用各种工具,具体取决于您要搜索的信息的性质。

要从内存转储中提取字符串,可以使用stringsrabin2 -zz等命令:

# Extracting strings using strings command
$ strings memory > strings.txt

# Extracting strings using rabin2
$ rabin2 -ZZ memory > strings.txt

对于更详细的分析,包括搜索特定数据类型或模式,radare2 提供了广泛的搜索功能:

$ r2 <name_of_your_dump_file>
[0x00000000]> /?
...

运行时内存分析

r2frida 提供了一个强大的替代方案,可以实时检查应用程序的内存,而无需进行内存转储。该工具使得可以直接在运行应用程序的内存上执行搜索命令:

$ r2 frida://usb//<name_of_your_app>
[0x00000000]> /\ <search_command>

加密破解

密钥管理流程不当

一些开发人员会将敏感数据保存在本地存储中,并使用在代码中硬编码/可预测的密钥进行加密。这样做是不应该的,因为一些逆向工程可能会使攻击者提取机密信息。

使用不安全和/或已弃用的算法

开发人员不应该使用已弃用的算法来执行授权检查存储发送数据。一些这些算法包括:RC4,MD4,MD5,SHA1... 如果用于存储密码的哈希,应该使用抗暴力破解的哈希算法并加盐。

检查

要执行的主要检查包括查找代码中是否存在硬编码的密码/秘密信息,或者这些信息是否是可预测的,以及代码是否使用某种加密算法。

有趣的是,您可以使用objection自动监视一些加密****库,方法如下:

ios monitor crypt

有关iOS加密API和库的更多信息,请访问https://mobile-security.gitbook.io/mobile-security-testing-guide/ios-testing-guide/0x06e-testing-cryptography

本地身份验证

本地身份验证在通过加密方法保护远程端点的访问时发挥着至关重要的作用。关键在于,如果没有适当的实现,本地身份验证机制可能会被规避。

苹果的本地身份验证框架钥匙串为开发人员提供了强大的API,以便于用户身份验证对话框和安全处理秘密数据。安全区域为Touch ID安全指纹ID提供安全性,而Face ID依赖于面部识别,而不会泄露生物识别数据。

要集成Touch ID/Face ID,开发人员有两个API选择:

  • LocalAuthentication.framework 用于高级用户身份验证,无法访问生物识别数据。

  • Security.framework 用于较低级别的钥匙串服务访问,使用生物识别身份验证保护秘密数据。各种开源包装器使钥匙串访问更简单。

然而,LocalAuthentication.frameworkSecurity.framework都存在漏洞,因为它们主要返回布尔值,而不传输用于身份验证过程的数据,使其容易受到绕过的攻击(参见Don't touch me that way, by David Lindner et al)。

实现本地身份验证

为了提示用户进行身份验证,开发人员应该在**LAContext类中使用evaluatePolicy**方法,在以下选项之间进行选择:

  • deviceOwnerAuthentication:提示使用Touch ID或设备密码,如果两者都未启用,则失败。

  • deviceOwnerAuthenticationWithBiometrics:仅提示使用Touch ID。

从**evaluatePolicy**返回的布尔值表示成功的身份验证,突显了潜在的安全漏洞。

使用钥匙串进行本地身份验证

在iOS应用中实现本地身份验证涉及使用钥匙串API来安全存储诸如身份验证令牌之类的秘密数据。该过程确保数据只能由用户使用其设备密码或Touch ID等生物识别身份验证来访问。

钥匙串提供了设置带有SecAccessControl属性的项目的功能,该属性限制对项目的访问,直到用户通过Touch ID或设备密码成功进行身份验证。这一功能对于增强安全性至关重要。

以下是Swift和Objective-C中的代码示例,演示如何将字符串保存到钥匙串中并从中检索出来,利用这些安全功能。这些示例特别展示了如何设置访问控制以要求Touch ID身份验证,并确保数据仅在设置的设备上可访问,前提是配置了设备密码。

// 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
}

Objective-C

为什么要关注 Objective-C

Objective-C 是 iOS 应用程序的主要编程语言之一。虽然 Swift 已经成为了更流行的选择,但仍然有许多应用程序是使用 Objective-C 编写的。因此,了解 Objective-C 是进行 iOS 渗透测试的关键一步。

Objective-C 基础

在进行 iOS 渗透测试时,您可能会遇到 Objective-C 代码。以下是一些 Objective-C 的基础知识:

  • Objective-C 使用 @interface@implementation 关键字来定义类和实现。

  • 方法是通过在括号中指定参数来声明的,例如 - (void)methodName:(NSString *)parameterName;

  • Objective-C 使用方括号 [] 来调用方法,例如 [self methodName:parameter];

Objective-C 安全问题

在进行 iOS 渗透测试时,您需要了解 Objective-C 中常见的安全问题,例如:

  • 内存管理问题,如内存泄漏和野指针。

  • 输入验证不足可能导致的漏洞,如缓冲区溢出。

  • 未经身份验证的用户输入可能导致的安全漏洞,如 SQL 注入。

Objective-C 渗透测试工具

有一些工具可用于帮助您进行 Objective-C 代码的静态和动态分析,以发现潜在的安全问题。一些常用的工具包括:

  • Clang Static Analyzer:用于静态代码分析。

  • Infer:另一个用于静态代码分析的工具。

  • LLDB:用于动态调试和分析 Objective-C 代码。

了解 Objective-C 的基础知识和常见安全问题,以及掌握相关的渗透测试工具,将有助于您更好地评估 iOS 应用程序的安全性。

// 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
}

现在我们可以从钥匙串请求保存的项目。 钥匙串服务将向用户显示身份验证对话框,并根据是否提供了合适的指纹返回数据或nil。

// 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
}

Objective-C

Objective-C is the primary programming language used for iOS app development. When performing iOS pentesting, it is essential to understand Objective-C code to identify security vulnerabilities effectively.

Key Points:

  • Learn the basics of Objective-C syntax, classes, and methods.

  • Understand memory management in Objective-C.

  • Analyze how sensitive data is handled in Objective-C code.

  • Identify common security issues in Objective-C applications, such as insecure data storage, improper input validation, and lack of encryption.

Tools:

  • Xcode: Integrated development environment (IDE) for iOS app development.

  • Hopper Disassembler: Tool for static analysis of compiled code.

  • Cycript: Utility for runtime manipulation and dynamic analysis of iOS apps.

Resources:

// 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");
}

检测

应用程序中使用的框架还可以通过分析应用程序二进制文件的共享动态库列表来检测。可以使用 otool 来完成此操作:

$ otool -L <AppName>.app/<AppName>

如果应用程序中使用了 LocalAuthentication.framework,输出将包含以下两行内容(请记住 LocalAuthentication.framework 在内部使用 Security.framework):

/System/Library/Frameworks/LocalAuthentication.framework/LocalAuthentication
/System/Library/Frameworks/Security.framework/Security

如果使用Security.framework,只会显示第二个。

本地身份验证框架绕过

Objection

通过位于此 GitHub 页面Objection生物识别绕过,可以使用一种技术来绕过LocalAuthentication机制。 这种方法的核心是利用Frida来操纵evaluatePolicy函数,确保它始终产生True结果,而不考虑实际的身份验证成功与否。 这对于规避存在缺陷的生物识别身份验证流程特别有用。

要激活此绕过,需要使用以下命令:

...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 应用中使用 evaluatePolicy 的一个示例:

+(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方法的结果。它将回调的结果更改为始终指示成功。

// 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脚本并绕过生物识别身份验证,使用以下命令:

frida -U -f com.highaltitudehacks.DVIAswiftv2 --no-pause -l fingerprint-bypass-ios.js

通过IPC暴露敏感功能

通用链接

UIActivity共享

UIPasteboard

应用扩展

WebViews

序列化和编码

网络通信

重要的是检查是否发生未加密的通信,并且应用程序是否正确验证服务器的TLS证书。 要检查这类问题,可以使用像Burp这样的代理:

主机名检查

验证TLS证书的一个常见问题是检查证书是否由受信任的CA签名,但未检查证书的主机名是否为正在访问的主机名。 为了使用Burp检查此问题,在iPhone上信任Burp CA后,您可以为不同主机名使用Burp创建新证书并使用它。如果应用程序仍然正常工作,则存在漏洞。

证书固定

如果应用程序正确使用SSL Pinning,则应用程序仅在证书符合预期时才能正常工作。在测试应用程序时,这可能是一个问题,因为Burp将提供自己的证书。 为了绕过此保护,在越狱设备内部,您可以安装应用程序SSL Kill Switch或安装Burp Mobile Assistant

您还可以使用objectionios 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 但也有其他选项,如Sirenreact-native-appstore-version-checker这是一种危险的机制,可能会被恶意第三方SDK滥用,因此建议检查使用哪种方法进行自动更新(如果有)并进行测试。 您可以尝试下载应用程序的先前版本以进行此目的。

第三方

第三方SDK的一个重要挑战是对其功能的缺乏细粒度控制。开发人员面临选择:要么集成SDK并接受其所有功能,包括潜在的安全漏洞和隐私问题,要么完全放弃其优势。通常,开发人员无法自行修补这些SDK中的漏洞。此外,随着SDK在社区中获得信任,一些可能开始包含恶意软件。

第三方SDK提供的服务可能包括用户行为跟踪、广告显示或用户体验增强。然而,这带来了风险,因为开发人员可能并不完全了解这些库执行的代码,从而导致潜在的隐私和安全风险。限制与第三方服务共享的信息至必要内容,并确保不会泄露敏感数据是至关重要的。

第三方服务的实施通常有两种形式:独立库或完整SDK。为了保护用户隐私,与这些服务共享的任何数据都应匿名化,以防止泄露个人可识别信息(PII)。

要识别应用程序使用的库,可以使用**otool**命令。应该针对应用程序及其使用的每个共享库运行此工具,以发现其他库。

otool -L <application_path>

参考资料和更多资源

使用 Trickest 可以轻松构建和 自动化工作流程,使用世界上 最先进 的社区工具。 立即获取访问权限:

从零开始学习 AWS 黑客技术,成为专家 htARTE (HackTricks AWS Red Team Expert)!

支持 HackTricks 的其他方式:

最后更新于