stalld:未打补丁的固定临时文件使用和其他问题
#local #tmpfiles目录
- 1) 引言
- 2) 在
scripts/throttlectl.sh中使用固定临时文件路径/tmp/rtthrottle - 3)
fill_process_comm()函数可能读取意外的控制字符 - 4) 实验性的 FIFO 提升功能可能存在锁定系统的风险
- 5) 访问
/proc/<pid>/{status,comm}时可能出现的竞争条件 - 6)
daemonize()中使用的奇怪umask()设置 - 7) CVE 分配
- 8) 时间线
- 9) 参考资料
1) 引言
Stalld 是一个旨在防止 Linux 操作系统线程饿死的守护进程。它最近被添加到 openSUSE Tumbleweed 中,我们对其包含的 systemd 服务进行了例行审查。在审查过程中,我们发现了一些应予解决的安全问题。
我们通过其 GitLab 问题跟踪器联系了上游,并创建了一个公开问题和一个私有问题(仍为私有),但从未收到任何回复。在近三个月没有得到答复后,我们决定现在发布现有信息。
本报告基于 stalld 版本 v1.19.6。
2) 在 scripts/throttlectl.sh 中使用固定临时文件路径 /tmp/rtthrottle
stalld 的 systemd 单元中作为预脚本和后脚本调用的 throttlectl.sh 脚本,使用固定的 /tmp 路径 /tmp/rtthrottle 来缓存从 /proc/sys/kernel/sched_rt_runtime_us 和 /proc/sys/kernel/sched_rt_period_us 中找到的原始值。这允许进行符号链接攻击和文件预创建攻击。
2.a) 符号链接攻击
只有当 Linux 内核的 protected_symlinks 设置未生效时,符号链接攻击才能成功。如果那样的话,攻击者就可以在指定位置放置一个符号链接,导致 throttlectl 覆盖系统中的任意文件,从而可能导致本地拒绝服务。
2.b) 文件预创建攻击
在 /tmp/rtthrottle 中预创建文件将始终有效,即使内核中的 protected_regular 设置已激活。这是因为脚本中的 shell 重定向(如 echo $period > $path/sched_rt_period_us 这一行)在 open() 标志中,如果创建文件失败,将回退到不带 O_CREAT 打开目标文件。没有 O_CREAT,protected_regular 逻辑将不再触发。
这意味着,如果本地攻击者预先创建了该文件,脚本将写入攻击者拥有的文件。当脚本尝试从该文件中恢复值时,本地攻击者可以在其中放置任意值,这些值随后将被写入 /proc/sys/kernel/sched_rt_* 中的伪文件中。这是一种本地拒绝服务或本地完整性破坏。它不是信息泄露,因为这些伪文件的内容对所有人都是可访问的。
2.c) 可利用性
当 stalld 在启动时启动时,未经授权的本地用户对此问题几乎没有利用机会。然而,如果服务在稍后启动或重新启动,则该攻击向量是可利用的。
2.d) 建议的修复方法
为了解决这个问题,我们建议将文件放置在由 root 拥有的 /run/stalld 目录中。该目录已通过 stalld 的 systemd 单元创建。
在 systemd 单元中,还可以应用 PrivateTmp=yes 等加固措施,以防止将来发生此类临时文件问题。
throttlectl 脚本还应设置 errexit shell 选项,使其在任何意外错误时退出。
3) fill_process_comm() 函数可能读取意外的控制字符
从系统中可能不受信任的进程读取 /proc/<pid>/comm 的内容的 fill_process_comm() 函数。那里找到的数据来自内核执行的可执行文件的名称。可执行文件名称可以包含除 / 字符以外的任何数据。这还包括控制字符,如 \r 甚至终端控制序列。stalld 使用此字符串将信息写入日志。通过在可执行文件名中嵌入回车符,本地攻击者可以实现日志欺骗。
为了解决这个问题,我们建议将字符串中的所有非字母数字字符转换为一个安全字符,如 ?。
4) 实验性的 FIFO 提升功能可能存在锁定系统的风险
通过 --force_fifo 命令行开关,可以指示 stalld 通过将任务切换到 SCHED_FIFO 调度来“提升”被卡住的任务。我们想知道如果一个“恶意任务”被分配到这个调度程序会发生什么。据我们所知,如果这样一个任务不再释放 CPU,整个系统可能会被锁定。这可能需要 stalld 本身在 SCHED_FIFO 下运行,使用比被提升任务更高的调度优先级,以防止这种情况发生。
5) 访问 /proc/<pid>/{status,comm} 时可能出现的竞争条件
像往常一样,在迭代 /proc 文件系统中的进程时,可能会发生竞争条件。目标进程可能试图通过其他进程替换自身,从而使 stalld 混乱。但我们不认为“卡顿”情况可以轻易被本地攻击者所诱发,因此从这个方向利用任何可能性的几率可能很小。
我们只是在此作为提示,也许我们忽略了更关键的东西。
6) daemonize() 中使用的奇怪 umask() 设置
通过调用 umask(DAEMON_UMASK),daemonize() 函数向守护进程应用新的 umask。但其常量具有奇怪的值。
/*
* Daemon umask value.
*/
#define DAEMON_UMASK 0x133 /* 0644 */
我们不知道为什么不首先使用八进制 0644 值,而是将其作为注释。常量 0x133 对应于八进制值 0463。它将屏蔽所有者可读位、组的读写位以及世界的读写执行位。这可能不是这里的意图。
幸运的是,不会因此产生任何世界可写文件,但这种错误配置可能会导致未来出现奇怪的效果,例如,因为文件的所有者将对其没有读取权限。
我们不认为这是一个安全问题,因此我们在此创建了一个上游 GitLab 跟踪器中的公开问题。
7) CVE 分配
由于上游没有做出反应,因此也未确认任何这些问题,所以我们至今尚未从 Mitre 请求任何 CVE。固定的临时文件使用问题 2) 可能值得分配 CVE。
8) 时间线
| 2024-09-09 | 我们已在上游 GitLab 项目中报告了这些问题(1,2),并为敏感问题提供了协调披露。 |
| 2024-11-13 | 在长时间未收到回复后,我们在问题中评论,要求在 2024 年 11 月 22 日前回复,否则我们将自行发布该问题。 |
| 2024-11-28 | 我们在上游修复程序可用之前发布了这些信息。 |