一个存在了大约37年的整数溢出,加上一个独立的内核缺陷,串起来就能在OpenBSD 7.9上拿到root。发现并报告这两个漏洞的安全公司Calif,在OpenBSD九月发布补丁后,公开了详细分析和完整的PoC代码。

打开网易新闻 查看精彩图片

这条攻击链绕过了三样东西:OpenBSD的KARL内核地址随机化,以及SMAP和SMEP两项硬件保护。更值得琢磨的是,作为整条链入口的那个整数溢出,当初是被当作可靠性修复发布的,不是安全修复。

两个补丁,两种定性

shmat(4) attach函数里的整数溢出,在errata 018中修复,9月14日发布,定性为可靠性修复。补全整条链的引用计数use-after-free,则在errata 026中单独修复,9月30日发布,定性为安全修复。

两个补丁都能通过syspatch获取,覆盖所有架构。OpenBSD 7.8也拿到了那个整数溢出的修复。截至发稿,两个漏洞都没有分配CVE编号,也没有证据显示它们被用于实际攻击。

一个16位short撑了37年

共享内存的挂接计数器shm_nattch,在1988年的原始BSD源码里被定义为一个有符号16位short,最大值32,767。OpenBSD继承了这个类型,从来没有把它加宽。

攻击方式很直接:映射127个共享内存段,fork出259个进程,把计数器推过上限,回绕成−32,643。随后一个竞态条件触发use-after-free——被破坏的引用计数让内核释放了其他进程仍在使用的内存。

另一处独立的越界写覆盖了内核中的多播成员计数。受影响的socket关闭时,随之而来的递减操作会修改页表项,把一段用户内存映射切换成超级用户可访问,从而绕过SMAP和SMEP。之后,攻击者覆盖一个可预测内核地址上的函数指针,执行自己的代码。

KARL随机了内核,没随机分配器

研究员Andrew Griffiths写道:“OpenBSD的KARL会重新链接内核二进制,随机化函数地址和数据地址,但内核的内存分配器仍然是确定性的。”

启动时分配器和最佳适配分配器都缺少基址随机化,所以早期启动阶段的结构——包括lo0和enc0网络接口处理函数里的函数指针——在同一台硬件上每次重启都落在相同地址。整条攻击链不需要任何信息泄露漏洞。

除了这两个errata,Calif提出了四类加固方向:

  • 内核链表的safe-unlinking(FreeBSD和NetBSD已在用)
  • 页表自引用映射的随机化
  • 内核分配器随机化
  • 基于编译器的边界检查

Calif表示,他们在“今年早些时候”向OpenBSD报告了这些缺陷。PoC利用代码已在GitHub上公开。