Big Fish: Exploiting a 37-Year-Old Kernel Bug in OpenBSD
中文摘要
由模型生成 · 人工抽检安全研究者披露 OpenBSD 内核中一个存在 37 年的漏洞,可被利用实现本地提权。该漏洞源于早期代码逻辑缺陷,影响长期被视为高安全性系统的 OpenBSD,提醒人们长期未审计的遗留代码仍是重大风险。
全文
已译中文大鱼。小柜台。Root shell。

Calif 经常发布我们的攻击性研究成果和工作,但除此之外,我们并不太宣传我们所做的防御性工作——协助开源项目进行修复、生成补丁、改进密码学,以及我们为客户执行的内部工程项目。
今年早些时候,我们报告了一些漏洞,通常还附带了针对 OpenBSD 的建议补丁。其中一个可以追溯到大约37年前的原始 BSD 源码,其中共享内存附加计数器被定义为有符号的16位 short。反复 fork 一个带有共享内存附加记录的进程,可能会将该计数器推过其最大值32,767,导致其回绕为负值。这种整数溢出可能通过竞态条件导致释放后使用;再结合内核中另一个独立的越界写入,它允许本地权限提升至 root。这些问题分别在勘误 026和018中得到解决。
漏洞利用
我们在启用了 `KARL` / `SMAP` / `SMEP` 的 OpenBSD 上,在未利用信息泄露漏洞的情况下,通过以下路径成功获取了本地 root 权限:
- 映射 127 个 SysV 共享内存段,通过
munmap()解除映射,并 fork 259 个进程将共享内存附加计数回绕至 -32,643。 - 执行堆布局与整理,利用 sysctl(
KERN_MALLOCSTATS)(如kb_total和kb_totalfree)的指标来跟踪分配情况并确定新页面何时被分配。 - 在一个进程中利用
IPC_RMID赢得竞态条件,同时在辅助进程中附加一个新的尺寸混淆的共享内存段。 - 对齐 IP socket 分配,并将
IP_MULTICAST_TTL设置为非默认值 2,以分配一个持久的 16 字节 `struct ip_moptions`。其最后一个字打包了两个 16 位计数器:imo + 12 = imo_num_memberships | imo_max_memberships << 16。 - 触发条件性
-1越界写入,覆盖成员数量和最大成员数量字段。 close()该 socket 以遍历组成员,触发原子递减,该操作两次修改 PTE 自引用映射,将其从用户可访问映射切换为超级用户可访问映射,并通过覆盖一个可预测的内核指针实现内核代码执行。- 清理共享内存记账并恢复被覆盖的内核指针,以获得 root shell。
简而言之就是这样,详细版本更有意思,包括该漏洞的历史——一个简短的 shm_nattch 可以追溯到约 37 年前的原始 BSD 源码中。
退一步看,这个漏洞最终通过一个 write what where 原语实现了代码执行,其中 write 由 `TAILQ_REMOVE` 触发,what 是我们的用户态内存映射——它被设为 supervisor mode 可访问以绕过 SMAP/SMEP,而 where 则是一个可预测的内核指针位置。
漏洞利用缓解措施
虽然 OpenBSD 的 KARL 会重新链接内核二进制文件以随机化函数地址和数据地址,但内核的内存分配器仍然是确定性的。启动时分配器和最佳适配分配器都缺少基址随机化。因此,在同一硬件上,早期启动分配始终落在跨启动可预测的位置。当这些可预测的结构体分配包含函数指针时,它们就成为理想的利用目标。例如,该漏洞利用可靠地针对 lo0 和 enc0 的 ioctl 处理程序,覆盖其中一个以获取控制流,并使用另一个来恢复被覆盖的指针。
除了上述勘误修复之外,还有几种其他方法可以消除或降低这些途径再次被使用的可能性。
首先,我们可以实现安全解链(safe-unlinking),这是一种在执行解链内存写入之前检查双向链表不变量的技术。如果不变量被违反,则根据是用户模式还是内核模式调用适当的 abort() 或 panic(),从而防止内存损坏的发生。一个示例实现见此处。FreeBSD 和 NetBSD 在其 sys/queue.h 实现中已有安全解链的实现。
其次,我们可以随机化自引用 PTE 映射的分配地址,使得针对性的原子递减位置无法被利用。具体做法是在启动时使用一个随机字节(.openbsd.randomdata)选择一个 PML4 槽位(320 + (random_byte & 127)),并在该位置安装递归条目。PTE_BASE 的定义变为一个运行时表达式,以 512 GiB 为间隔产生从 0xffffa00000000000 到 0xffffdf8000000000 的基址。一个示例实现见此处。如果随机化不适用,其他方案可能也足够,例如将 PTE 标记为只读,并在修改期间更改 CR0.WP。
第三,可以通过两种方法使内核分配器变得非确定性:一是随机化基地址,即内存分配在启动时所发生的位置;二是额外在运行时随机化分配顺序。基地址随机化实际上几乎没有代价,因为它所产生的空隙之后仍然可以使用;而运行时随机化确实会在分配期间带来一些开销——或许更深入的分配器改动可以减轻这一影响。无论采用哪种方法,都足以在没有额外漏洞(无论是软件还是硬件层面)的情况下打破可预测的指针位置。
此外,还有一些现有和即将推出的编译器特性可用于缓解整类漏洞,例如数组边界检查器(未定义行为检查器的一个子集),它可以消除越界的固定大小数组访问。Calif 已向 FreeBSD 建议,数组边界检查器可以作为一种有用的漏洞利用缓解措施,在发布构建中启用,同时私下报告了 21 个数组边界检查器本可捕获的问题;其中一些具有本地权限提升的安全影响。一个即将推出的编译器特性可用于帮助缓解堆问题,即 Alloc Tokens,它可用于将包含指针的分配与不包含指针的分配隔离开来。这种隔离通过阻止指针访问,使来自不包含指针的分配的溢出更难以被利用,同时使 Use-after-Free 等漏洞类别无法使用相反类型的分配作为替换内存分配。
自诞生以来,OpenBSD 始终通过源代码审计、替换不安全的 API、先进的密码哈希、密码学、特权分离、沙箱化,以及通过 `ASLR` 和 `W^X` 实现的内存安全,持续倡导主动安全防护;并屡屡为系统与网络加固树立标准。
通过主动加固并采用新的和现有的缓解措施,我们往往能够系统性地消除整类利用原语;或者至少提高攻击者的成本(即额外的时间投入、利用问题所需的更多漏洞、寻找替代途径等)。在漏洞被积极利用之前投资于防御性改进,会提高攻击者的门槛,有助于确保稳健的保护和长期的韧性。在 AI 辅助漏洞发现的时代,针对整类漏洞进行缓解,并结合提高攻击复杂度,只会变得更加关键。
本译文由模型自动生成,仅供参考与快速理解;涉及数字、版本、命令、法律条文等请以原文为准。
正文为原站公开内容的抓取与清洗结果,英文条目附模型生成的中文译文;两者均仅供参考, 准确内容请以原站为准。内容版权归原作者所有,若认为侵权请联系我们下线。