2026年11月2日星期一
漏洞

OpenBSD Root 提权链:37年老漏洞被串联利用

Calif将37年前的SysV整数溢出与越界写入串联,在OpenBSD 7.9上绕过KARL、SMAP和SMEP获取root权限,PoC已经公开。

放大的一枚锈蚀小齿轮,位于大型抛光黄铜机器的中央
这个16位计数器继承自原始BSD源码,却足以让数十年的内核加固功亏一篑。

在KARL重链接、SMAP和SMEP全部开启、也没有信息泄露漏洞辅助的情况下,OpenBSD 7.9仍被获取了root权限。安全公司Calif本周展示了这一结果,其公开的研究报告显示,切入点是一个在BSD共享内存代码中潜伏了约37年的整数溢出。

在OpenBSD的笑话出现之前,先说清楚谁要为此买单。这是本地权限提升,而非远程漏洞:攻击者必须先以非特权用户身份执行代码。因此,受影响的是运行多用户OpenBSD系统、共享构建主机、隔离服务,或任何默认存在恶意本地账户的环境。如果你身处其中,那么把本地提权补丁当作二线维护工作的日子已经结束了。

一个来自里根时代的16位计数器

这个漏洞的简单程度近乎令人难堪。在原始BSD源码中,共享内存的挂接计数器shm_nattch被定义为有符号16位短整数,最大值为32,767。Calif发现,反复fork带有共享内存挂接记录的进程,可以把计数器推过这一上限,使其回绕为负值。这一整数溢出与一个竞争条件相结合,就产生了释放后使用漏洞。

OpenBSD的品牌形象建立在主动审计之上,却让这个算术化石在代码中躺了几十年。Hacker News讨论串中有人指出,NetBSD的对应代码在1999年8月的一次重构中被意外修复。是意外。没有人是有意找到它的,它只是恰好卷入了一次重构。比起报告中的任何利用原语,这个细节更能说明代码审查作为一种控制手段的局限。你可以尽情审计:一个自1980年代起就没人有理由触碰的有符号短整数,在有人提出那个尖锐的问题之前,看起来根本不像攻击面。

OpenBSD分别在errata 026和018中修复了整数溢出,以及内核中另一个独立的越界写入漏洞;Calif在补丁发布后公开了完整的概念验证代码,据The Hacker News报道。也就是说,补丁先于公开的利用代码出现,这说明供应链按设计运转了。接下来,才是会让你的补丁队列紧张起来的部分。

真正的威胁在于攻击链,而非单个漏洞

单独看,这两个本地漏洞属于那种被录入、修复、然后遗忘的类型。但串联起来,它们在没有任何信息泄露的情况下,绕过了OpenBSD三项主要防护机制。根据Calif的报告,其步骤堪称堆内存经济学的小型教科书:

  1. 映射127个SysV共享内存段,用munmap()解除映射,然后fork 259个进程,使挂接计数回绕至-32,643。
  2. 利用sysctl(KERN_MALLOCSTATS)提供的分配器指标进行堆整理,追踪新页面何时出现。
  3. 在一个进程中调用IPC_RMID,同时在辅助进程中以大小混淆的方式挂接,从而赢得竞争条件。
  4. 使用设置了非默认IP_MULTICAST_TTL值的IP套接字,将一个持久的16字节ip_moptions结构体放置在目标旁边。
  5. 触发有条件的-1越界写入以破坏成员计数器,然后对套接字执行close(),使清理路径对攻击者选定的内存递减两次——由此将PTE自引用映射由用户态翻转为内核态,绕过SMAP/SMEP,并覆写一个可预测的内核指针以获取root权限。

这些步骤都不需要通过暴力破解来绕过随机化,这正是令人不安之处。KARL会重链接内核二进制文件,随机化函数和数据地址,但正如Calif指出的,内核内存分配器依然是确定性的。在相同硬件上,每次启动时早期引导的内存分配都会落在相同位置。因此,攻击者只需瞄准包含函数指针的可预测结构,本例中是lo0和enc0的ioctl处理函数:覆写其中一个以劫持控制流,再利用另一个在事后恢复指针。

一排栅栏,其中一扇门没有上锁,而其余部分都用钢条加固
KARL打乱了内核代码的布局,但分配器从未学会打乱,而攻击者知道该敲哪扇门。

你为“默认安全”真正买单的是什么

董事会应该追问的问题在这里。它不是OpenBSD现在是否不安全,而是:这些防护机制究竟买到了什么?KARL、SMAP和SMEP把利用成本从一个漏洞提高到了两个漏洞加上数周的专业堆整理,这是实实在在的价值。但这种价值也是有限的,因为漏洞链的算术十分残酷:防护强度会成倍增加攻击者的工作量,而漏洞数量则会将其除回去。同一代码库中两个平庸的本地漏洞,就等于一个root shell。

我们近期在攻击侧也看到了同样的算账方式:当Xbow花费4万美元的AI算力构建Chrome漏洞利用链时,教训并不是Chrome不安全,而是串联攻击已经成为一项预算科目,而预算正在不断下降。今天一名人类研究员串联一个37年前的溢出,明天就可能成为自动化工具针对你全部资产发起的工作流。应该关注的指标是串联所需的时间,而不是CVE数量。

值得肯定的是,Calif并没有止步于拿下战利品。其报告提出了具体的加固建议:对双向链表实施安全断链检查(FreeBSD和NetBSD的sys/queue.h中已有相关实现);在启动时使用随机字节选择PML4槽位,从而随机化PTE自引用映射地址;并让内核分配器变得非确定性。OpenBSD是否采纳,是资源投入问题,而非技术问题,因为这些实现方案已在同一份文档中勾勒出来。

因此,本周防御者需要做什么

第一,立即应用errata 026和018。这看似显而易见,但仍需强调:从公开概念验证代码出现到你完成补丁部署之间的窗口,才是唯一重要的时段,而这个窗口现在已经打开。

第二,重新排定补丁策略中本地提权漏洞的优先级。大多数组织的分级依据是远程可利用性,于是本地内核漏洞就一直拖到下一个维护窗口。在任何运行不可信代码的主机上,攻击链都会颠覆这一逻辑:包括CI运行器、共享shell、容器宿主机,以及任何存在你并不完全信任的本地用户的系统。如果你无法确切说明某台机器上哪些账户可以执行代码,那么你真正的风险敞口就是攻击链场景,而不是单个CVE。

第三,花一点时间考虑成本更低的控制手段。SysV共享内存是一种有数十年历史的进程间通信机制,在许多现代系统上,已经没有任何合法程序在使用它。限制或审计其使用,并监控单个进程异常的fork()频率,所花费的成本只是一次安全事件的一小部分。昂贵的控制手段,即迫使攻击者串联漏洞的防护机制,已经通过让攻击变得困难而发挥了作用;而廉价的控制,则是确保没有人有机会尝试。

我的结论是:这件事很重要,但并不是因为OpenBSD的光环褪色了。它之所以重要,是因为它清楚地表明,审计声誉和防护层更像是折旧计划,而不是安全保证。有些漏洞比你的一些工程师年龄还大,却仍在支撑你的内核。本周期就打补丁,重新排定本地提权的优先级,并停止把"经过审计"当作一个已完成的状态来买单,它其实是一份需要持续付费的订阅。

来源

本文参考了以下报道与公告: