本文档既包含对 macOS 上 ssh 二进制中一个逻辑漏洞的分析(我的第一个软件漏洞!),也包含演示 Apple 如何修复该漏洞的补丁分析。我于 2022 年底通过 Apple 安全赏金计划向 Apple 报告了该问题,它后来在 macOS Ventura 13.5 中修复,并产生了 CVE-2023-42829(🎉)。该问题导致保存在 macOS 用户本地"登录"钥匙串(com.apple.ssh.passphrases 访问组)中的 SSH 口令短语以明文形式暴露给本地攻击者。
免责声明:
本报告仅供教育目的,且已遵循负责任披露流程。本分析按原样提供,任何对本信息的进一步发布或使用都应遵守负责任披露准则。
已修补的高层流程
未修补的高层流程
| 硬件 | 操作系统软件 |
|---|---|
| MacBook Pro M1 2021 | /usr/bin/ssh @ macOS Ventura build 13.0.1 (22A400) |
以下概念验证展示了该漏洞相当简单的可利用性,只需向 ssh 二进制的 -I 参数传入一个动态库即可:
jamesd@local build % ssh -I /Users/jamesd/rome.dylib [email protected]
SSH Private Key Passphrase -> 'SuperSecret!!@@$'
下面来聊聊这是如何被发现的,以及为什么会发生这种情况!
从前,我在用 ssh 二进制做一些无害操作时,把 -i 参数(用于传入 SSH 身份文件)误打成了 -I 参数,然后看到了如下标准输出:
jamesd@local build % ssh -I /Users/jamesd/.ssh/id_rsa something@somewhere
dlopen /Users/jamesd/.ssh/id_rsa failed: dlopen(/Users/jamesd/.ssh/id_rsa, 0x0002): tried: '/Users/jamesd/.ssh/id_rsa' (not a mach-o file), ...
...
在检查了 ssh 二进制的授权之后……
jamesd@local build % ldid -e /usr/bin/ssh # dump the entitlements of the /usr/bin/ssh binary
<key>com.apple.private.security.clear-library-validation</key>
<true/>
<key>keychain-access-groups</key>
<array>
<string>com.apple.ssh.passphrases</string>
</array>
……这激起了我的兴趣——该二进制拥有从受保护的钥匙串访问组(com.apple.ssh.passphrases)读取数据的授权,具备 com.apple.private.security.clear-library-validation 授权,而且 ssh 正在尝试对我私钥执行 dlopen()?
事实证明,ssh 支持通过一种名为 pkcs11 的机制对远程系统进行身份验证,这是一种用于硬件安全模块(HSM)上执行加密操作的标准(https://docs.aws.amazon.com/cloudhsm/latest/userguide/pkcs11-library.html)。
就本文而言,我们无需关心 pkcs11 的具体细节,只需知道客户端会向 ssh -I 提供一个 pkcs11(动态库)库即可。
com.apple.private.security.clear-library-validation 授权虽然概念上等价,但 com.apple.private.security.clear-library-validation 与先前的等价授权(com.apple.security.cs.disable-library-validation)有所不同,因为 com.apple.private.security.clear-library-validation 要求你通过 csops() 系统调用传入 CS_OPS_CLEAR_LV 来控制库验证的启用/禁用,以便在运行时对进程完整性保持更强的控制(相比之下,com.apple.private.security.clear-library-validation 大概允许你在运行时不受控制地加载任意库)。(https://github.com/apple-oss-distributions/xnu/blob/main/bsd/kern/kern_proc.c)(https://theevilbit.github.io/posts/com.apple.private.security.clear-library-validation/)
由于 csops(CS_OPS_CLEAR_LV) 会在 dlopen() 我们的库之前被调用,因此我们的库会被直接加载并执行构造函数,从而允许我们提供一个伪装成 pkcs11 库的恶意动态库,在 /usr/bin/ssh 的上下文中执行代码,并利用 keychain-access-groups 授权。
或许补丁是在调用 csops() 之前对二进制执行检查,以验证特定的受信任签名身份?
并不是!
在补丁版本(22G74)发布后,我对比了 pkcs11_add_provider()(即调用 dlopen() 的方法)的不安全实现,发现它与补丁版本看起来完全相同——但 /usr/bin/ssh 中少了一个授权?
<?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">
<dict>
<key>keychain-access-groups</key>
<array>
<string>com.apple.ssh.passphrases</string>
</array>
</dict>
</plist>
但 Apple 肯定没有从 SSH 中移除 pkcs11 支持吧?我尝试用我的 PoC 重新利用该漏洞,发现虽然我的库被加载了,但它无法再从钥匙串访问组中读取数据,而且似乎是在 /usr/libexec/ssh-apple-pkcs11(一个我之前从未见过的二进制)的上下文中执行,该二进制拥有以下授权:
<?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">
<dict>
<key>com.apple.private.security.clear-library-validation</key>
<true/>
</dict>
</plist>
csops() 系统调用中关于 CS_OPS_CLEAR_LV 的注释(https://github.com/apple-oss-distributions/xnu/blob/main/bsd/kern/kern_proc.c)提到,重新 exec 到一个没有库验证的二进制是 CS_OPS_CLEAR_LV 的替代方案,而非与它组合使用。那么,既然现在有了一个额外的辅助二进制,为什么逻辑上有缺陷的例程仍然存在于 /usr/bin/ssh 的 pkcs11_add_provider() 中?为什么实现看起来在补丁中没有变化?
嗯,事实证明,补丁并不是添加一个辅助二进制,而是在 macOS 中分发两个 ssh 二进制:/usr/bin/ssh 和 /usr/libexec/ssh-apple-pkcs11——除授权外两者完全相同(我使用 Diaphora 验证了这一点):
在对修补后的 ssh 二进制进行了一些动态分析后,我发现 ssh 那个(相当大的)start() 例程中新增了一个检查,用于在使用 pkcs11 功能时推断该二进制是在 /usr/bin/ssh 的上下文中执行,还是在 /usr/libexec/ssh-apple-pkcs11 的上下文中执行:
在绿色块中,我们可以看到对 SecTaskCopyValueForEntitlement() 的调用(传入的值为 "com.apple.private.security.clear-library-validation"),然后在橙色块中对返回值进行判断,如果当前任务/进程不具备 "com.apple.private.security.clear-library-validation" 授权,就会条件性地调用重新 exec 的例程(红色块)。
这样一来,在加载用户提供的 pkcs11 库时会使用授权更少的 ssh-apple-pkcs11 二进制(实际上禁用了 ssh 与钥匙串相关的功能),而在不加载不可信库的情况下则使用 ssh(启用了 ssh 与钥匙串相关的功能)。
我们还可以通过调试修补后的
如果我们调试修补后的 ssh 二进制(随 macOS 22G74 分发)并在 execv() 上设置断点,我们确实能在提供 -I 参数时发现对 execv() 的调用,而在不使用 pkcs11 功能启动 ssh 时则不会出现该调用:
我于 2022 年底通过 Apple 安全赏金计划向 Apple 报告了该问题,该问题后来在 macOS Ventura 13.5 中修复,并产生了 CVE-2023-42829(🎉)。
本文是一份独立出版物,未经 Apple Inc. 授权、赞助或以其他方式批准。macOS、iOS 和 iWork 是 Apple Inc. 的商标。