目录

1) 引言

Below 是一个用于在 Linux 上记录和显示系统数据(如硬件利用率和 cgroup 信息)的工具。2025 年 1 月,Below 被打包并提交到 openSUSE Tumbleweed。Below 以 root 权限作为 systemd 服务运行。SUSE 安全团队监控 openSUSE Tumbleweed 中 systemd 服务单元文件的添加和更改,并在此过程中发现了 Below 代码中应用的有问题的日志目录权限。

我们在该上下文中审查的版本是 v0.8.1,本报告基于该版本。

上游在版本 v0.9.0 中发布了一个错误修复和一个 GitHub 安全公告

2)在 /var/log/below/error_root.log 中进行符号链接攻击

Below 的 systemd 服务以完整的 root 权限运行。它试图在 /var/log/below 中创建一个全局可写目录。即使该目录已存在,Rust 代码会确保它获得 0777 权限模式。

    if perm.mode() & 0o777 != 0o777 {
        perm.set_mode(0o777);
        match dir.set_permissions(perm) {
            Ok(()) => {}
            Err(e) => {
                bail!(
                    "Failed to set permissions on {}: {}",
                    path.to_string_lossy(),
                    e
                );
            }
        }
    }

此逻辑会导致不同的结果,具体取决于 Linux 发行版上的打包方式。

  • 在 openSUSE Tumbleweed 中,该目录以 01755 权限打包(below.spec 第 73 行),从而触发 set_permissions() 调用,导致运行时目录模式为 0777。
  • 在 Gentoo Linux 中,目录以 01755 模式创建,导致与 openSUSE Tumbleweed 相同的结果(below.ebuild)。01755 模式的确切来源并不完全清楚,可能是 cargo 构建过程在安装过程中分配了这些权限。
  • 在 Fedora Linux 中,该目录以 01777 权限打包,因此 set_permissions() 代码不会运行,因为 if 条件会屏蔽掉 sticky bit。目录保持 01777 模式(rust-below.spec)。
  • Arch Linux AUR 包(可能错误地)没有预先创建日志目录。因此,set_permissions() 代码将运行,并以 0777 模式创建目录(Arch Linux AUR 包)。

Below 在 /var/log/below/error_root.log 中创建一个日志文件,并为其分配 0666 模式。这(有点令人困惑地)通过一个 log_dir 变量完成,该变量已更改为指向 error_root.log 文件。日志文件的 0666 权限分配logging::setup() 中完成,代码中还有一个相当奇怪的注释。

本地未提权的攻击者可以在此位置进行符号链接攻击,并导致系统中的任意文件获得 0666 权限,如果操作得当,可能导致完全的本地 root 漏洞利用,例如将符号链接指向 /etc/shadow。即使文件已存在,由于全局可写目录权限,也可以将其删除并替换为符号链接。因此,攻击不限于文件尚未由 Below 创建的场景。

我们认为这段代码的实际意图可能是分配 01777 模式(即带有 sticky bit)。但是,sticky bit 既不在 if 条件中,也不在 set_permissions() 调用中。设置 sticky bit 后,Linux 内核的 protected_symlinks 逻辑(在大多数 Linux 发行版上启用)将防止符号链接攻击。

3)进一步的问题

即使在 Fedora Linux 上,其中 /var/log/below 具有“安全”的 01777 权限,也存在一个可能出现问题的窗口期。只要 below.service 未启动,其他本地用户就可以预先创建 /var/log/below/error_root.log 并例如在那里放置一个 FIFO 特殊文件。这将对 Below 服务造成本地拒绝服务(DoS),因为它将无法打开路径,从而无法启动。

如果出于任何原因删除了 /var/log/below,Below 仍会使用糟糕的 0777 模式权限重新创建它,这在最初使用不触发 Below 代码中 set_permissions() 调用的权限打包 /var/log/below 的发行版上也会发生。

Below 在 /var/log/below 下应用了许多全局可写和全局可读的权限。这似乎是一个奇怪的选择。出于某种原因,Below 的内部状态数据也存储在 /var/log/below/store 的日志目录中。数据是完全全局可读的,这可能导致信息泄露,如果 Below 在那里存储的信息对于未提权的本地用户而言否则是无法访问的。我们没有检查这是否适用。通过在 below.service 首次运行时之前预先创建此目录,未提权用户可以控制其所有内容,可能以各种方式违反 Below 的完整性。

全局可写的日志文件 error_root.log 对我们来说也没有任何意义。为什么系统中的任意用户能够修改 Below 的日志数据?这允许本地用户进行日志欺骗。如今,即使将日志文件设置为全局可读,也被一些人认为是糟糕的做法。为什么 /var/log/below 首先应该是全局可写的,对我们来说也不清楚。理想情况下,只有 root 或专用的 below 服务用户应该被允许写入。

4)错误修复

上游在 commit 10e73a21d67 中发布了一个错误修复,该修复包含在 Below v0.9.0 中。该提交基本上删除了代码中所有有问题的权限分配,并指出这些目录最好由 systemd 设置。这似乎是指在 below.service 文件中添加的 systemd 指令 LogsDirectory=below

通过此更改,/var/log/below 中不再会出现全局可写目录或文件,并且本报告中最严重的问题得到了解决。但是,全局可读日志和存储文件的问题仍然存在。

我们没有从上游那里获得任何关于 Below 导致此问题的设计决策的细节,也没有关于上游打算进行的任何进一步改进该领域安全性的更改的细节。

5) CVE 分配

上游为此问题分配了 CVE-2025-27591。

6)加固建议

可以考虑在 Below 的 systemd 服务单元中应用加固指令,以防止某些攻击类型。最突出的是,限制守护进程对一系列已知位置的写访问。

7) 时间线

2025-01-20 我们发现了这个问题,并开始在 bsc#1236109 中私下跟踪。
2025-01-20 我们通过 Meta 的 安全漏洞报告系统 与 Meta 共享了信息,并提议协调披露。
2025-01-21 我们收到了 Meta 的初步自动回复。
2025-02-21 我们收到更新,报告将被转交给相关工程团队。
2025-02-26 我们获得了该报告的漏洞赏金,但未收到有关发布、错误修复或 CVE 分配的任何详细信息。我们将把漏洞赏金捐赠给开源项目和其他非营利组织。
2025-02-26 我们的 Below 打包人员将 openSUSE Tumbleweed 包更新到了新发布的 v0.9.0 版本,该版本恰好已经包含了该问题的错误修复。
2025-02-27 我们确定 commit 10e73a21d67 是可能的错误修复,并再次向上游询问了技术细节以及这是否是他们打算应用的完整错误修复。
2025-02-28 我们收到了关于该问题错误修复状态的自动回复。
2025-03-03 我们收到了确认 commit 10e73a21d67 是预期的错误修复,并且进一步的步骤(包括可能的 CVE 分配)正在内部处理。
2025-03-03 我们询问现在是否可以发布完整报告。
2025-03-07 我们至今未收到上游关于发布的回复。由于错误修复是公开的,但没有明确标记为安全问题,因此我们将此报告与 linux-distros 邮件列表共享,建议在普遍发布前进行 5 天禁运。
2025-03-08 Michel Lind,linux-distros 邮件列表成员,也是 Meta 的工程师,在内部联系了上游关于即将进行的披露。
2025-03-08 上游与我们联系,表示计划在下周发布关于该问题的 GitHub 安全公告。他们还与我们共享了 CVE 分配。他们要求我们将本地发布推迟到那时。
2025-03-10 我们回复说推迟发布对我们来说是可以的。我们还指出,linux-distros 邮件列表的最长禁运期为 14 天,这限制了最长推迟到 2025-03-21。
2025-03-12 上游发布了 GitHub 公告。因此,普遍发布可以按照我们最初在 linux-distros 邮件列表上提出的日期进行。

8) 参考资料