Linux内核Cgroup BPF释放后使用漏洞分析(CVE-2020-14356与CVE-2020-25220)

本文详细分析了Linux内核Cgroup BPF子系统中的释放后使用漏洞。作者通过内核崩溃调试发现,当net_prio或net_cls启用时,cgroup2套接字匹配被禁用,但套接字仍可能引用已释放的cgroup BPF数据,导致内核崩溃。文章涵盖漏洞原理、调试过程、补丁分析及两个相关CVE的追踪过程。

CVE-2020-14356与CVE-2020-25220:Linux内核Cgroup BPF释放后使用漏洞

漏洞背景

2019年,Tejun Heo发现cgroup_bpf生命周期存在竞态条件问题,可能导致双重释放和其他内存损坏。该漏洞在内核5.3中修复。随后,Roman Gushchin在新修复的代码中发现另一个可能导致释放后使用漏洞的问题。

漏洞细节

在LKRG开发和测试过程中,作者发现一台使用最新内核(5.7.x)的虚拟机在关机过程中产生内核崩溃。分析表明该崩溃与LKRG无关,且未加载LKRG的内核同样会出现此问题。

崩溃发生在__cgroup_bpf_run_filter_skb函数中:

1
0xffffffff9423e801 <__cgroup_bpf_run_filter_skb+401>: mov 0x10(%rax),%rdi

此时RAX寄存器值为0x0000000000000000或0x6b6b6b6b6b6b6b6b(SLAB_DEBUG模式下),表明存在释放后使用漏洞。

漏洞触发路径

当进程(如sshd)退出时,会执行以下调用链:

1
2
3
do_exit -> sock_close -> __sock_release -> inet_release -> tcp_close -> 
__tcp_push_pending_frames -> tcp_write_xmit -> __tcp_transmit_skb -> 
__ip_queue_xmit -> ip_finish_output -> __cgroup_bpf_run_filter_skb

问题在于cgroup已被销毁,但套接字仍持有活动客户端引用。

根本原因分析

  1. 进程创建套接字,两者均位于某个cgroup v2(非根cgroup)中
  2. 当net_prio或net_cls被使用时,cgroup2套接字匹配被禁用
  3. 套接字被克隆,但cgroup引用未被克隆,导致套接字移动到新cgroup
  4. 旧cgroup中的所有任务死亡后,该cgroup被销毁
  5. 当原始进程尝试使用套接字时,可能访问已"死亡"的cgroup,导致释放后使用条件

补丁与CVE分配

CVE-2020-14356

原始漏洞影响4.5+至5.7.10的内核版本。Roman Gushchin的提交ad0f75e5f57c(及后续修复14b032b8f8fc)彻底解决了此问题。

CVE-2020-25220

在向后移植补丁时,LTS内核(4.19/4.14/4.9)的修复代码缺少关键检查:

1
2
if (skcd->no_refcnt)
    return;

这可能导致引用计数错误和释放后使用漏洞。该问题已被单独分配CVE-2020-25220。

影响范围

  • CVE-2020-14356:影响4.5+至5.7.10的内核
  • CVE-2020-25220:影响4.19(至4.19.140前)、4.14(至4.14.194前)、4.9(至4.9.233前)的内核

总结

本文详细分析了Linux内核Cgroup BPF子系统中的复杂释放后使用漏洞,展示了从漏洞发现、调试分析到补丁修复和CVE追踪的完整过程。该研究强调了正确向后移植安全补丁的重要性。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计