Linux内核Cgroup BPF释放后使用漏洞分析与CVE追踪

本文详细分析了Linux内核Cgroup BPF子系统中的释放后使用漏洞,涉及CVE-2020-14356和CVE-2020-25220两个安全漏洞,涵盖漏洞发现过程、技术原理、修复方案及错误补丁的影响。

CVE: 2020-14356 & 2020-25220

作者:pi3

一个Linux内核Cgroup BPF释放后使用漏洞与两个CVE的简短故事

名称: Linux内核Cgroup BPF释放后使用漏洞 作者: Adam Zabrocki (pi3@pi3.com.pl) 日期: 2020年5月27日

背景概述

2019年,Tejun Heo发现了一个与cgroup_bpf生命周期相关的竞争条件问题,可能导致双重释放和其他内存损坏。该漏洞在5.3内核中修复。关于该问题和补丁的更多信息可在此找到: https://lore.kernel.org/patchwork/patch/1094080/

Roman Gushchin在新修复的代码中发现了另一个可能导致释放后使用漏洞的问题。他的报告和修复可在此找到: https://lore.kernel.org/bpf/20191227215034.3169624-1-guro@fb.com/

在修复讨论中,Alexei Starovoitov指出在不持有cgroup_mutex的情况下遍历cgroup层次结构可能很危险: https://lore.kernel.org/bpf/20200104003523.rfte5rw6hbnncjes@ast-mbp/

然而,Roman和Alexei得出结论认为这不应该是个问题: https://lore.kernel.org/bpf/20200106220746.fm3hp3zynaiaqgly@ast-mbp/

不幸的是,还存在另一个与Cgroup BPF释放逻辑相关的释放后使用漏洞。

“新"漏洞 - 技术细节

在LKRG开发和测试期间,我的一个虚拟机在关机过程中生成了内核崩溃。该特定机器当时运行最新的内核(5.7.x),我编译时包含了所有调试信息以及SLAB DEBUG功能。分析崩溃后发现与LKRG无关。后来我确认没有LKRG的内核也会遇到此问题:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
KERNEL: linux-5.7/vmlinux
DUMPFILE: /var/crash/202006161848/dump.202006161848 [PARTIAL DUMP]
CPUS: 1
DATE: Tue Jun 16 18:47:40 2020
UPTIME: 14:09:24
LOAD AVERAGE: 0.21, 0.37, 0.50
TASKS: 234
NODENAME: oi3
RELEASE: 5.7.0-g4
VERSION: #28 SMP PREEMPT Fri Jun 12 18:09:14 UTC 2020
MACHINE: x86_64 (3694 Mhz)
MEMORY: 8 GB
PANIC: "Oops: 0000 [#1] PREEMPT SMP PTI" (check log for details)
PID: 1060499
COMMAND: "sshd"
TASK: ffff9d8c36b33040 [THREAD_INFO: ffff9d8c36b33040]
CPU: 0
STATE: (PANIC)

崩溃发生在函数”__cgroup_bpf_run_filter_skb"中,具体是在这段代码:

1
2
3
4
5
0xffffffff9423e7ee <__cgroup_bpf_run_filter_skb+382>: callq 0xffffffff94153cb0 <preempt_count_add>
0xffffffff9423e7f3 <__cgroup_bpf_run_filter_skb+387>: callq 0xffffffff941925a0 <__rcu_read_lock>
0xffffffff9423e7f8 <__cgroup_bpf_run_filter_skb+392>: mov 0x3e8(%rbp),%rax
0xffffffff9423e7ff <__cgroup_bpf_run_filter_skb+399>: xor %ebp,%ebp
0xffffffff9423e801 <__cgroup_bpf_run_filter_skb+401>: mov 0x10(%rax),%rdi

其中RAX: 0000000000000000。但在SLAB_DEBUG下进行复现测试时,我经常得到RAX: 6b6b6b6b6b6b6b6b。

这表明存在某种释放后使用漏洞。该漏洞可从用户模式触发。

漏洞根本原因

损坏的指针(struct cgroup *)来自这行代码:

1
cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data);

此代码与CONFIG_SOCK_CGROUP_DATA相关。Linux源码在"cgroup-defs.h"文件中有相关注释说明:

1
2
3
4
5
6
7
8
9
/*
 * sock_cgroup_data is embedded at sock->sk_cgrp_data and contains
 * per-socket cgroup information except for memcg association.
 *
 * On legacy hierarchies, net_prio and net_cls controllers directly set
 * attributes on each sock which can then be tested by the network layer.
 * On the default hierarchy, each sock is associated with the cgroup it was
 * created in and the networking layer can match the cgroup directly.
 */

在崩溃中我们可以看到:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
crash> print/a ((struct sock *)0xffff9ce3de26b280)->sk_cgrp_data
$5 = {
  {
    {
      is_data = 0x0,
      padding = 0x68,
      prioidx = 0xe241,
      classid = 0xffff9ce3
    },
    val = 0xffff9ce3e2416800
  }
}

描述的socket保持"sk_cgrp_data"指针,指示其"附加"到cgroup2。然而,cgroup2已被销毁。

漏洞触发场景

  1. 进程创建socket,两者都在某个cgroup v2(非root)中
  2. cgroup BPF仅适用于cgroup2
  3. 在某些时候net_prio或net_cls被使用:
    • 此操作禁用cgroup2 socket匹配
    • 现在,所有相关socket应转换为使用net_prio,sk_cgrp_data应更新
  4. socket被克隆,但对cgroup的引用未被克隆
    • 这实质上将socket移动到新的cgroup
  5. 旧cgroup中的所有任务必须死亡,当这种情况发生时,该cgroup也会死亡
  6. 当原始进程开始"使用"socket时,它可能尝试访问已经"死亡"的cgroup,这实质上生成释放后使用条件

修复与CVE分配

CVE-2020-14356

我决定在2020年7月中旬向Linux内核安全邮件列表报告此问题。Roman Gushchin回复了我的报告,并建议验证当提交ad0f75e5f57c(“cgroup: fix cgroup_sk_alloc() for sk_clone_lock()")应用时是否仍能复现此问题。该提交在我报告前几天合并到Linux内核git源码树。我仔细验证了它确实修复了问题。

之后,Greg KH决定将Roman的补丁反向移植到LTS内核。同时,我决定申请CVE编号(通过RedHat)来跟踪此问题:

  • CVE-2020-14356被分配来跟踪此问题
  • 由于某些未知原因,此漏洞被分类为NULL指针解引用

RedHat正确地将此问题确认为释放后使用漏洞,但在CVE MITRE门户中我们可以看到非常不准确的描述。

CVE-2020-25220

在分析此漏洞期间,我联系了Brad Spengler。当此问题的补丁被反向移植到LTS内核时,Brad注意到它与预先存在的反向移植冲突,并且上游反向移植看起来不正确。

反向移植的补丁缺少检查:

1
2
+       if (skcd->no_refcnt)
+           return;

这可能导致引用计数器错误,并最终再次出现释放后使用。看起来稳定内核的反向移植补丁仍然有错误。

我再次联系RedHat,他们开始为自己的内核提供正确的补丁。然而,LTS内核仍然有错误。我还要求为此问题分配单独的CVE,但RedHat建议我自己完成。

总结

  • 原始问题(由CVE-2020-14356跟踪)影响从4.5+到5.7.10的内核
  • RedHat正确修复了所有他们的内核,并有正确的错误描述
  • CVE MITRE仍然有无效和误导性的描述

错误反向移植的补丁(由CVE-2020-25220跟踪)影响内核:

  • 4.19直到版本4.19.140(不包括)
  • 4.14直到版本4.14.194(不包括)
  • 4.9直到版本4.9.233(不包括)

*grsecurity内核从未受CVE-2020-25220影响

此致, Adam ‘pi3’ Zabrocki

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