深入观察——更多关于iOS“查找”网络
iOS正在观察。并且在进行判断。
注意:本文假设您已阅读之前一篇关于AirTag的文章(链接在此),因为它会引用与iOS searchpartyd相关的项目,且不作或仅作很少解释。强烈建议您先阅读前一篇文章。
在过去几个月里,我一直在探索iOS的“查找”网络。正是它使得苹果的“查找”网络得以运转,而我花了一些时间来理解其底层机制,以及哪些数据可能在取证中可用。主要的障碍是加密,对此我心情复杂。作为一名用户,我欣赏这种数据保护。作为一名取证从业者,我则不欣赏这种数据保护。去年在为SANS演讲做准备时,我纠结了一段时间,最终才弄明白如何获取关于非配对AirTag的信息(您可以阅读关于此努力的文章)。
事实证明,searchpartyd还提供了其他数据,这使其与Android处理蓝牙追踪器检测的方式相一致。为了测试,我使用了运行iOS 17.5.1的iPhone 14和运行iOS 18.3的iPhone 11,后者是对去年研究的更新。请注意,本文描述的文件仅在iOS 16.x及以上版本中观察到。
加密。又一次。
在研究“野生AirTag”文章时,我不禁想知道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查询来汇总数据:
|
|
参见图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列中的数据本应包含某些信息,但我发现,至少对于AirTag和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的三个注意事项。首先,我发现当设备静止或移动不多时,条目更频繁地写入此文件。在我开车期间,与手机在我家或其他我不怎么移动的地方相比,它似乎观察得没那么频繁。静止时,手机经常看到信标。例如,观察到一个信标的间隔从两秒到四秒不等。其他信标被看到的频率更高!这种频率可能是手机观察环境的产物,但要知道它可能非常频繁。
第二点,这一点非常重要:这个数据库非常、非常快地丢弃和清理记录。举个例子,其中一个测试环境是离家五分钟的一家当地餐馆。我带着一个“非配对”的AirTag,在餐馆里坐了一个多小时。我直接开车回家并运行了提取。Observations.db中完全没有任何关于餐馆里AirTag的信息;然而,从我开进车库到我将手机置于飞行模式以运行提取(在我家)之间,它确实看到了该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机场。由于有这么多人(和苹果设备),iPhone应该有大量可观察的对象,这应该会让Observations.db非常繁忙。AirTag和iPhone 11都在我的背包口袋里,并且实际上接触着,因此理论上RSSI信号应该相对较高,这可能有助于识别AirTag。事实证明,这些想法是正确的。参见图14。
图14. iPhone 11看到了大量记录,但有两个突出项。
由于它们的RSSI信号,图14中红框高亮的值与Observations.db中的其余数据相比是异常值。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. 观察到的状态和最后看到的位置。
上图红框中可以看到通知状态“已暂存”,以及最后一次看到的位置(未高亮)。映射位置见图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的广播标识符。
|
|
此查询从ObservedAdvertisementLocation表中提取广播标识符。此列中的值与ObservedAdvertisementBeaconInfo表中的列索引相关,该列正是我们感兴趣的列。请跟随查看图21和图22。注意图21右窗格中共享标签的高亮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条记录,因此理论上,可能在合理范围内(即,在NearOwnerKeys表中有记录的范围内)预测共享AirTag的广播MAC地址。显然,这需要超出本文范围的额外研究,但规范要求能够预测地址序列,这确实为这一理论提供了可信度。
到此为止。暂时如此。
这是一个不错的发现,因为它在一定程度上证实了我的想法,即iOS设备正在跟踪它们看到的所有FindMy兼容设备,很像Android所做的那样。Observations.db为iOS上的取证人员提供了又一个位置数据来源,尽管保留时间还有很大的提升空间。尽管如此,这是另一个来源,如果处理得当,可以被利用。
虽然在iOS 17和18之间情况似乎相当一致,但我完全预计一旦公共规范发布,情况将会改变。当然,这并不排除今年晚些时候iOS 126到来时情况发生变化。