深入分析CVE-2022-0492漏洞

匿名者  120天前

概述

24日,Linux披露了CVE-2022-0492漏洞,这是在内核中新发现的一个权限提升漏洞。据公告称,该漏洞是由于control groupscgroups)中的一个逻辑错误所致;cgroupsLinux的一个模块,同时也是容器的基本构建块。它是近期发现的最容易利用的Linux权限提升漏洞之一:Linux内核错误地将特权操作暴露给非特权用户。

幸运的是,大多数容器环境中的默认安全强化措施足以防止容器逃逸。例如,使用AppArmorSELinux运行的容器就能面免受该漏洞的威胁。也就是说,如果您在没有采取基于最佳实践的安全加固的环境下运行容器,或者使用了额外的特权的话,那就很可能会受到该漏洞的攻击。为此,我们将为读者详细介绍哪些容器配置是易受攻击的,并提供有关如何测试容器环境是否易受攻击的说明。

1.png

除了容器之外,该漏洞还允许受限的root用户主机进程或具有CAP_DAC_OVERRIDE特权的非root用户的主机进程提升权限并获得所有特权。这就使得攻击者能够绕过某些服务使用的某些安全加固措施——这些措施会降低权限,以便在受到攻击时将损失降到最低程度。

我们强烈建议用户升级到已经修复该漏洞的内核版本。对于那些正在运行的容器,可以启用Seccomp,并确保启用了AppArmorSELinuxCVE-2022-0492是近几个月来曝光的第三个允许恶意容器逃逸的内核漏洞。对于这些漏洞,只要通过SeccompAppArmorSELinux来保护容器,就能够防止容器逃逸。

关于Cgroups的背景知识

Control Groupscgroups)是一个Linux特性,允许管理员限制、记录和隔离一组进程所使用的资源。Linux支持两种cgroup架构,分别名为cgroup v1cgroup v2。实际上,这个新的漏洞只影响cgroup v1架构,而该架构是目前使用的最广泛的架构。在本文下面的内容中,我们将只针对cgroup v1进行介绍。

Control Groups是通过cgroupfsAPI进行管理的,这是一个作为文件系统暴露的管理API,通常挂载在/sys/fs/cgroup下。通过利用已挂载的cgroupfs创建和写入文件和目录,管理员就可以创建相应的cgroup,并控制施加在cgroup上的约束,或向某个cgroup添加进程,等等。

Cgroups被划分为各个子系统,每个子系统用于配置对不同资源的访问控制。例如,内存cgroup可以限制进程集合的内存消耗;而设备cgroup则定义了cgroup中的进程可以访问哪些设备(例如,硬盘驱动器或鼠标)。此外,还有控制其他资源其他子系统,例如块设备IOCPU和远程直接内存访问(RDMA)

所有的子系统通常都挂载在/sys/fs/cgroup/<subsystem>(即子系统的rootcgroup)。root cgroup下的任何子目录都表示一个新的子cgroup。例如,Docker容器通常是/docker/<ctr-id>cgroup的一部分,可以在主机的/sys/fs/cgroup/<subsystem>/docker/<ctr-id>路径上找到。图1显示了Docker容器的cgroup成员,图2显示了Kubernetespodcgroup成员。

1.png

1  Docker容器中进程的cgroup成员。

1.png

2  Kubernetes pod中进程的cgroup成员。

从上面的截图中可以看出,并非所有的主机都被配置为支持相同的子系统。DockerKubernetes也有不同的cgroup配置,所以,如果读者的主机或集群上的容器中没有发现上面显示成员,也是非常正常的现象。

漏洞的成因

cgroups v1的特性之一便是release_agent文件。管理员可以借助该文件来配置“release agent”程序,该程序将在cgroup中的进程终止时运行。这是通过将所需的释放代理(releaseagent)路径写入release_agent文件来完成的,具体如下所示:

$ echo /bin/my-release-agent >/sys/fs/cgroup/memory/release_agent

需要注意的是,release_agent文件仅在root cgroup目录中可见,并且只影响其子cgroups。通过写入notify_on_release文件,可以将每个子组配置为(在其中一个进程终止时)触发或不触发释放代理。以下命令将为a_child_cgroup cgroup启用notify_on_release功能:

$ echo 1 >/sys/fs/cgroup/memory/a_child_cgroup/notify_on_release

当进程结束时,内核将检查其cgroups是否启用了notify_on_release:如果启用了,则生成配置的release_agent二进制文件。这时,发布代理将以尽可能高的权限运行:一个具有初始命名空间中所有权限的root进程。因此,配置发布代理被认为是一个特权操作,因为它允许用户决定哪个二进制文件将以完整的root权限运行。

CVE-2022-0492漏洞的成因,在于缺失相应的验证:Linux没有检查设置release_agent文件的进程是否具有管理权限(即CAP_SYS_ADMIN权限)。此外,我们还可以通过CVE-2022-0492的补丁代码(以下第2-8行)来了解该漏洞:

@@ -549,6 +549,14 @@ static ssize_tcgroup_release_agent_write(struct kernfs_open_file *of,

+       /*

+       * Release agent gets called with all capabilities,

+       * require capabilities to set release agent.

+       */

+       if((of->file->f_cred->user_ns != &init_user_ns) ||

+           !capable(CAP_SYS_ADMIN))

+               return-EPERM;

利用该漏洞的先决条件

如前所述,只要攻击者能够对release_agent文件执行写操作,就能强制内核以提升后的权限执行指定的二进制文件,进而控制整个机器。那么,哪些用户可以在易受攻击的计算机上对release_agent文件执行写操作呢?虽然内核不会显式地检查写入进程的特权,但是,正常的文件所有权和权限语义仍然还是适用的。

因为Linuxrelease_agent文件的所有者设置为root,所以,只有root用户(或者可以通过CAP_DAC_OVERRIDE权限绕过文件权限检查的进程)才能对该文件执行写操作。因此,该漏洞仅允许root进程提升权限。

乍一看,只能由root用户才能利用的权限提升漏洞,听起来似乎很奇怪。在过去,这不会被认为是一个安全问题。但是今天,以root用户身份运行并不一定意味着对机器的完全控制:在root用户和包括功能、命名空间和容器在内的完全权限之间,实际上还存在一个灰色区域。在root进程无法完全控制计算机的情况下,CVE-2022-0492将会成为一个严重的安全漏洞。

纵深防御的胜利——容器逃逸先决条件

实际上,并不是所有的容器都能利用CVE-2022-0492漏洞实现逃逸;相反,只有具有特定的安全配置文件的容器才能执行必要的逃逸步骤。

由于在容器内cgroup的挂载是以只读方式进行的,因此,不允许对它们托管的release_agent文件执行写操作。所以,想要利用CVE-2022-0492漏洞的恶意容器,必须挂载另一个可写的CGroupFS

 1.png

容器内的Cgroupfs挂载是只读的(“RO”)

另外,AppArmorSELinux都会阻止安装操作,这意味着使用两者运行的容器都会受到相应的保护。如果没有两者的保护,容器就可以通过以下两种方式之一挂载cgroupfs:滥用用户命名空间或CAP_SYS_ADMIN功能。

基于用户命名空间的容器逃逸

要想挂载cgroupfs,必须在托管当前cgroup命名空间的用户命名空间中拥有CAP_SYS_ADMIN权限。但是在默认情况下,容器说在非CAP_SYS_ADMIM权限的情况下运行的,因此,无法在初始用户命名空间中挂载cgroupfs。但是通过unshare系统调用,容器可以创建新的usercgroup命名空间,并且在这些空间中拥有CAP_SYS_ADMIN权限,所以,也就可以挂载cgroupfs了。

1.png

容器创建一个新的用户命名空间,并且在其中拥有CAP_SYS_ADMIN权限

当然,并不是每个容器都可以创建新的用户命名空间——底层主机就必须启用非特权用户命名空间,例如,Ubuntu最新版本的默认设置就是如此。由于Seccomp会拦截unshare系统调用,因此,只有在没有启用Seccomp的环境中运行的容器才能创建新的用户命名空间。下图显示是容器在没有启用SeccompAppArmorselinux的环境中的运行情况。

1.png

容器在新的用户和cgroup命名空间中挂载内存cgroup

在上面的截图中,容器成功地挂载了一个内存cgroup,但是您可能已经注意到了——release_agent文件并没有出现在已挂载的目录中!

如前所述,release_agent文件仅在root cgroup中可见。注意,在cgroup命名空间中挂载cgroupfs时,挂载的是您所属的cgroup,而不是rootcgroup。如果观察图1,就会发现,容器并不是在root用户的内存cgroup中运行的,而是在子cgroup://docker/<id>中运行的。为了使release_agent文件在cgroup挂载中可见,容器必须在子系统的root cgroup中运行。

同样,在图1中,您将看到Docker是在root RDMA cgroup中运行容器的。如果我们对RDMAcgroup执行相同的命令,那么release_agent文件也会变成是可见的。

1.png

容器,它在新的用户和cgroup命名空间中挂载了rootRDMA cgroup

为了利用这个安全漏洞,我们需要在release_agent文件中写入一个恶意的释放代理。如上图6所示,由于该文件的属主为root,所以,只有root容器进程才能够设置该释放代理。图7显示了容器设置释放代理的情况,而图8则显示了非root容器设置释放代理失败的情形。

1.png

7  root容器在设置释放代理

1.png

root容器无法设置释放代理

逃逸的最后一步,就是调用配置好的release_agent,需要说明的是,这一步不需要任何权限。由于这一步总是可以做到的,它对环境是否容易受到CVE-2022-0492的攻击没有影响,所以,我们决定把它排除在外。在下图中,我们为读者展示了一个完整的exploit

1.png

利用CVE-2022-0492漏洞,通过用户命名空间实现容器逃逸

基于CAP_SYS_ADMIN 的容器逃逸

如果容器被授予CAP_SYS_ADMIN权限,则可以直接利用该漏洞,而无需创建新的用户和cgroup命名空间。使用CAP_SYS_ADMIN功能运行的容器可以挂载cgroupfs,系统不会进行任何干涉。另外,现在大多数容器都是在没有cgroup命名空间的情况下运行的,这意味着挂载的cgroup隶属于rootcgroup,并且有权读写release_agent文件。

1.png

10  在初始的cgroup命名空间中,无论容器的cgroup是谁,只要挂载cgroupfs,就会挂载rootcgroup

即使有CAP_SYS_ADMIN权限,AppArmorSELinux仍然拦截相应的挂载操作,因此,在启用了它们的系统中,容器是无法利用CVE-2022-0492漏洞实现逃逸的。图11显示了在没有启用AppArmorSELinux的情况下运行的容器,同时,该容器具有CAP_SYS_ADMIN权限,最终,该容器利用CVE-2022-0492漏洞成功实现了逃逸。

1.png

11  利用CVE-2022-0492漏洞通过CAP_SYS_ADMIN权限实现容器逃逸

如何进行自检

总而言之,如果一个容器满足下列条件,就可能发生逃逸:

  •     作为root用户运行,或者没有提供no_new_privs选项;以及
  •     没有启用AppArmorSELinux;以及
  •     没有启用Seccomp;以及
  •     在一个启用非特权用户命名空间的主机上运行;以及
  •     隶属于root v1 cgroup

或者,如果它在运行过程中满足下列条件:

  •     具有CAP_SYS_ADMIN权限;以及
  •     没有启用AppArmorSELinux;并且
  •     没有创建cgroup命名空间,或隶属于rootv1 cgroup

我们的安全研究人员创建了一个脚本,可以用来测试容器环境是否含有CVE-2022-0492的漏洞。为了测试自己的环境,您可以部署一个新的容器,然后直接运行我们的us-central1-docker.pkg.dev/twistlock-secresearch/public/can-ctr-escape-cve-2022-0492镜像即可,它已经被配置为运行该脚本,打印检测结果,并自动退出。另外,您也可以使用该工具的存储库中的Docker文件,以构建和运行自己的镜像。

需要注意的是,我们不建议在一个现有的容器中运行该脚本。这是因为该容器可能没有该脚本所依赖的实用程序,或者没有使用正确的版本,而这可能导致不准确的结果。

1.png

12  使用脚本来测试哪些容器配置容易受到CVE-2022-0492漏洞的影响

缓解措施

目前,CVE-2022-0492已经在最新的Linux版本上得到了修复;我们鼓励所有用户升级到各自发行的最新内核版本。

为了在无法升级的情况下防范恶意容器,用户可以启用以下缓解措施之一:

  •     启用AppArmorSELinux。更多信息请参见Kubernetes指南。
  •     启用Seccomp。更多信息请参见Kubernetes指南。请注意,这并足以抵御以CAP_SYS_ADMIN权限运行的容器。

为了防止恶意的主机进程提升权限,在无法升级的情况下,用户可以启用以下两种缓解措施。请注意,这个解决方案在重启后将会失效,需要重新启用相关措施:

  •     使用以下命令禁用非特权用户命名空间:sudosysctl -w kernel.unprivileged_userns_clone=0。需要注意的是,系统中的某些服务,如Podman,依赖于非特权用户命名空间,因此,采用此措施可能导致它们无法按预期工作。
  •     使用下面的脚本防止进程在任何cgroupmount中设置release_agent。该脚本通过只读型绑定挂载来屏蔽所有release_agent文件。如果您的系统将cgroup挂载在一个自定义的路径上(默认是/sys/fs/cgroup),需要将该路径作为一个参数提供给该脚本。

#!/bin/bash

set -e

mask_dir=/var/lib/cve_2022_0492_release_agent_mask

cgroup_dir=/sys/fs/cgroup

if [ ! -z "$1" ]; thencgroup_dir=$1 ; fi

echo "[+] Creating mask at$mask_dir/mask"

sudo mkdir -p $mask_dir

sudo mount -t tmpfs release_agent_mask$mask_dir

sudo touch $mask_dir/mask

sudo mount -o remount,ro $mask_dir

for release_agent in $(find $cgroup_dir-name 'release_agent') ;do

   echo "[+] Mounting read-only mask over $release_agent"

   sudo mount --bind $mask_dir/mask $release_agent

done

小结

CVE-2022-0492是一个可实现容器逃逸的Linux漏洞。幸运的是,遵循最佳实践的环境可以免受这个漏洞的影响。但是,对于安全控制措施不严、托管不受信任或公开暴露的容器的环境来说,它们所面临的风险还是挺高的。像往常一样,我们建议大家将主机升级到一个已经修复该漏洞的内核版本。

 

原文地址:https://unit42.paloaltonetworks.com/cve-2022-0492-cgroups/

最新评论

昵称
邮箱
提交评论