Big Fish:利用一个 37 年前的内核漏洞拿下 OpenBSD root
来源:calif.io — 2026-10-16
📋 概述
安全公司 Calif 披露了今年年初上报给 OpenBSD 的一组漏洞,其中一处在原始 BSD 源码里可追溯到约 37 年前:共享内存的挂接计数器被定义为有符号 16 位 short。反复 fork 带共享内存挂接记录的子进程会把计数推过 32767 上限并回绕成负数,配合一处内核越界写,最终可形成释放后使用并本地提权到 root,相关修复分别在 errata 026 与 018。文章的利用链相当精细:映射 127 个 SysV 共享内存段并 munmap,fork 259 个进程把挂接计数绕到 -32643;借 sysctl(KERN_MALLOCSTATS) 的指标做堆布局;在一个进程用 IPC_RMID 抢竞态、在辅助进程挂接一个尺寸混淆的段;对齐 IP socket 分配并把 IP_MULTICAST_TTL 设为 2 以留下一个 16 字节的结构体,其最后一个字里打包了两个 16 位计数;触发条件性的 -1 越界写覆盖这两个成员数上限;最后 close() 触发原子递减,把自引用映射从用户态翻成特权态,绕过 SMAP/SMEP 并覆盖可预测的内核指针拿到代码执行。作者还专门讨论了缓解思路,包括在 unlink 前检查双向链表不变量的 safe-unlinking。
🔑 核心要点
- 历史根源:共享内存挂接计数器是 37 年前原始 BSD 源码里的有符号 16 位 short
- 触发方式:反复 fork 让计数超过 32767 上限并回绕为负,配合越界写形成释放后使用,最终本地提权
- 利用链起点是数量堆叠:映射 127 个 SysV 共享内存段后 munmap,再 fork 259 个进程把计数绕到 -32643
- 堆布局靠内核自身指标:使用 sysctl(KERN_MALLOCSTATS) 的 kb_total 等字段判断新页何时被分配
- 关键技巧是把 IP_MULTICAST_TTL 设为非默认的 2,留下 16 字节结构体,其最后一字打包了成员数与上限两个 16 位计数,再用条件性 -1 越界写覆盖它们
- 绕过缓解靠写入位置:把用户态映射翻成特权态以绕过 SMAP/SMEP,再覆盖可预测的内核指针;作者建议实施 safe-unlinking 并在分配器层面增加随机化,因为 KARL 只重排内核二进制,内存分配器仍是确定性的
💡 金句
这是一个「写什么、写到哪」的原语:写来自 TAILQ_REMOVE,写什么是我们被翻成特权态的用户态内存映射,写到哪则是一个可预测的内核指针位置。
👍 0
👎 0
← 返回 Lobsters 首页