逃逸复现
环境准备
我们需要准备一套存在前述三个漏洞的Kata Containers环境,并配置其使用Cloud Hypervisor作为虚拟机管理程序。这里,笔者采用VMWare + Ubuntu18.04 + Docker + Kata Containers 1.10.0作为测试环境。
首先,参照官方文档安装Docker[9]。接着,从Kata Containers官方Github仓库[10]下载1.10.0版本的静态程序包kata-static-1.10.3-x86_64.tar.xz,下载后进行安装即可,具体可参考如下步骤(需要root权限):
1 | !/bin/bash |
安装完成。可以看一下Docker当前配置的runtime是否为Kata Containers:
1 | docker info | grep 'Runtime' |
OK,再尝试使用Kata Containers + Cloud Hypervisor运行一个容器:
1 | docker run --rm -it --runtime="kata-clh" ubuntu uname -a |
可以看到,容器使用的内核版本为5.3.0-rc3,而我们测试环境宿主机的内核版本为4.15.0-117-generic:
1 | uname -a |
这说明我们的环境搭建成功。
我们想要模拟的场景如下:
目标环境是一个提供容器服务(Container-as-a-Service)的云虚拟化平台,使用Kata Containers作为容器运行时。该容器服务的用户能够上传自己的镜像并在云平台上运行一个或多个容器。攻击者首先上传恶意镜像,启动一个容器,污染Kata Containers使用的虚拟机镜像;然后再次启动一个恶意容器,此时,Kata Containers使用被污染的虚拟机镜像创建出一个恶意虚拟机,它会欺骗Kata Containers运行时组件(kata-runtime)将恶意容器根文件系统挂载到云平台宿主机上/bin目录下。管理员在使用/bin目录下的工具时触发反弹shell,攻击者收到反弹shell,实现逃逸。
漏洞利用
下图更清晰地展示了整个逃逸流程:
下面,我们就来逐步看一下。
>>>>
1. 构建恶意kata-agent
结合前面漏洞分析部分可知,要利用好CVE-2020-2026漏洞,就需要在kata-agent的gRPC服务器上做文章。
首先拿到kata-agent的源码并切换到1.10.0版本:
1 | mkdir -p $GOPATH/src/github.com/kata-containers/ |
在grpc.go文件中,找到CreateSandobx函数,其中有一部分代码是用来将宿主机共享目录挂载到虚拟机中的:
1 | mountList, err := addStorages(ctx, req.Storages, a.sandbox) |
共享目录挂载后,我们才能在里边创建符号链接。因此,在上述代码后面添加创建符号链接的代码即可。
这样一来,当kata-runtime向kata-agent发出CreateSandbox指令时,kata-agent将在共享目录内的rootfs位置创建一个符号链接,指向/bin;此后,当kata-runtime向该位置绑定挂载容器根文件系统时,实际的挂载路径将是宿主机的/bin。
除此之外,我们还需要避免kata-runtime在容器生命周期结束时从/bin卸载容器根文件系统。因此,我们需要想办法在卸载操作之前把共享目录中的rootfs位置重新替换为一个正常的目录。我们注意到,kata-runtime在挂载容器镜像后,还会向kata-agent发出CreateContainer指令,因此,我们可以在kata-agent源码grpc.go文件中的CreateContainer函数内添加删除符号链接、创建正常目录的操作,避免/bin挂载点被卸载。
至此,恶意kata-agent编写完成,make构建一下即可。
>>>>
2. 构建恶意镜像kata-malware-image
从上面的流程图中可以发现,攻击者实际上需要先后创建两个恶意容器。为简单起见,我们只构造一个恶意镜像,它需要完成两个任务:
- 在第一个容器启动时,利用CVE-2020-2023和CVE-2020-2025漏洞,将底层虚拟机块设备中的kata-agent替换为攻击者准备好的恶意文件;
- 第二个容器本身不需要做任何事情,但此时由于CVE-2020-2026漏洞的存在,kata-runtime会将容器的根文件系统挂载到宿主机上指定位置(由恶意kata-agent创建的符号链接指定)。因此,镜像中还需要包含反弹shell需要的程序。
第二个任务比较简单,我们只需要在恶意容器的根目录下准备反弹shell程序(建议用C语言编写,另外,网络上有很多反弹shell源码)即可。由于是覆盖到/bin,因此我们可以考虑以/bin下的一些常用命令为反弹shell命名,例如ls等。另外,假如反弹shell程序依赖bash等系统自带shell,那么我们也需要在镜像中准备——一旦/bin被覆盖,/bin/bash及一系列其他shell就不可用了。
第一个任务则稍微复杂一些,需要将上一步中构建好的恶意kata-agent写入底层虚拟机块设备中。我们可以利用现成工具「debugfs」来达到目的。
如「漏洞分析」部分所述,在获取设备号后,直接使用mknod创建设备文件:
1 | mknod --mode 0600 /dev/guest_hd b 254 1 |
接着,就可以用debugfs打开该设备进行操作了(利用漏洞CVE-2020-2023)。默认情况下,直接执行debugfs会进入交互式界面。我们也可以借助它的-f参数,以文件形式给出操作指令。具体操作如下:
1 | /sbin/debugfs -w /dev/guest_hd |
由于CVE-2020-2025漏洞的存在,上述操作会直接将Kata Containers使用的虚拟机镜像中的kata-agent替换为恶意程序,任务完成。
将上述步骤制作成容器镜像即可。
>>>>
3. 向目标环境上传恶意镜像
我们模拟的是针对提供容器服务的云平台场景的攻击,云平台一般会提供上传或拉取镜像的方法。为简单起见,笔者直接在目标主机上构建恶意镜像。
>>>>
4. 发起攻击
万事俱备,只欠东风。攻击者现在只需要做三件事:
- 开启一个监听反弹shell的进程;
- 在目标环境上使用恶意镜像创建一个新容器;
- 在上一容器内的恶意脚本执行完后,继续使用恶意镜像创建第二个容器。
可以编写一个简单的脚本来自动化上述步骤:
如上图所示,攻击成功(覆盖kata-agent可能耗时较久)。此时目标宿主机上的/bin目录已经被恶意镜像的根目录覆盖(绑定挂载)。假设此时管理员登录到了宿主机上,执行了一些常用命令,例如ls:
由于ls已经被替换为恶意程序,此时,攻击者收到了目标宿主机反弹回来的shell:
>>>>
注意事项
- 如果在VMWare中搭建测试环境,使用Kata Containers运行容器前需要配置一下vsock[15]:
1 | sudo systemctl stop vmware-tools |
- 构建恶意镜像时,使用runC构建会比直接在配置好kata-runtime的环境中快很多。
- 事实上,对于攻击者来说,覆盖/bin并非是最好的思路。一方面,他在反弹shell中能够用到的工具会减少——原宿主机上/bin目录下的所有工具都无法使用了;另一方面,攻击者需要管理员的配合(管理员执行ls等命令)才能实现攻击。一种更好的思路是覆盖/lib或/lib64目录并提供恶意的动态链接库[8],这样既不会影响到/bin目录下的工具(严格来说,可能会影响一些使用到动态链接库的程序),又不需要管理员的配合就可实施攻击,因为许多系统进程(以及kata-runtime)都会自动去调用动态链接库中的函数。
漏洞修复
在了解漏洞原理后,修复思路就显而易见了。修复细节不是本文关注的重点,感兴趣的读者可以参考官方仓库[27][28][29][30]。
总结
纵观云计算与虚拟化技术发展可以发现,从虚拟机到容器再到安全容器,每一种技术都曾出现过逃逸情况。笔者相信,未来还会不断有新的逃逸方式出现。当然,我们不会因噎废食,科技的进步会在效率和安全性两方面都带来越来越多的增益。但是另一方面,最小权限、纵深防御等安全最佳实践和原则依然有必要贯穿始终。
致谢
研究过程中,笔者曾就技术细节问题向原漏洞发现者、Palo Alto Networks的高级安全研究员Yuval Avrahami请教,得到其热情友好的帮助,在此向Yuval Avrahami表示真诚的感谢。
参考文献
[1] https://i.blackhat.com/USA-20/Thursday/us-20-Avrahami-Escaping-Virtualized-Containers.pdf
[2] https://katacontainers.io
[3] https://katacontainers.io/learn/
[4] https://gvisor.dev
[5] https://github.com/kata-containers/community/blob/master/VMT/KCSA/KCSA-CVE-2020-2023.md
[6] https://github.com/kata-containers/community/blob/master/VMT/KCSA/KCSA-CVE-2020-2024.md
[7] https://github.com/kata-containers/community/blob/master/VMT/KCSA/KCSA-CVE-2020-2025.md
[8] https://github.com/kata-containers/community/blob/master/VMT/KCSA/KCSA-CVE-2020-2026.md
[9] https://docs.docker.com/engine/install/ubuntu/
[10] https://github.com/kata-containers/runtime/releases/download/1.10.0/kata-static-1.10.0-x86_64.tar.xz
[11] https://nvd.nist.gov/vuln/detail/CVE-2020-2023
[12] https://nvd.nist.gov/vuln/detail/CVE-2020-2024
[13] https://nvd.nist.gov/vuln/detail/CVE-2020-2025
[14] https://nvd.nist.gov/vuln/detail/CVE-2020-2026
[15] https://github.com/teawater/documentation/blob/4eee7346655d9c954ab595c05e9f5dad0f5efeda/VSocks.md#system-requirements
[16] https://github.com/cloud-hypervisor/cloud-hypervisor
[17] https://github.com/kata-containers/documentation/tree/master/design
[18] https://github.com/opencontainers/runtime-spec
[19] https://github.com/kata-containers/kata-containers
[20] https://gvisor.dev/docs/
[21] https://nvd.nist.gov/vuln/detail/CVE-2019-14271
[22] https://www.kernel.org/doc/Documentation/cgroup-v1/devices.txt
[23] https://man7.org/linux/man-pages/man2/mknod.2.html
[24] https://github.com/moby/moby/blob/a24a71c50f34d53710cccaa4d5e5f62169c5e1dc/oci/caps/defaults.go#L4
[25] https://en.wikipedia.org/wiki/Symlink_race
[26] https://github.com/kata-containers/documentation/blob/master/design/architecture.md
[27] https://github.com/kata-containers/agent/pull/792
[28] https://github.com/kata-containers/runtime/pull/2477
[29] https://github.com/kata-containers/runtime/pull/2487
[30] https://github.com/kata-containers/runtime/pull/2713