关注了就能看到更多这么棒的文章哦~
When ELF notes reveal too much
By Jonathan Corbet
February 22, 2024
Gemini translation
https://lwn.net/Articles/962782/
Linux 内核使用多种加固技术来防止自身受到破坏,其中一种技术是内核地址空间布局随机化 (KASLR)。但是,如果内核泄露其代码最终位于何处的信息,则随机化就没什么价值了。碰巧的是,内核自 2007 年以来一直在做这件事,这是一种在 KASLR 添加之前就在做的事情。人们进行了一些更改以填补该漏洞,但这说明了保密是多么困难。
对内核(如任何其他程序一样)漏洞进行攻击和利用,通常依赖于以意想不到的方式跳转到特定代码。人们已经开发了许多技术来防止这种情况发生;例如,控制流完整性(control-flow integrity)机制,就是用来防止执行过程偏离预定的路径的。此外,还有其他一些技术试图让攻击者很难知道将控制流重定向到何处,即使他们找到了允许此类攻击的 bug。KASLR 就是其中一种技术,它基于这样一个观察:如果攻击者不知道内核在其地址空间中的位置,他们就无法控制代码执行跳转到已知位置。
KASLR 背后的思想很简单:在启动时,内核在内存中的实际位置被随机选择。每次启动所选择的偏移量不同,因此攻击者一开始不知道内核位于任何给定目标机器上的什么位置。如果内核费尽心思不告诉攻击者其位置,就可以让对方在利用可能存在的任何漏洞时碰到更多障碍。
KASLR 最初被添加到内核(用于 x86 架构)中,是在 2013 年的 3.14 开发周期中。然而,随机化内核的布局已成为这项工作中最简单的部分;更困难的部分是防止内核泄露有关其位置的信息,让所有努力付诸东流。例如,大量内核代码乐于在 printk()
调用中打印出内核指针值。在 大量工作 之后,通过修复内核代码来使用针对指针的特殊格式化指令,并在未设置 kptr_restrict 的情况下拒绝将实际指针值输出到日志中,从而基本解决了这个问题。根据需要还修改了各种 /proc
和 sysfs 文件。随着时间的推移,要想了解特定系统上的内核位置就变得更加困难了;它是否足够有效地阻止一个技术水平够高的攻击者仍然是一个争论的问题。
问题在于内核和用户空间之间有很多信息通道,其中任何一个都可能泄露或揭示内核位置的信息。例如,考虑相对晦涩的 sysfs 文件 /sys/kernel/notes
。此文件 记录 得很简洁,包含 "正在运行的 vmlinux 的 .notes 部分的二进制表示"。该部分是包含内核映像的 ELF 文件的一部分,包含有关映像本身的有用信息;任何内核代码都可以使用 ELFNOTE() 宏 将数据添加到此部分。放置在那里的所有内容都可以从 /sys/kernel/notes
中读取到,尽管它采用二进制格式,这与通常的 sysfs “每个文件都是一个数值”的规则有些违背。
此文件的最常见用途是用来存储内核版本号,但至少还有另一种用途。Guixiong Wei 最近注意到此文件包含可能敏感的信息,包括 startup_xen 符号的位置;该信息足以让攻击者击败 KASLR。因为此文件是 world-readable,因此这些信息可供没有特权的攻击者使用,于是更加严重。Wei 发布了 一个补丁,将读取权限限制为 root 帐户,从而在某种程度上堵住了这个漏洞。截至本文撰写时,该补丁尚未进入主线,但预计会在不久的将来发生。
Kees Cook 是将大量 KASLR 工作加入内核(基于 其他人的工作,而这一工作已经进行了一段时间)的人,他证实,由于上述原因自 2007 年以来一直导致 KASLR 暴露,远早于 KASLR 本身被添加。他赞同这个权限更改,但也询问 /sys/kernel/notes
中的地址是否需要在启动时进行重定位;Xen 开发人员 Jürgen Groß 回答是,至少 Xen 不需要进行重新定位。换句话说,出现在 /sys/kernel/notes
中的地址不需要调整以反映启动时选择的随机偏移量。因此,我们可能会在未来看到此 sysfs 文件的进一步更改,尽管总是需要一段时间才能确认这类改动是否会导致某些用户空间程序被破坏。
正如这一事件表明的那样,查找出那些将地址泄露给用户空间的内核代码有点像一场打地鼠游戏,只不过在这种情况下,这只地鼠在不合理的情况出现后保持了 11 年。有一个工具旨在帮助人们发现需要打击的地鼠,称为 leaking_addresses.pl;它会查找系统日志,并遍历 /proc
和 /sys
以搜索已泄露到用户空间的内核地址。但是,它错过了 /sys/kernel/notes
,因为它所寻找的是人类可读字符串形式的地址;而 /sys/kernel/notes
是一个二进制文件,不太适合该工具使用的类似 grep
的方法。编码成 ASCII 的地址相对容易识别;相反,在二进制形式中,它们看起来就像数据。
然而,大多数数据都不包含对应于内核符号地址的值。为了找到异常情况(并捕获以二进制形式泄露的地址),Cook 发布了 leaking_addresses.pl 的补丁。它可以读取内核符号文件(例如 /proc/kallsyms
),并查看与这些符号关联的地址是否出现在 /sys/kernel/notes
这样的二进制文件中。有了此更改之后, leaking_addresses.pl
就会发现这种长期存在的内核地址泄露;它还应该有助于防止将来引入其他泄露。
这些更改将阻止 /sys/kernel/notes
破坏系统的 KASLR。不过,几乎可以肯定还有其他需要打击的地鼠。在内核的大部分历史中,开发人员没有什么理由要隐藏内部地址;即使是现在,保证 KASLR 正常工作,仍然不是大多数开发人员的首要关注点。KASLR 与其他技术结合使用时是一种有用的防御措施,但是既然任何开发人员在疏忽的情况下都可能破坏它,那么 KASLR 无法独立确保自己的价值。
全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。
欢迎分享、转载及基于现有协议再创作~
长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~