iOS搜索派对深度解析:FindMy网络取证与加密数据库分析

本文深入分析iOS搜索派对(searchpartyd)机制,探讨FindMy兼容设备的追踪数据存储方式,包括加密的Observations.db数据库结构、解密方法、数据保留特性,以及与Android设备追踪的对比研究。

iOS正在观察。也在判断。

注意:本文假设您已阅读之前关于AirTags的文章,因为它引用了与iOS searchpartyd相关的项目,但几乎没有任何解释。强烈建议您在继续阅读之前先阅读之前的文章。

在过去的几个月里,我一直在探索iOS的搜索派对。正是这个东西让苹果的FindMy网络运转起来,理解其底层原理以及哪些数据可用于取证需要一些时间。主要的障碍是加密,我对此感到矛盾。作为用户,我欣赏数据保护。作为从业者,我不欣赏数据保护。去年在准备SANS演示时,我纠结了一段时间,最终才弄清楚如何获取不需要的AirTags的信息。

事实证明,searchpartyd还有其他可用数据,这与Android处理蓝牙追踪器检测的方式一致。为了测试,我使用了运行iOS 17.5.1的iPhone 14和运行iOS 18.3的iPhone 11,后者是对去年研究的更新。请注意,本文描述的文件仅在iOS 16.x及更高版本中看到。

加密。再次。

在研究Wild AirTags文章时,我发现自己想知道iOS是如何跟踪它在野外看到的FindMy兼容设备的。毕竟,苹果在谈论该功能时喜欢吹嘘网络上兼容设备的数量。显然,iOS必须将数据存储在某个地方,我认为这可能类似于Android处理恶意设备的方式。在同一父目录(/private/var/mobile/Library/com.apple.icloud.searchpartyd)中有几个数据库看起来是可行的候选者,但我发现这些文件当然是加密的。其中一个候选者是名为Observations.db的SQLite数据库。参见图1。

图1. com.apple.icloud.searchpartyd中的Observations.db。

在十六进制中查看此文件揭示了一些有趣的信息。参见图2。

图2. 十六进制格式的Observations.db。

乍一看,这个文件看起来是加密的,确实如此;然而,这里有一个非常微妙的线索,我最初忽略了,直到我开始查看父searchpartyd目录中的其他数据库。这个线索,在图2的红框中高亮显示,是字节16-23没有加密。这些字节是SQLite头的一部分,并提供了一些关于数据库的信息。它们代表以下内容(读作大端序):

字节
16 – 17 页面大小(字节)
18 格式写入版本(1 = 传统,2 = WAL)
19 格式读取版本(1 = 传统,2 = WAL)
20 页面末尾的保留字节(填充)
21 最大嵌入式负载(必须为64)
22 最小嵌入式负载(必须为32)
23 叶子负载分数(必须为32)

检查同一目录路径中的其他数据库发现,这种行为(和字节值)在所有数据库中都是相同的。我观察到每个数据库的以下值:

字节
16 – 17 每页4096字节 (x1000)
18 WAL写入版本 (x02)
19 WAL读取版本 (x02)
20 12字节填充 (x0C)
21 64字节最大负载 (x40)
22 32字节最小负载 (x20)
23 32字节叶子负载分数 (x20)

多个文件的字节相同,排除了SQL Cipher作为加密方法。事实证明,Observations.db(以及其他文件)是使用SQLite加密扩展(SEE)加密的。SEE规范声明这些字节作为方法的一部分不被加密。此外,该方法在RC4、AES-128(OFB或CCM模式)或OFB模式下的AES-256中运行,填充为12字节;在这种情况下,填充是数据库中每个页面的初始化向量(IV或nonce)。知道了这些信息,我前往钥匙串查看可能与searchpartyd相关的其他凭据。参见图3。

图3. 按searchpartyd过滤的钥匙串。

图3显示了来自iPhone 14的钥匙串,过滤了与searchpartyd相关的项目(红框中高亮显示)。令人惊讶的是,我在searchpartyd目录中看到的每个数据库(见图1)都有它们自己对应的钥匙串条目,名称完全一致。Observations.db的密钥长度为32字节,这意味着可能正在使用AES-256 OFB风格的SEE。有了密钥、页面大小和填充,我开始尝试解密数据。虽然我不是它的最大粉丝,但我承认ChatGPT在这里实际上派上了用场,在最终呈现一个可工作的数据库之前尝试了不同的东西。我过去不(现在仍然不)信任它,所以我寻求了Ian Whiffin的帮助,他发现了一些关于-WAL文件加密的额外问题,花了一些时间才解决。不过,最终的结果是,有了解密的数据库及其关联的-WAL文件。

观察

Observations.db的行为确实如我所料:它正在跟踪它看到的所有FindMy兼容设备(稍后详述其外观)。与它的Android对应物不同,数据分散在三个表中:ObservedAdvertisement、ObservedAdvertisementBeaconInfo和ObservedAdvertisementLocation。为了帮助理解,我创建了一个快速的SQLite查询来汇总数据:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
SELECT
ObservedAdvertisement.scanDate AS "Seen_Time",
ObservedAdvertisementLocation.latitude AS "Latitude",
ObservedAdvertisementLocation.longitude AS "Longitude",
ObservedAdvertisementBeaconInfo.beaconIdentifier AS "Identifier",
ObservedAdvertisement.macAddress AS "MAC_Address",
ObservedAdvertisement.rssi AS "Signal_Strength",
ObservedAdvertisement.advertisementData AS "Advertised_Data"
FROM
ObservedAdvertisementLocation
LEFT JOIN ObservedAdvertisement ON ObservedAdvertisement.advId = ObservedAdvertisementLocation.advId
LEFT JOIN ObservedAdvertisementBeaconInfo ON ObservedAdvertisementBeaconInfo.advId = ObservedAdvertisementLocation.advId
ORDER BY "Seen_Time"

参见图4了解执行后的查询。

图4. 执行后的SQL查询。

与Android一样,iOS记录了它看到FindMy兼容设备的时间以及检测到它们时手机的位置。iOS在位置数据来源方面并不缺乏,但这是另一个可靠的来源(这些纬度和经度值非常准确)。还有一个信号强度(RSSI)值,有助于理解与被检查设备的接近程度。该值越接近0,信号越强,因此,理论上,设备离手机越近;然而,重要的是要记住,环境条件如结构、地形、人员和电磁干扰都可能影响RSSI值。

查询还显示了信标标识符。由手机上登录的iCloud账户拥有或与该账户共享的信标(读作任何参与FindMy网络的东西)将有一个标识符。关于这些UUID的信息可以在~/BeaconNamingRecords/~/SharedBeacons中的plist文件中找到。这些在我之前的AirTag文章中已经介绍过。图4中看到的每个条目都有一个UUID值,这表明看到的信标与手机上登录的iCloud账户相关联,包括拥有和共享的。

查询返回的最后两列涉及观察到的设备的MAC地址和相应的广播负载。由于在FindMy网络上运行的设备使用旋转的蓝牙MAC地址(根据规范),在信标与账户无关的情况下(即数据库记录中没有UUID),这些MAC地址基本上是无用的。然而,在不需要的跟踪情况下可能有应用。例如,如果相同的MAC地址在一段时间内(可能是24小时左右)持续出现在Observations.db记录中,但手机尚未将其识别为WildMode项目。

广播信息很有趣。首先,参见图5。

图5. 来自Advertised_Data(advertisementData)列的BLE广播数据。

Advertised_Data列中的数据应该包含某些信息,但我发现,至少对于AirTags和AirPods,只有部分数据。由苹果和谷歌编写的公共规范规定,广播其数据的项目应在其广播负载中提供以下信息:

字节:
0 – 5 MAC地址
6 – 8 标志TLV(可选)
9 – 12 服务数据TLV(可选)
13 网络ID
14 靠近所有者位(LSB + 7个保留位 (1=是, 0=否)
15 – 36 专有负载(可选)

关于此规范和我观察到的负载,有三点需要注意。首先,此规范仍处于草案阶段,因此可能不完整。例如,据我所知,网络ID尚未在IANA注册,因此,目前很难判断设备属于哪个网络(谷歌或苹果)。我在上述字节13中没有发现任何一致性。其次,我还发现广播负载中的靠近所有者位在设备之间不一致。有时字节14中的最低有效位设置为"1",但它不是测试账户拥有的信标,测试手机会警告它是不需要的。这引发了关于任一家公司目前是否遵循规范的疑问。我完全预计这会改变,但要知道,就目前而言,事情似乎并不一致,这使得它们不可靠。

第三,“Advertised_Data"列中广播负载数据中的MAC地址与"MAC_Address"列中看到的MAC地址存在差异。参见图6。

图6. 图5中看到的信标的MAC地址。

注意,图6中第一个字节的第一个半字节与图5中看到的不同(87对比C7)。这是由于网络上运行的设备使用的随机化方式。蓝牙规范详细说明了(参见规范第1.3.2节),但要知道,根据苹果-谷歌规范,两个最高位应以某种方式切换,成为私有的、不可解析的MAC地址;然而,这并不是我观察到的(再次强调,规范是草案)。在上面的例子中,第二个最高位从1变为0,导致MAC地址中的第一个八位字节从C7(0b11 – 静态地址)变为87(0b10 – 保留供私人使用)。

图7-1和7-2显示了位变化如何影响第一个八位字节。

图7-1. 以C7开头的静态地址。

图7-2. 以87开头的“保留供将来使用”地址。

为了提供一个例子,我带着一个AirTag、iPhone 11和小米Poco X7 Pro(Android 15)外出。两个设备都看到了这个标签。图8显示了来自Observations.db的MAC地址,图9显示了来自小米的Android追踪器检测数据库personalsafety_db的MAC地址。

图8. 从Observations.db观察到的静态MAC地址。

图9. 从小米(personalsafety_db)观察到的MAC地址。

注意,小米设备似乎已经完成了从“保留供将来使用”地址到静态地址的转换,因为没有引用以8F开头的地址,这在iPhone观察到的负载中看到(见图10)。

图10. 从AirTag观察到的负载。

为了再次说明位变化,参见图11-1和11-2。

图11-1. 以CF开头的静态地址。

图11-2. 以8F开头的“保留供将来使用”地址。

关于Observations.db的三点说明。首先,我发现当设备静止或移动不多时,条目更频繁地写入此文件。在我开车期间,与手机在我家或其他我或手机移动不多的地方相比,它似乎观察频率较低。静止时,手机经常看到信标。例如,一个信标以两到四秒的间隔被观察到。其他信标被看到的频率更高!频率可能是手机观察环境的产物,但要知道它可能非常频繁。

第二点说明,这一点非常重要:这个数据库非常、非常快地丢弃和清理记录。例如,一个测试环境是离我家五分钟路程的当地餐厅。我带着我的一个“恶意”AirTags,在餐厅坐了一个多小时。我直接开车回家并运行提取。Observations.db完全没有任何关于餐厅AirTag的信息;然而,在我把车开进车库到我放置手机进入飞行模式以运行提取(在我家)之间,它看到了十次。另一个测试涉及AirTag和iPhone 14,当时我正在做一些纹身工作(大约三个小时)。在我离开纹身店之前,我将手机设置为飞行模式,确保蓝牙无线电关闭,然后开车回家。这次,Observations.db有大量记录条目,似乎是纹身店的AirTag(基于RSSI值),以及从我家到纹身店路线沿途偶尔记录的位置,纹身店离我家开车近40分钟。

我提到这两种情况是为了说明:如果您想从此数据库中获取不涉及您的实验室/工作位置的位置数据,必须在设备被扣押的地方(即犯罪现场)确保蓝牙无线电被切断。否则,Observations.db将丢弃和清理可能与您的检查目标相关的记录。这里的例外是与拥有/共享信标相关的记录。这些记录往往保留得更久。

最后,检查此文件时务必关联-WAL文件也非常重要。为了说明原因,参见图12和图13。

图12. 没有-WAL的Observations.db。只有264条记录…

图13. …有-WAL。多了四十条记录!

从-WAL中提取的记录数量可能很大,并且可能包含主数据库中不包含的数据,因此请确保您使用的任何工具都能处理-WAL条目。由于记录写入和删除的速度,检查带有-WAL的文件是必要的。

一个例子

为了说明Observations.db如何处理未知信标,我带着iPhone 11和一个AirTag去了一个人口密集的地点:纽约市的JFK机场。有这么多人(和i设备),iPhone应该有大量可观察的对象,这应该会使Observations.db非常繁忙。AirTag和iPhone 11在我背包的口袋里,实际上接触着,因此,理论上,RSSI信号应该相对较高,这可能有助于识别AirTag。事实证明,这些想法是正确的。参见图14。

图14. iPhone 11看到了很多日志,但有两个突出。

图14中红框高亮的值与Observations.db中的其余数据相比是异常值,因为它们的RSSI信号。RSSI值为-37和-38都是接近距离的良好指标。与这两个条目关联的广播负载是相同的。图15高亮了图14中-38 RSSI记录条目的广播负载。图15中还高亮了广播的MAC地址(字节0-5 – 蓝框)。

图15. 广播负载。

Observations.db中有多个具有此负载的记录,这表明AirTag在机场“跟随”我。最终,AirTag被识别为不需要的,这在searchpartyd目录下的~/WildModeAssociationRecord中触发了plist创建,但没有通知。AirTag被iPhone 11分配了标识符13BB48EF-F050-4013-A36B-309821C999ED。解密plist发现了以下信息。参见图16。

图16. 测试AirTag的WildModeAssociation记录。

加密plist文件中的广播数据(图16中的红框)与Observations.db中广播负载中看到的内容匹配。此外,负载中的MAC地址在plist中有自己的键/值对(图16中的蓝框高亮),并与广播负载中的内容匹配。此外,第一个位置条目(未高亮)将手机定位在JFK的4号航站楼,这正是我所在的位置。参见图17。

图17. WildModeAssociation记录中看到的第一个位置。

.plist文件的底部包含一些信息。即,给用户(我)的(可能最终的)通知状态,以及.plist文件创建之前最后看到的位置。参见图18。

图18. 观察状态和最后看到的位置。

上面,通知状态“staged”在红框中看到,以及最后看到的位置(未高亮)。映射的位置见图19。我静止在4号航站楼的B48登机口等待登机。

图19. 通知前最后看到的位置。我在B48登机口。

Observations.db中的位置略有不同,因为关联的时间戳晚于.plist文件中找到的时间戳;Observations.db记录发生在.plist文件中最后位置大约1小时20分钟后(21:29:21对比20:08:22)。图20显示了映射的位置。注意,位置出现在4号航站楼外,这是合理的,因为我在飞机上(就在起飞前)打开了iPhone 11的飞行模式。

图20. 在飞机上。

还有一个观察

虽然我能够查看父searchpartyd目录中的其他数据库,但除了一个之外,大多数都是空的,没有包含任何有趣的数据:ItemSharingKeys.db。这个数据库很有趣。它包含已与受检设备上的iCloud账户所有者共享的信标的广播数据,但这并不是它有趣的原因。它有趣之处在于,它似乎包含共享信标的当前和未来广播负载信息,包括一组私钥和蓝牙MAC地址。为了提取必要的信息,我稍微修改了SQL查询,添加了来自Observations.db的广告标识符。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
SELECT
ObservedAdvertisement.scanDate AS "Seen_Time",
ObservedAdvertisementLocation.latitude AS "Latitude",
ObservedAdvertisementLocation.longitude AS "Longitude",
ObservedAdvertisementLocation.advId AS "Advertisment_Identifier",
ObservedAdvertisementBeaconInfo.beaconIdentifier AS "Identifier",
ObservedAdvertisement.macAddress AS "MAC_Address",
ObservedAdvertisement.rssi AS "Signal_Strength",
ObservedAdvertisement.advertisementData AS "Advertised_Data",
ObservedAdvertisementBeaconInfo.sequence AS "Sequence_Number"
FROM
ObservedAdvertisementLocation
LEFT JOIN ObservedAdvertisement ON ObservedAdvertisement.advId = ObservedAdvertisementLocation.advId
LEFT JOIN ObservedAdvertisementBeaconInfo ON ObservedAdvertisementBeaconInfo.advId = ObservedAdvertisementLocation.advId
ORDER BY "Seen_Time"

此查询从ObservedAdvertisementLocation表拉回广告标识符。此列中的值与表ObservedAdvertisementBeaconInfo中的列索引相关,这是感兴趣的列。接下来,参见图21和图22。注意图21右窗格中共享标签的记录条目,以及广告ID和MAC地址。

图21. Observations.db中共享标签的记录条目,以及广告ID和MAC地址。

图22. Observations.db中表ObservedAdvertisementBeaconInfo中的广告ID和索引值。

ObservedAdvertisementBeaconInfo中的列索引(图22)与ItemSharingKeys.db中表NearOwnerKeys中同名的列相关。参见图23。

图23. 来自图21的广播MAC地址。

图23中有趣的发现是,AirTag的广播MAC地址列在nearOwnerAdvertisement列中。还有额外的索引号数字递增,每个都有它们自己的nearOwnerAdvertisment值,所有这些都似乎是MAC地址。为了进一步调查这一点,我等了几个小时,带着同一个“共享”AirTag,在车里转了一会儿,然后重新提取设备。图24显示了后来提取的Observations.db。

图24. 后来提取的记录条目。注意广告标识符。

查看表ObservedAdvertisementBeaconInfo,在索引列中找到值126603。参见图25。

图25. 来自后来提取的新索引值。

跳回第一次提取的ItemSharingKeys.db,查找126603,找到下面图26中看到的内容。

图26. nearOwnerAdvertisement中匹配的MAC地址。

广播的MAC地址匹配。本质上,与AirTag共享的手机正在为共享的AirTag保留一批广播MAC地址,很可能用于将来如果“看到”AirTag时使用。NearOwnerKeys表中有230条记录,因此,理论上,可能可以合理地预测共享AirTag的广播MAC地址(即,只要NearOwnerKeys表中有记录)。显然,这需要超出本文范围的额外研究,但能够预测地址序列的规范要求确实为这一理论提供了可信度。

到此为止。暂时。

这是一个很好的发现,因为它在一定程度上证实了我的想法,即iOS设备正在跟踪它们看到的所有FindMy兼容设备,很像Android那样。Observations.db为检查员提供了iOS上另一个位置数据来源,尽管保留时间还有很多不足之处。尽管如此,它是另一个来源,如果处理得当,可以被利用。

虽然在iOS 17和18之间事情似乎相当一致,但我完全预计一旦公共规范发布,事情会发生变化。当然,这并不排除今年晚些时候iOS 126到来时事情会发生变化。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计