hacktricks/mobile-pentesting/ios-pentesting
2023-09-03 18:16:18 +00:00
..
basic-ios-testing-operations.md Translated to Chinese 2023-08-03 19:12:22 +00:00
burp-configuration-for-ios.md Translated ['README.md', 'cryptography/certificates.md', 'forensics/basi 2023-09-03 18:16:18 +00:00
extracting-entitlements-from-compiled-application.md Translated to Chinese 2023-08-03 19:12:22 +00:00
frida-configuration-in-ios.md Translated ['backdoors/salseo.md', 'macos-hardening/macos-red-teaming/RE 2023-08-15 18:29:39 +00:00
ios-app-extensions.md Translated to Chinese 2023-08-03 19:12:22 +00:00
ios-basics.md Translated to Chinese 2023-08-03 19:12:22 +00:00
ios-custom-uri-handlers-deeplinks-custom-schemes.md Translated to Chinese 2023-08-03 19:12:22 +00:00
ios-hooking-with-objection.md Translated to Chinese 2023-08-03 19:12:22 +00:00
ios-protocol-handlers.md Translated to Chinese 2023-08-03 19:12:22 +00:00
ios-serialisation-and-encoding.md Translated to Chinese 2023-08-03 19:12:22 +00:00
ios-testing-environment.md Translated to Chinese 2023-08-03 19:12:22 +00:00
ios-uiactivity-sharing.md Translated to Chinese 2023-08-03 19:12:22 +00:00
ios-uipasteboard.md Translated to Chinese 2023-08-03 19:12:22 +00:00
ios-universal-links.md Translated to Chinese 2023-08-03 19:12:22 +00:00
ios-webviews.md Translated to Chinese 2023-08-03 19:12:22 +00:00
README.md Translated ['README.md', 'cryptography/certificates.md', 'forensics/basi 2023-09-03 18:16:18 +00:00

iOS渗透测试


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

{% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}

☁️ HackTricks云 ☁️ -🐦 推特 🐦 - 🎙️ Twitch 🎙️ - 🎥 YouTube 🎥

iOS基础知识

{% content-ref url="ios-basics.md" %} ios-basics.md {% endcontent-ref %}

测试环境

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

{% content-ref url="ios-testing-environment.md" %} ios-testing-environment.md {% endcontent-ref %}

初始分析

基本iOS测试操作

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

{% content-ref url="basic-ios-testing-operations.md" %} basic-ios-testing-operations.md {% endcontent-ref %}

{% hint style="info" %} 对于以下步骤,应该已经在设备上安装了应用程序,并且已经获得了应用程序的IPA文件
阅读基本iOS测试操作页面以了解如何执行此操作。 {% endhint %}

基本静态分析

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

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

  • PIE位置无关可执行文件:启用后,应用程序每次启动时都会加载到随机的内存地址,使得难以预测其初始内存地址。
otool -hv <app-binary> | grep PIE   # 应该包含PIE标志
  • 堆栈保护为了验证堆栈的完整性在调用函数之前在堆栈上放置一个“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命令获取连接的USB设备上当前已安装的所有应用程序-a)的信息(-i

$ 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

基本枚举和Hooking

学习如何枚举应用程序的组件以及如何使用objection轻松hook方法和类

{% content-ref url="ios-hooking-with-objection.md" %} ios-hooking-with-objection.md {% endcontent-ref %}

IPA结构

.ipa文件是压缩,因此您可以将扩展名更改为.zip并对其进行解压缩。一个完整的打包好待安装的应用程序通常被称为Bundle
解压缩后,您应该看到<NAME>.app,一个包含其他资源的压缩存档。

  • Info.plist:包含一些应用程序特定配置的文件。
  • _CodeSignature/ 包含一个plist文件其中包含包中所有文件的签名。
  • Assets.car:另一个压缩存档,包含资源(图标)。
  • Frameworks/ 包含应用程序的本机库,以.dylib或.framework文件的形式。
  • PlugIns/ 可能包含应用程序扩展,以.appex文件的形式在示例中未出现
  • Core Data用于保存应用程序的离线永久数据缓存临时数据并在单个设备上为应用程序添加撤消功能。为了在单个iCloud帐户上跨多个设备同步数据Core Data会自动将模式镜像到CloudKit容器中。
  • PkgInfoPkgInfo文件是指定应用程序或Bundle的类型和创建者代码的另一种方式。
  • en.lproj, fr.proj, Base.lproj:是包含特定语言资源的语言包,以及在不支持某种语言时的默认资源。

在iOS应用程序中有多种定义UI的方式storyboard, nibxib 文件。

Info.plist

信息属性列表或Info.plist是iOS应用程序的主要信息来源。它由一个结构化文件组成其中包含描述应用程序的基本配置信息的键值对。实际上,所有打包的可执行文件(应用程序扩展、框架和应用程序)都应该有一个Info.plist文件。您可以在Apple开发者文档中找到所有可能的键。

该文件可能以XML或二进制bplist格式进行格式化。您可以使用一个简单的命令将其转换为XML格式:

  • 在macOS上使用plutil这是一个与macOS 10.2及以上版本一起自带的工具(目前没有官方在线文档):
$ plutil -convert xml1 Info.plist
  • 在Linux上
$ apt install libplist-utils
$ plistutil -i Info.plist -o Info_xml.plist

下面是一些信息及其在Info.plist文件中对应的关键字的非详尽列表,您可以通过检查文件或使用grep -i <keyword> Info.plist来轻松搜索Info.plist文件中的这些关键字:

  • 应用程序权限目的字符串:UsageDescription
  • 自定义URL schemesCFBundleURLTypes
  • 导出/导入的自定义文档类型:UTExportedTypeDeclarations / UTImportedTypeDeclarations
  • 应用程序传输安全ATS配置NSAppTransportSecurity

请参考相关章节,了解如何测试这些点的更多信息。

数据路径

在iOS上系统应用程序可以在/Applications目录中找到,而用户安装的应用程序可以在**/private/var/containers/下找到。然而,仅通过浏览文件系统来找到正确的文件夹并不是一项简单的任务,因为每个应用程序都会被分配一个随机的128位UUID**(通用唯一标识符)作为其目录名称。

为了轻松获取用户安装的应用程序的安装目录信息,您可以使用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

正如您所见,应用程序有两个主要位置:

  • 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 中看到的应用程序包,它包含了必要的应用程序数据、静态内容以及应用程序的编译二进制文件。
  • 此目录对用户可见,但用户无法写入其中
  • 此目录中的内容不会备份
  • 此文件夹的内容用于验证代码签名

Data 目录:

  • Documents/
  • 包含所有用户生成的数据。应用程序最终用户启动了此数据的创建。
  • 对用户可见,用户可以写入其中
  • 此目录中的内容会备份
  • 应用程序可以通过设置 NSURLIsExcludedFromBackupKey 来禁用路径。
  • Library/
  • 包含所有非用户特定的文件,例如缓存首选项Cookie和属性列表plist配置文件。
  • iOS 应用程序通常使用 Application SupportCaches 子目录,但应用程序可以创建自定义子目录。
  • Library/Caches/
  • 包含半持久性缓存文件
  • 对用户不可见,用户无法写入其中
  • 此目录中的内容不会备份
  • 当应用程序未运行且存储空间不足时,操作系统可能会自动删除此目录的文件。
  • Library/Application Support/
  • 包含运行应用程序所需的持久性文件
  • 对用户不可见,用户无法写入其中。
  • 此目录中的内容会备份
  • 应用程序可以通过设置 NSURLIsExcludedFromBackupKey 来禁用路径。
  • Library/Preferences/
  • 用于存储可以在应用程序重新启动后仍然存在的属性。
  • 信息以未加密的方式保存在应用程序沙盒中的名为 [BUNDLE_ID].plist 的 plist 文件中。
  • 使用 NSUserDefaults 存储的所有键/值对都可以在此文件中找到。
  • tmp/
  • 使用此目录来写入不需要在应用程序启动之间保留的临时文件。
  • 包含非持久性缓存文件。
  • 对用户不可见
  • 此目录中的内容不会备份。
  • 当应用程序未运行且存储空间不足时,操作系统可能会自动删除此目录的文件。

让我们仔细查看 Bundle 目录 (/var/containers/Bundle/Application/3ADAF47D-A734-49FA-B274-FBCA66589E67/iGoat-Swift.app) 中 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轻松构建和自动化由全球最先进的社区工具提供支持的工作流程。
立即获取访问权限:

{% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}

数据存储

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

{% content-ref url="ios-basics.md" %} ios-basics.md {% endcontent-ref %}

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

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上
$ apt install libplist-utils
$ plistutil -i Info.plist -o Info_xml.plist
  • 在objection的会话中
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是一个用于管理应用程序中对象的模型层的框架。Core Data可以使用SQLite作为其持久存储,但框架本身不是一个数据库。
CoreData默认不加密其数据。但是可以向CoreData添加额外的加密层。有关更多详细信息请参阅GitHub Repo

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

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

{% code title="来自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");
}
}

{% endcode %}

YapDatabase

YapDatabase是建立在SQLite之上的键值存储。
由于Yap数据库是SQLite数据库您可以使用前一节中提到的命令找到它们。

其他SQLite数据库

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

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 {% endcontent-ref %}

Realm数据库

Realm Objective-CRealm Swift不是由Apple提供的但仍值得注意。除非配置启用了加密否则它们会将所有内容存储为未加密的。

您可以在/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数据库引擎可以进行同步。它可以原生编译为iOS和macOS。

检查可能存在的Couchbase数据库的路径为/private/var/mobile/Containers/Data/Application/{APPID}/Library/Application Support/

Cookies

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

要检查cookie文件您可以使用此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. 建议在注销后删除缓存的响应。可以使用Apple提供的名为removeAllCachedResponses的方法来完成此操作。可以按以下方式调用此方法:

URLCache.shared.removeAllCachedResponses()

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

Apple文档

临时会话配置对象类似于默认会话配置请参阅default但相应的会话对象不会将缓存、凭据存储或任何会话相关数据存储到磁盘上。相反会话相关数据存储在RAM中。只有在要求将URL的内容写入文件时临时会话才会将数据写入磁盘。 3. 可以通过将缓存策略设置为.notAllowed来禁用缓存。它将禁止以任何方式存储缓存,无论是在内存中还是在磁盘上。

快照

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

除非iPhone已越狱否则攻击者需要访问****未锁定设备才能查看这些屏幕截图。默认情况下,最后一个快照存储在应用程序的沙盒中的Library/Caches/Snapshots/Library/SplashBoard/Snapshots文件夹中受信任的计算机无法从iOS 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是一种面向对象的编程语言用于开发iOS应用程序。它是iOS平台上最常用的编程语言之一。Objective-C是C语言的超集具有面向对象的特性如封装、继承和多态。在iOS应用程序的渗透测试中了解Objective-C语言是非常重要的因为它是iOS应用程序的主要开发语言之一。

@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

可以使用Keychain-Dumper等工具来转储钥匙串(设备必须越狱)。
您还可以使用Objection中的ios keychain dump命令。

NSURLCredential

NSURLCredential是在钥匙串中存储用户名和密码的完美类。不需要使用NSUserDefaults或任何钥匙串包装器。
一旦用户登录,您可以将他的用户名和密码存储到钥匙串中:

NSURLCredential *credential;

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

您可以使用Objectionios nsurlcredentialstorage dump命令来导出这些秘密信息。

自定义键盘/键盘缓存

从iOS 8.0开始Apple允许安装自定义扩展如自定义键盘。
安装的键盘可以通过设置 > 通用 > 键盘 > 键盘进行管理。
自定义键盘可以用于嗅探按键并将其发送到攻击者的服务器。但是,请注意需要网络连接的自定义键盘会通知用户
此外,用户可以切换到其他(更可信任的)键盘以输入凭据。

此外,应用程序可以阻止用户在应用程序中使用自定义键盘(或者至少在应用程序的敏感部分中阻止使用)。

{% hint style="warning" %} 如果您认为用户不需要第三方键盘,则建议不要允许其使用。 {% endhint %}

请注意,由于自动更正和自动建议,如果属性securetTextEntry未设置为trueautoCorrectionType未设置为UITextAutoCorrectionTypeNo默认的iOS键盘将捕获和存储每个非标准单词的缓存文件。

默认情况下,键盘将此缓存存储在应用程序沙盒中的Library/Keyboard/{locale}-dynamic-text.dat文件或/private/var/mobile/Library/Keyboard/dynamic-text.dat文件中。但是,它也可能将数据保存在其他位置。
可以在_设置_ > 通用 > 重置 > _重置键盘字典_中重置缓存。

{% hint style="info" %} 因此,始终检查这些文件并搜索可能的敏感信息
拦截网络流量是检查自定义键盘是否将按键发送到远程服务器的另一种方法。 {% endhint %}

键盘缓存使用了UITextInputTraits协议。UITextField、UITextView和UISearchBar类自动支持此协议并提供以下属性

  • var autocorrectionType: UITextAutocorrectionType确定在输入时是否启用自动更正。当启用自动更正时,文本对象会跟踪未知单词并建议合适的替换,除非用户覆盖替换,否则会自动替换输入的文本。此属性的默认值为UITextAutocorrectionTypeDefault,对于大多数输入方法都启用了自动更正。
  • var secureTextEntry: BOOL确定是否禁用文本复制和文本缓存,并隐藏正在输入的文本(适用于UITextField)。此属性的默认值为NO

要在代码中识别此行为:

  • 在源代码中搜索类似的实现,例如
textObject.autocorrectionType = UITextAutocorrectionTypeNo;
textObject.secureTextEntry = YES;
  • 在Xcode的“Interface Builder”中打开xib和storyboard文件并在适当的对象的“Attributes Inspector”中验证“Secure Text Entry”和“Correction”的状态。

应用程序必须防止敏感信息输入到文本字段中进行缓存。您可以通过在所需的UITextFields、UITextViews和UISearchBars中使用textObject.autocorrectionType = UITextAutocorrectionTypeNo指令来以编程方式禁用缓存。对于应该被屏蔽的数据例如PIN码和密码请将textObject.secureTextEntry设置为YES

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

日志

调试代码最常见的方法是使用日志记录,而应用程序可能会在日志中打印敏感信息
在iOS 6及以下版本中日志是可读的恶意应用程序可以读取其他应用程序的日志并从中提取敏感信息现在,应用程序只能访问自己的日志

然而,攻击者如果能够物理接触到一个解锁的设备,可以将其连接到计算机并读取日志(请注意,应用程序写入磁盘的日志在卸载应用程序时不会被删除)。

建议浏览应用程序的所有屏幕,与每个UI元素进行交互,并在所有文本字段中提供输入文本,并查看日志以寻找暴露的敏感信息

使用以下关键字检查应用程序的源代码以查找预定义和自定义的日志记录语句:

  • 预定义和内置函数:
  • NSLog
  • NSAssert
  • NSCAssert
  • fprintf
  • 自定义函数:
  • Logging
  • Logfile

监控系统日志

许多应用程序将信息性(和潜在敏感的)消息记录到控制台日志中。日志还包含崩溃报告和其他有用信息。

您可以使用以下工具:

idevice_id --list   # To find the device ID
idevicesyslog -u <id> (| grep <app>)   # 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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1

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] <Notice>: MS:Notice: Injecting: (null) [chmod] (1556.00)
Jun  7 13:42:14 iPhone readlink[9706] <Notice>: MS:Notice: Injecting: (null) [readlink] (1556.00)
Jun  7 13:42:14 iPhone rm[9707] <Notice>: MS:Notice: Injecting: (null) [rm] (1556.00)
Jun  7 13:42:14 iPhone touch[9708] <Notice>: MS:Notice: Injecting: (null) [touch] (1556.00)
...


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

{% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}

备份

iOS包括自动备份功能可以创建设备上存储的数据的副本。您可以使用iTunesmacOS Catalina之前或Finder从macOS Catalina开始从主机计算机创建iOS备份或者通过iCloud备份功能进行备份。在这两种情况下备份包括存储在iOS设备上的几乎所有数据但不包括高度敏感的数据如Apple Pay信息和Touch ID设置。

由于iOS备份已安装的应用程序及其数据一个明显的问题是应用程序存储的敏感用户数据是否可能通过备份无意中泄漏。另一个问题虽然不太明显但同样重要的是恢复修改后的备份后是否可以篡改用于保护数据或限制应用程序功能的敏感配置设置以更改应用程序行为。这两个问题都是合理的并且这些漏洞已经在大量应用程序中得到证实。

已安装移动应用程序的设备的备份将包括所有子目录(除了Library/Caches/)和应用程序的私有目录中的文件。因此,请避免在应用程序的私有目录或子目录中的任何文件或文件夹中以明文形式存储敏感数据。

尽管默认情况下始终备份Documents/Library/Application Support/中的所有文件,但您可以通过使用NSURL setResourceValue:forKey:error:NSURLIsExcludedFromBackupKey键来排除备份中的文件。您可以使用NSURLIsExcludedFromBackupKeyCFURLIsExcludedFromBackupKey文件系统属性来排除文件和目录的备份。

{% hint style="warning" %} 因此,在检查应用程序的备份时,您应该检查是否可以访问任何敏感信息,以及是否可以通过修改备份的某些设置来修改应用程序的敏感行为并恢复备份。 {% endhint %}

测试方法

首先通过创建设备的备份可以使用Finder并找到备份存储位置来开始。官方的Apple文档将帮助您找到iPhone、iPad和iPod touch的备份

找到设备的备份后(/Users/carlos.martin/Library/Application Support/MobileSync/Backup/{deviceID}您可以使用grep等工具查找敏感信息或者使用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轻松读取和提取密码加密的iOS备份中的文件。

如何修改行为

在开源的比特币钱包应用程序Bither您可以看到可以配置一个PIN码来锁定用户界面。
这个PIN码存储在备份中的文件net.bither.plist中的pin_code键中。
如果您从备份中清除该plist文件中的此键并恢复备份您将能够访问钱包。

测试内存中的敏感数据

敏感信息最终会存储在内存中。目标是确保这些信息尽可能短暂地暴露。

要调查应用程序的内存,首先创建一个内存转储。或者,您可以使用调试器等工具实时分析内存。无论使用哪种方法,这是一个非常容易出错的过程,因为转储提供了执行函数留下的数据,您可能会错过执行关键步骤。此外,除非您知道要查找的数据的足迹(无论是确切的值还是格式),否则在分析过程中忽略数据是非常容易的。例如,如果应用程序根据随机生成的对称密钥进行加密,除非您通过其他方式找到其值,否则在内存中很难找到该密钥。

获取和分析内存转储

无论您使用越狱设备还是非越狱设备,都可以使用objectionFridump来转储应用程序的进程内存。

在内存转储完成后(例如转储到名为"memory"的文件中),根据您要查找的数据的性质,您将需要一组不同的工具来处理和分析该内存转储。例如,如果您专注于字符串,执行stringsrabin2 -zz命令提取这些字符串可能就足够了。

# using strings
$ strings memory > strings.txt

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

在您喜欢的编辑器中打开strings.txt文件,并深入其中以识别敏感信息。

但是如果您想检查其他类型的数据最好使用radare2及其搜索功能。有关更多信息和选项列表请参阅radare2的搜索命令/?)的帮助。以下仅显示其中的一部分选项:

$ r2 <name_of_your_dump_file>

[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您可以在应用程序运行时分析和检查内存而无需转储内存。例如您可以运行之前在r2frida中的搜索命令并在内存中搜索字符串、十六进制值等。在这样做时请记住在使用r2 frida://usb//<name_of_your_app>启动会话后在搜索命令和任何其他r2frida特定命令之前使用反斜杠\

破解密码学

弱密钥管理流程

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

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

开发人员不应使用已弃用的算法来执行授权检查存储发送数据。其中一些算法包括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供开发人员将身份验证对话框扩展到用户。在连接到远程服务的情况下可以并且建议利用keychain来实现本地身份验证。

指纹ID传感器由SecureEnclave安全协处理器操作并且不会将指纹数据暴露给系统的其他部分。除了Touch ID之外Apple还引入了基于面部识别的_Face ID_它允许基于面部识别进行身份验证。

开发人员有两种选项来整合Touch ID/Face ID身份验证

  • LocalAuthentication.framework是一个高级API可用于通过Touch ID对用户进行身份验证。应用程序无法访问与已注册指纹相关联的任何数据只会收到身份验证是否成功的通知。
  • Security.framework是一个较低级别的API用于访问keychain服务。如果您的应用程序需要使用生物识别身份验证来保护某些机密数据,这是一个安全的选项,因为访问控制是在系统级别上进行管理的,不容易被绕过。Security.framework具有C API但也有几个开源包装器可用使得访问keychain就像访问NSUserDefaults一样简单。

{% hint style="danger" %} 请注意,使用LocalAuthentication.frameworkSecurity.framework都可以被攻击者绕过,因为它们只返回一个布尔值,而没有数据可供继续使用。有关更多详细信息,请参见Don't touch me that way, by David Lindner et al。 {% 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" %}

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

{% tab title="Objective-C" %}

这个目录包含了关于iOS应用程序渗透测试的技术和工具。

目录

贡献

如果你对iOS应用程序渗透测试有任何建议或改进意见欢迎贡献。请查看贡献指南了解更多信息。

许可证

本项目使用MIT许可证

{% endtab %}

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

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

{% tab title="Objective-C" %}

这个目录包含了关于iOS应用程序渗透测试的技术和工具。

目录

贡献

如果你对iOS应用程序渗透测试有任何建议或改进意见欢迎贡献。请查看贡献指南了解更多信息。

许可证

本项目使用MIT许可证

{% endtab %}

// 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 来完成此操作:

$ 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

Objection生物识别绕过可以用于绕过LocalAuthentication。Objection使用Frida来对evaluatePolicy函数进行注入,使其返回True,即使身份验证未成功执行。使用ios ui biometrics_bypass命令来绕过不安全的生物识别身份验证。Objection将注册一个任务用于替换evaluatePolicy的结果。它适用于Swift和Objective-C实现。

...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

以下是从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检查。如你在上面粘贴的代码片段中所见,evaluatePolicy使用一个回调函数来确定结果。因此,实现这个黑客技巧的最简单方法是拦截该回调函数,并确保它始终返回success=1

// 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暴露敏感功能

{% content-ref url="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 {% endcontent-ref %}

UIActivity共享

{% content-ref url="ios-uiactivity-sharing.md" %} ios-uiactivity-sharing.md {% endcontent-ref %}

UIPasteboard

{% content-ref url="ios-uipasteboard.md" %} ios-uipasteboard.md {% endcontent-ref %}

应用扩展

{% content-ref url="ios-app-extensions.md" %} ios-app-extensions.md {% endcontent-ref %}

WebViews

{% content-ref url="ios-webviews.md" %} ios-webviews.md {% endcontent-ref %}

序列化和编码

{% content-ref url="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 {% endcontent-ref %}

主机名检查

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

证书固定

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

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

第三方

第三方SDK的一个问题是对SDK提供的功能没有细粒度的控制。您可以使用SDK并拥有所有功能包括诊断泄漏和不安全的HTTP连接或者不使用它。此外通常应用程序开发人员无法修补SDK上的漏洞
此外一些SDK一旦被社区非常信任就会开始包含恶意软件

此外,这些服务提供的功能可能涉及跟踪服务以监控用户在使用应用程序时的行为,销售横幅广告或改善用户体验。第三方服务的缺点是开发人员不知道通过第三方库执行的代码的详细信息。因此,只应发送必要的信息给服务,并且不应披露敏感信息。

缺点是开发人员不知道通过第三方库执行的代码的详细信息,因此放弃了可见性。因此,应确保向服务发送的信息不超过所需的信息,并且不会泄露敏感信息。

大多数第三方服务有两种实现方式:

  • 使用独立库
  • 使用完整的SDK

发送给第三方服务的所有数据都应匿名化以防止暴露可能使第三方能够识别用户帐户的PII个人可识别信息

您可以通过对应用程序运行**otool来找到应用程序使用的库**(并对每个共享库运行它以找到更多使用的共享库)。

参考资料

更多信息


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

{% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥