跳到正文
Hacker News· rs_rs_rs_rs_rs·· 1 天前AI 评分34

OpenBSD 曝出 37 年前内核漏洞:整数溢出可实现本地提权至 root

Big Fish: Exploiting a 37-Year-Old Kernel Bug in OpenBSD

AI 导读

Calif 披露 OpenBSD 一个可追溯约 37 年的内核漏洞,源于共享内存附加计数器被定义为有符号 16 位 short,反复 fork 可使其超过 32,767 后回绕为负值。

正文 · AI 翻译

大鱼。小计数器。Root shell。

Big Fish: an OpenBSD adventure

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 是我们的用户态内存映射,它被设为超级用户模式可访问以绕过 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 个数组边界消毒器本可捕获的问题;其中一些具有本地权限提升的安全影响。一个即将推出的可用于帮助缓解堆问题的编译器特性是分配令牌,它可用于将包含指针的分配与不包含指针的分配隔离开来。这种隔离可以使来自不包含指针的分配的溢出更难被利用,因为它阻止了指针访问,同时还能使释放后使用等漏洞类别无法使用相反类型的分配作为替换内存分配。

自成立以来,OpenBSD 一直通过源代码审计、替换不安全的 API、高级密码哈希、密码学、特权分离、沙箱,以及通过ASLR和W^X实现的内存安全,持续倡导主动安全;经常为系统和网络加固设定标准。

通过主动加固,并采用新的和现有的缓解选项,我们通常可以系统性地消除整类利用原语;或者至少增加攻击者的成本(即额外的时间投入、利用问题所需的额外漏洞、寻找替代途径等)。在漏洞被积极利用之前投资于防御性改进,会提高攻击者的门槛,有助于确保稳健的保护和长期的韧性。在 AI 辅助漏洞发现的时代,缓解整类漏洞,并结合增加攻击复杂性,只会变得更加关键。

代码和 PoC →

来源:Hacker News · calif.io