网络安全研究机构 Calif 近期披露了名为 Big Fish 的内核漏洞利用链,其源头可追溯至约 37 年前 早期 BSD 源码中的一行定义。通过组合这个共享内存计数器溢出与内核越界写,研究人员在完全不依赖信息泄露的前提下,一举击穿了 OpenBSD 标志性的内核随机化链接机制与硬件级防护,直接获得最高管理员权限。
这起事件击碎了长期以来针对 Unix 衍生系统的防御迷信。很多人认为只要开启内核地址随机重排与芯片级执行防护,没有内存泄漏漏洞就绝无可能完成提权。事实恰恰相反:当内核堆分配算法与关键分页基址保持强确定性时,再严密的代码随机化也只是一层薄纸。
37年的一行short,击穿OpenBSD的安全神话
在早期 BSD 源码中,SysV 共享内存段的挂载计数器被定义为一个有符号 16 位整数,最大上限仅为 32,767。研究人员 Andrew Griffiths 发现,只需在用户态映射 127 个 SysV 共享内存段后释放,并连续 fork 259 个子进程,便能驱动这一计数器跨过极值,直接翻转为负数 -32,643。

计数器翻转后,攻击者借助系统调用 IPC_RMID 赢得条件竞争,触发释放后引用(Use-after-Free)。为了稳定控制内核堆内存排布,利用链直接读取系统指标 sysctl(KERN_MALLOCSTATS) 中的总分配量与空闲量数据,完成精密的堆修剪。随后,攻击者通过将网络套接字的组播选项 IP_MULTICAST_TTL 设为 2,诱导内核申请一个 16 字节的组播控制块,触发一次边界为 -1 的内核越界写。
更为致命的是写入原语的触发方式。内核在移除队列元素时调用宏 TAILQ_REMOVE,由于缺乏对双向链表前后指针有效性的校验,攻击者直接构造了任意地址写。借此,两次原子的递减操作精确改写了自引用页表条目的属性位,将原本属于用户态的受控页面直接提升为特权级可访问,彻底绕过了硬件级的访问防护。
堆分配的确定性:为什么KARL未能挡住攻击
OpenBSD 历来享有极高的安全声誉,其自创的内核地址随机链接机制(KARL)会在系统每次重启时重新链接内核镜像,打乱函数与静态变量的位置。同时,系统还全面启用了芯片层级的保护,禁止内核态随意读写或执行用户空间数据。

然而,随机重排的只有静态代码和编译期数据。OpenBSD 内核的引导期分配器与最佳匹配分配器,其算法本身不具备基址随机化能力。
动态分配的确定性,让所有静态代码重排的防线在物理上彻底落空。
在同一套物理硬件上,系统每次启动时各底层内核对象的排布顺序几乎完全一致。攻击者根本不需要先利用内存泄漏漏洞刺探内核地址,就能精准命中网络虚拟接口 lo0 和加密接口 enc0 内部的控制指针。攻击链利用前者改写函数指针劫持执行流,利用后者在提权完成后恢复现场,整个过程没有任何随机碰撞或暴力猜测。
这一利用方式点出了系统安全的深层盲区:自引用页表(PTE)映射基址被硬编码在固定的虚拟地址空间内。只要攻击者掌握了单次写原语,就能通过精确计算算出需要篡改的物理页表条目地址,将硬件防护的边界彻底撕开。
防御机制的虚实:从链表校验到内核基址随机化
在研究成果正式公开前,OpenBSD 官方已分步发布修复更新。2026 年 9 月 14 日发布的补丁修补了整数溢出并补充了匿名页边界校验;9 月 30 日发布的更新则正式将挂载计数器由有符号 16 位扩容为 64 位无符号整型,从根本上消除了计数翻转利用链。

然而在官方发布的补丁勘误摘要中,这一改动仅被定性为避免整数溢出的可靠性修复。常规运维人员若只查看官方摘要,很难意识到这个看似细小的改动背后,潜藏着一条能够穿透现代芯片级防御的完整提权链条。
- 风险.若仅修补表面漏洞而不推进架构随机化,内核堆分配的强确定性仍会被后续未知的写原语反复利用。
跨系统对比进一步显现出基础安全设施的参差。FreeBSD 与 NetBSD 在源码树的队列实现中虽然引入了安全解链检查,但经核查,FreeBSD 生产环境发行的内核默认并未强制开启这类断言,校验逻辑依赖于调试内核选项。相比之下,Linux 内核通过配置项引入了硬化的链表防护,并利用分配器随机构造对抗堆喷排布。
彻底解决确定性利用并非没有工程解法。OpenBSD 下一步最具现实意义的防御改动,是在系统引导期利用硬件真随机数随机挑选自引用页表的插槽,将硬编码基址打散到以 512GB 为步长的动态区间,同时推进引导期分配器随机化。在漏洞挖掘门槛因自动化技术快速下降的当下,修补单个短整型代码只是治标,打破整个内核运行时的确定性布局,才是防御体系能否立足的核心变量。
