LWN: 名为 Sequoia 的 seq_file 漏洞!

关注了就能看到更多这么棒的文章哦~

The Sequoia seq_file vulnerability

By Jake Edge
July 21, 2021
DeepL assisted translation
https://lwn.net/Articles/863729/

7 月 20 日,Qualys 公司披露了 Linux 内核中一个名为 Sequoia 的本地 root 漏洞。在内核打上补丁(或采用一些可能不能完全解决问题的措施)之前,系统整体可能是不安全的。这个漏洞依赖于内核中的一条代码路径中 64 位 size_t 值被 "转换" 为有 signed integer 符号整数,这个过程就会出现溢出问题。该漏洞于 6 月 9 日被报告给 Red Hat 公司,同时发现的还有一个本地 systemd 拒绝服务(DoS)漏洞,会导致内核崩溃。如果系统中有无法信任的本地用户的话,需要尽快针对这两个问题进行更新。出于谨慎的考虑,建议所有系统都应该进行更新。

内核的 seq_file 接口是用来处理/proc 等虚拟文件的,它需要 buffer 来存储文件的每一行内容。最开始时,会给 buffer 分配一个内存 page,但如果这还不够的话,就会分配一个新的 buffer,其大小是旧 buffer 的两倍。这里都利用了一个 size_t 类型,它是一个 unsigned 64-bit 数据(在 x86_64 上),足以存放结果了,也就是说 "系统会在这个倍增之后的数据溢出之前,早就会耗尽内存了"。

但该值( m->size)被传递给了其他原本用于处理有符号的 32 位整数的函数。具体来说,该漏洞利用了 /proc/self/mountinfo 的输出,从而走到内核中的一个地方是满足这个条件的。这样一来,攻击者可以创建一个路径长度大于 1GB 的目录结构(大约 100 万个嵌套目录),将其 bind-mount 到一个 user namespace 中,然后删除该目录。在这个 namespace 里,攻击者打开并读取 mountinfo,会导致字符串"//deleted "被写入 seq_file buffer。seq_file 接口生成了一个大小为 2GB 的 buffer,但是在写字符串的代码中,这个值被解释为-2GB,错误的数值被用来决定在哪个位置写入数据了。当然,肯定会导致写在 buffer 之外了,所以它在 vmalloc() 区域的其他地方写入了一个精心计算过的位置。

正如我们所猜测的那样,在 buffer 外写一个 10 字节的固定字符串只是一个复杂的、但容易重现的获取 root shell 的一系列操作的开始。Qualys 公司的报告详细介绍了这一过程,其中涉及到过去十年左右出现的几个不同的内核特性:用户命名空间、BPF 和 user-space page fault handling(或者 FUSE 也行,它出现得要早得多)。但最终还是这里对 buffer size 的整数大小处理不当导致了问题。修复方法是一种创可贴方式的 fix,简单拒绝 "过大" 的 seq_buf 分配动作。

Qualys 生成它已经有了可以在 "缺省安装的 Ubuntu 20.04、Ubuntu 20.10、Ubuntu 21.04、Debian 11 和 Fedora 34 工作站" 上获得 root 权限的漏洞利用代码,此外,"其他 Linux 发行版肯定有漏洞,而且可能可以利用"。它所建议的弥补措施之一是关闭非特权用户创建 user namespace 的能力(通过配置 /proc/sys/kernel/unprivileged_userns_clone),因为这将阻止他们,使得攻击者无法加载那些导致 1GB 以上路径名称的嵌套过深的目录。但是,即使没有 user namespace,也可能还有其他攻击方案:

然而,攻击者可能会通过 FUSE 来挂载一个非常深的目录,我们还没有完全探索这种可能性,因为我们无意中发现了 systemd 中的 CVE-2021-33910:如果攻击者通过 FUSE 来 mount 一个长目录(超过 8MB),那么 systemd 会耗尽其堆栈,崩溃,从而使整个操作系统崩溃(kernel panic)。

Qualys 对 systemd 的 bug 也进行了很好的描述,它来源于在 mount 路径处理代码中从 strdup()切换到 strdupa()。于是基于 heap 的分配变成了基于 stack 的分配,这就提供了耗尽这 8MB(默认设置)stack 的可能性。因此,当 systemd 在解析 /proc/self/mountinfo 时发现一个过长的路径时,会导致 segmentation fault;Linux 因为发现没有了 init 进程,于是也就 panic 了。

用户可以通过 mount FUSE 文件系统来创建一个超过 8MB 的目录路径,将 FUSE 文件系统 move 至该目录,并让 systemd 再次解析 mountinfo 文件,这样就可以触发 panic 了。这种情况下的 fix 是改回使用 strdup(),回复到 2015 年 4 月的那个 commit 之前的情况。

systemd crash 是在研究 Sequoia 漏洞时发现的,当时在使用 BPF 代码查找并重写内核中的 modprobe_path 变量。在加载 kernel module 时,这个代码分支会被用来以 root 身份运行一个可执行文件(通常是/sbin/modprobe)。把它指向一个不同的路径(也就是放置攻击者可以放置自己的可执行文件的位置),就能够获得 root 权限。

人们期望 BPF verifier 能检查出来并且避免执行有害程序,毕竟这是防止用户加载不安全的 BPF program 的主要机制。但这里就可以利用上 user-space page-fault handling 机制了。userfaultfd() 系统调用允许利用者在运行 BPF verifier 后暂停内核的执行。然后,"//deleted " 这个写入动作就可以被精心设计之后覆盖掉 BPF 代码的某个位置,因为该代码的加载位置就是 vmalloc() 区域。userfaultfd() 就可以被攻击者利用来在 BPF 代码被验证后、但在其被 JIT 编译之前来改变这部分代码,从而逃避掉 BPF verifier 的检查。

"//deleted "的越界写入可能会导致信息泄露,这样攻击代码可以对它写入的数据进行一些比较有限的控制。之后,Manfred Paul 发现的 2020 年 BPF 漏洞中的技术就可以被用来 "将这个危害很有限的越界写入行为转化为对内核内存的任意读写"。文档中介绍了六个步骤的 "Exploitation overview" 概述了如何实现攻击,而后面的部分则给出了所有惊心动魄的细节。对于那些对内核漏洞感兴趣,想了解如何利用这些漏洞的复杂步骤的人来说,这篇文章会很有用。

这两个漏洞已经在 Red Hat 公司手中近一个月的时间了,然后 7 月 6 日它们被报告到 kernel and distribution security reports 的非公开邮件列表中。两周后,大家协调发布了参考声明(advisories),发行版更新也开始推出。目前还不清楚为什么会有长达一个月的延迟,因为这两个 fix 方案似乎看起来都不是特别有困难。也许这些时间是用来寻找内核和 systemd 中的其他类似问题的。

显然,漏洞核心的这个整数转换动作需要 fix。我们不禁要问,在内核中还有多少类似的 size 转换问题。人们可能还想知道,当 userfaultfd()被添加到内核中时,究竟会给多少 bug 提供了孵化地。因为它为用户空间提供了一种可以基本任意暂停内核在它选择的位置的方法。这在今后的内核漏洞中可能会再次派上用场。

同时,systemd 可能已经仔细检查过它其他地方进行的 stack-based 分配动作,特别是对用户可控制的那些值,比如路径参数。虽然对于用户空间的程序来说,耗尽 stack 通常并不算是一个安全问题(当然,除了像 Stack Clash 这种著名漏洞),但 systemd 在许多 Linux 系统中处于一个很敏感的位置。用户能做到的任何会使其瘫痪的手段都是有效的系统拒绝服务攻击。

我们在这里看到的那种整型转换问题,在其他一些语言(比如 Rust)中可能根本不是个问题。另一方面,堆栈(或内存)耗尽并不是真正可以在编程语言层面上处理的事情;遇到资源限制必须以某种方式处理,比如让这个用户空间的程序 crash, 这其实是操作系统完全可以做的对系统无害的保护。但至少我们的系统中现在的 bug 数量又减少了 2 个,变成了 N-2。可不幸的是,这里这个未知数 N 可能大得令人不安。

全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值