使用 checkm8 和 Ramdisk 破解 iPad 锁屏密码
本文在 iPad mini 1 和 iOS 7 环境下测试。
在之前的文章利用 checkm8 漏洞访问 iPad 文件系统中,笔者记录了如何维修一台屏幕完全损坏的 iPad mini 1,并使用 Arduino、checkm8 和 SSH Ramdisk 访问其中的文件系统。当时已经可以把照片等文件复制出来,也可以通过修改 SpringBoard 配置解除锁屏密码的尝试次数限制,但要在屏幕上手工尝试最多 10000 组四位数字,仍然不太现实。
最近,笔者继续研究了旧款 iOS 设备的 Data Protection 机制,发现普通 SSH Ramdisk 不能直接用于密码破解,并制作出一套可以访问设备 UID 密钥的 iOS 6 Ramdisk,成功在设备端自动尝试锁屏密码。
iOS 如何使用锁屏密码保护数据
iOS 并不会直接保存锁屏密码。设备初始化时会生成一组用于保护不同类别文件的 Class Key,并把它们包装后保存在 System Keybag 中。这台设备的 Keybag 位于数据分区的/mnt2/keybags/systembag.kb。用户输入密码后,系统会将密码、Keybag 中的 Salt 以及设备内部独有的 UID 密钥一起送入密钥派生函数(KDF),计算出 Passcode Key,再用它解开 Keybag 中的 Class Key。只有这一整套过程成功,系统才能访问受到 Data Protection 保护的文件。
这也解释了为什么不能把 Keybag 复制到性能更高的电脑上离线破解。UID 密钥写在芯片内部,软件无法直接读出,只能请求设备的 AES 硬件使用它进行运算。因此,即使候选密码在 Mac 上生成,最关键的 KDF 仍然必须由这台 iPad 自己完成。
早期 iOS 安全研究项目 iphone-dataprotection 提供了一个名为bruteforce的设备端程序。它会读取 System Keybag,依次测试四位数字密码,并通过 AppleKeyStore 和硬件 AES 验证结果。找到正确密码后,程序还会生成 Passcode Key 和所有 Class Key,供后续解密数据使用。后文提到的bruteforce就是这个工具。
普通 SSH Ramdisk 为什么不够
checkm8 可以绕过安全启动限制,让设备运行自定义 iBSS、iBEC、内核和 Ramdisk。Legacy-iOS-Kit 提供的 SSH Ramdisk 已经足以挂载系统分区和数据分区,所以前一篇文章可以直接通过它访问文件。然而,普通 Ramdisk 的目标主要是文件维护和数据提取,并不一定修改内核对 UID 密钥的访问限制。
bruteforce提供两条密码测试路径:一条通过系统的 AppleKeyStore 服务验证,另一条在用户态自行执行 KDF 和 Keybag 解包。无论主循环使用哪一条路径找到候选,程序最后都会再次调用用户态实现,生成 Passcode Key 和 Class Key 并保存结果。
bruteforce每验证一个候选密码,都需要通过内核中的IOAESAccelerator调用 UID AES。原版内核只允许受信任的系统组件使用这一能力,普通用户态程序发起请求时会被拒绝。这种情况下是无法准确判断猜测的密码是否是正确的。
因此,真正可用的破解环境至少需要三个部分:能够在旧版 iOS 用户态运行的bruteforce、与该 Ramdisk 版本匹配的restored_external,以及允许用户态访问 UID AES 的补丁内核。
准备 iOS 6 破解 Ramdisk
这台 iPad 的硬件型号为iPad2,5,主板代号为p105ap,使用 A5 处理器,CPID 为0x8942,原系统则是 iOS 7.0.4。经过多次实验,笔者最终选择 iOS 6.1.3(10B329)的恢复 Ramdisk 作为基础。
构建过程中参考了 32bit-Bruteforce-Passcode。这个项目整理了iphone-dataprotection所需的 armv7 程序、不同 iOS 版本的内核补丁和 Ramdisk 构建脚本。笔者也单独编译了iphone-dataprotection中的源码,不过最终放进 Ramdisk 的是项目提供的旧 SDK 预编译版本,因为它与 iOS 6 用户态库的兼容性更好。
最终 Ramdisk 中的主要组件如下:
| 组件 | 用途 |
|---|---|
bruteforce | 读取 System Keybag 并在设备端测试四位数字密码 |
device_infos | 读取设备信息、分区信息以及 Data Protection 相关参数 |
iOS 6 版restored_external | 初始化恢复环境和 USB 服务,使 Ramdisk 可以稳定运行 |
SSH 和mount.sh | 通过 USB 登录设备,并挂载/mnt1和/mnt2 |
| 补丁 Kernelcache | 允许用户态工具使用 UID AES,并修复 A5 设备启动所需的兼容性问题 |
最关键的修改位于内核的IOAESAccelerator驱动中。补丁改变了 UID 使用权限检查,使 Ramdisk 里的bruteforce能够调用芯片内部的 UID 密钥。iOS 6 补丁集还包含 NAND 和 PPN 相关的兼容性修改,否则在这台设备上内核可能会在挂载存储时崩溃。修改后的 Kernelcache 仍然需要按照原来的 Img3 格式重新封装,iBSS 和 iBEC 也是如此;如果把解密后的裸 Mach-O 文件直接发送给设备,irecovery可能显示发送成功,但设备并不会执行它。
Ramdisk 的启动脚本只负责启动 SSH、运行 iOS 6 版restored_external并挂载分区,没有自动启动密码破解。这样即使启动链不稳定,也可以先通过 SSH 检查设备状态,确认所有条件满足后再手工运行bruteforce。
启动自定义 Ramdisk
首先仍然按照上一篇文章的方法,将 iPad 进入 DFU 模式并连接到运行checkm8-a5的 Arduino。Arduino 输出Done!后,设备已经进入 pwned DFU,此时把 Lightning 线从 Arduino 拔下并连接回 Mac。
A5 设备的启动过程比较特殊。iBSS 和 iBEC 是设备启动早期的两级引导程序,DeviceTree 描述主板上的硬件,Kernelcache 则包含 iOS 内核和驱动。笔者使用 Legacy-iOS-Kit 已经验证过的 12H321 pwnediBSS作为第一个过渡阶段,再发送与 iOS 6.1.3 Ramdisk 匹配的 iBEC、DeviceTree 和 Kernelcache。这里混用了 12H321 的 iBSS,它只负责把 Arduino 留下的 pwned DFU 状态过渡到可以由irecovery继续控制的阶段。
实际发送顺序如下,其中所有文件都来自提前准备好的 Ramdisk 目录:1
2
3
4
5
6
7
8
9
10
11
12
13python2 ./ipwndfu -l pwnediBSS
irecovery -f iBEC
irecovery -c go
irecovery -f Ramdisk.dmg
irecovery -c ramdisk
irecovery -f DeviceTree.dec
irecovery -c devicetree
irecovery -f Kernelcache.dec
irecovery -c bootx
bootx执行后,irecovery无法继续连接通常是正常现象,因为设备已经离开恢复模式并开始启动 Ramdisk。成功时,Mac 的 USB 设备列表中会出现 Product ID 为0x12ab的 iPad,序列号则显示为ramdisk tool,说明自定义内核和 Ramdisk 已经工作。
接下来使用iproxy把设备上的 SSH 端口转发到 Mac:1
2iproxy 6414 22
ssh -oHostKeyAlgorithms=+ssh-rsa -p 6414 [email protected]
Ramdisk 的 root 密码仍然是alpine。成功登录后,执行mount可以看到系统分区和数据分区分别挂载在/mnt1和/mnt2:1
2
3/dev/md0 on /
/dev/disk0s1s1 on /mnt1
/dev/disk0s1s2 on /mnt2
随后检查 System Keybag 和工具是否存在:1
2
3ls -l /mnt2/keybags/systembag.kb
ls -l /usr/bin/bruteforce /usr/bin/device_infos
/usr/bin/device_infos
device_infos能够正常输出设备型号、数据分区 UUID 和硬件密钥相关信息,System Keybag 也可以读取,说明破解所需的分区挂载和 UID AES 环境已经准备完成。
运行 bruteforce
确认环境无误后,直接在 SSH 会话中执行:1
/usr/bin/bruteforce -u
从iphone-dataprotection的源码来看,这里的-u参数实际表示使用用户态实现的破解路径。带-u时,程序调用bruteforceUserland,自己完成 Passcode Key 派生和 Keybag 验证;不带-u时,则通过系统的 AppleKeyStore 服务调用AppleKeyStoreUnlockDevice尝试密码。本文使用-u,是因为这个参数可以加快破解速度。无论是否使用-u参数,自定义内核都需要已经开放 UID AES,所有候选也仍然需要调用设备端的硬件 AES。程序启动后首先挂载数据分区、读取 Keybag 并判断密码键盘类型,然后从0000开始依次尝试:1
2
3
4
5
6
7Trying to mount data partition
Writing results to 3f9df781d6abf8aa.plist
keyboardType=0
0000
0001
0002
...
这台 iPad mini 1 的实测速度大约是每秒 7 至 8 个候选,完整扫描 10000 组四位数字需要二十多分钟。相比在正常系统界面上输入密码,这个过程不会等待锁屏倒计时,也不受失败次数限制,但速度仍然受到设备 KDF 设计的约束。
程序最终运行到9876时停止,并输出:1
2
3
4
5
6
7
8Found passcode : 9876
Keybag version : 4
Keybag keys : 10
Class Wrap Key
...
Passcode key : ...
Key 0x835 : ...
Writing results to 3f9df781d6abf8aa.plist
这次不仅出现了Found passcode,后面还成功列出了 10 个 Class Key、Passcode Key 和硬件密钥信息,程序以正常状态退出,没有出现e00002c1或Invalid passcode。因此可以确定9876就是正确密码。
程序把结果写入 Ramdisk 中的/var/root/3f9df781d6abf8aa.plist。这个文件包含passcode、passcodeKey、classKeys和KeyBagKeys等字段。更重要的是,/var/root位于临时 Ramdisk 中,设备重启后内容会消失,所以应当立刻通过 SCP 复制到 Mac:1
2
3scp -P 6414 \
-oHostKeyAlgorithms=+ssh-rsa \
[email protected]:/var/root/3f9df781d6abf8aa.plist ./
确认结果文件已经保存后,可以在 Ramdisk 中执行重启。重启后,这台 iPad 正常回到原来的 iOS 7.0.4,就可以使用恢复的锁屏密码解锁。
总结
这次实验中,通过 checkm8 漏洞启动了后续的专用 Ramdisk:它提供与旧版工具兼容的用户态环境,通过补丁内核开放 UID AES,再由bruteforce在设备端完成 KDF 和 Keybag 验证。
最终,程序在约三十分钟内找到了正确的四位数字密码,并成功生成 Passcode Key 和全部 Class Key。对数据恢复而言,后者和密码本身同样重要,因为它们可以用于处理 Data Protection 保护的文件和 Keychain 数据。实际操作时,应优先备份原始数据和结果 plist,尽量避免写入/mnt1、/mnt2,并在重启前确认临时 Ramdisk 中的结果已经复制到电脑。
参考项目: