这是关于 `pam_namespace.so` PAM 模块中的一个本地拒绝服务漏洞的报告。该模块是 `linux-pam` 项目中的核心 PAM 模块之一。

此报告先前已与 `linux-distros` 邮件列表共享,并在 `linux-pam` 上游发布了包含该错误修复的新版本 `1.6.0` (于 2024-01-17) 后公开发布。

引言

`pam_namespace` 模块允许在用户登录时设置“多实例目录”。典型示例是为每个用户设置一个私有的 /tmp 和/或 /var/tmp。

为实现此目的,在登录过程中会设置一个单独的挂载命名空间,并在配置的位置执行绑定挂载。为此提供了不同的方法,例如一个固定的每用户目录(即每用户内容持久且在会话之间共享)或一个临时的临时目录(会话关闭后内容将丢失)。

漏洞

PAM 模块明确支持在用户可控的位置(如用户主目录下方)进行多实例目录的绑定挂载。在用户可控的目录中使用 root 权限存在许多危险。为避免这些危险,`protect_dir()` 函数实现了一个特殊的算法来保护绑定挂载的目标路径。

该函数从文件系统根目录开始跟踪绑定挂载的目标路径。非 root 用户控制下的每个路径组件都通过将其自身绑定挂载来防止用户操作。

虽然这种方法看起来不寻常,但它应该能有效地防止未授权用户对正在挂载的目录进行任何恶意操作。

但有一点被遗漏了:该算法没有将 `O_DIRECTORY` 标志传递给 `openat()`,因此容易受到在用户可控目录中放置特殊文件(如 FIFO)的影响。例如,在 `namespace.conf` 配置文件中使用此配置条目可以轻松重现此问题:

$HOME/tmp /var/tmp/tmp-inst/ user:create root

未授权用户(尚未处于相应的挂载命名空间中,且 `~/tmp` 已被配置为多实例目录)现在可以在此处放置一个 FIFO。

nobody$ mkfifo $HOME/tmp

随后,尝试以该用户身份登录(已配置 `pam_namespace`)将导致 `protect_dir()` 中的 `openat()` 阻塞,从而造成本地拒绝服务。

错误修复

我建议的 错误修复 通过将 `O_DIRECTORY` 打开标志传递给 `openat()` 来解决此问题,如果路径不是目录,则导致打开失败。这样一来,一些现有的显式文件类型检查就可以被删除。

即使应用了此补丁,未授权用户仍然可以通过在挂载位置放置一个 FIFO 来阻止多实例目录的挂载。我不认为 `pam_namespace` 在这方面提供了(或应该提供)任何保证,因此我不认为这是一个问题。

时间线

2023-12-27 我已将此发现报告给 `linux-pam` 的维护者,并提供了协调披露和建议的补丁。
2023-12-27 一位上游维护者迅速回应,表示 `linux-pam` 项目不会为他们的目的特殊对待安全问题,但建议仍设置一个短期禁运,以允许其他下游使用者进行准备。
2023-12-29 鉴于上游计划在一月份发布新版本,我们同意在此版本发布前一段时间与 distros 邮件列表共享该问题。
2024-01-05 我已向 Mitre 申请一个 CVE 来跟踪此问题。
2024-01-09 Mitre 分配了 CVE-2024-22365。
2024-01-09 上游向我传达了计划于 2024-01-17 发布的新版本,其中将包含此错误修复。
2024-01-09 我已将此问题与 linux-distro 邮件列表共享。
2024-01-17 `linux-pam` 上游已按计划发布了包含该错误修复的版本 1.6.0。

参考文献