Kubernetes云原生安全渗透学习
Kubernetes云原生安全渗透学习
王先森前言
Kubernetes
简称k8s,是当前主流的容器调度平台,被称为云原生时代的操作系统。在实际项目也经常发现厂商部署了使用k8s进行管理的云原生架构环境,在目前全面上云的趋势,有必要学习在k8s环境的下的一些攻击手法,本文非常适合刚入门或者准备学习云安全方向的安全人员,每个步骤都是亲手复现整理。文中如有错误的地方,还望各位大佬在评论区指正。
Kubernetes用户管理
Kubernetes 集群中包含两类用户:一类是由 Kubernetes管理的service account,另一类是普通用户。
- service account 是由 Kubernetes API管理的账户。它们都绑定到了特定的 namespace,并由 API server 自动创建,或者通过 API 调用手动创建。Service account 关联了一套凭证,存储在 Secret,这些凭证同时被挂载到 pod 中,从而允许 pod 与 kubernetes API 之间的调用。
- Use Account(用户账号):一般是指由独立于Kubernetes之外的其他服务管理的用 户账号,例如由管理员分发的密钥、Keystone一类的用户存储(账号库)、甚至是包 含有用户名和密码列表的文件等。Kubernetes中不存在表示此类用户账号的对象, 因此不能被直接添加进 Kubernetes 系统中 。
k8s访问控制过程
k8s 中所有的 api 请求都要通过一个 gateway 也就是 apiserver 组件来实现,是集群唯一的访问入口。 主要实现的功能就是api 的认证 + 鉴权以及准入控制。
三种机制:
- 认证:Authentication,即身份认证。检查用户是否为合法用户,如客户端证书、密码、bootstrap tookens和JWT tokens等方式。
- 鉴权:Authorization,即权限判断。判断该用户是否具有该操作的权限,k8s 中支持 Node、RBAC(Role-Based Access Control)、ABAC、webhook等机制,RBAC 为主流方式
- 准入控制:Admission Control。请求的最后一个步骤,一般用于拓展功能,如检查 pod 的resource是否配置,yaml配置的安全是否合规等。一般使用admission webhooks来实现
注意:认证授权过程只存在HTTPS形式的API中。也就是说,如果客户端使用HTTP连接到kube-apiserver,是不会进行认证授权
Kubernetes 认证
客户端证书认证
客户端证书认证:X509 是一种数字证书的格式标准,是 kubernetes 中默认开启使用最多的一种,也是最安全的一种。api-server 启动时会指定 ca 证书以及 ca 私钥,只要是通过同一个 ca 签发的客户端 x509 证书,则认为是可信的客户端,kubeadm 安装集群时就是基于证书的认证方式。
user 生成 kubeconfig就是X509 client certs方式。
Service Account Tokens
因为基于x509的认证方式相对比较复杂,不适用于k8s集群内部pod的管理。Service Account Tokens是 service account 使用的认证方式。定义一个 pod 应该拥有什么权限。
service account 主要包含了三个内容:namespace、token 和 ca
- namespace: 指定了 pod 所在的 namespace
- token: token 用作身份验证
- ca: ca 用于验证 apiserver 的证书
Kubernetes 鉴权
K8S 目前支持了如下四种授权机制:
- Node
- ABAC
- RBAC
- Webhook
具体到授权模式其实有六种:
- 基于属性的访问控制(ABAC)模式允许你 使用本地文件配置策略。
- 基于角色的访问控制(RBAC)模式允许你使用 Kubernetes API 创建和存储策略。
- WebHook 是一种 HTTP 回调模式,允许你使用远程 REST 端点管理鉴权。
- node节点鉴权是一种特殊用途的鉴权模式,专门对 kubelet 发出的 API 请求执行鉴权。
- AlwaysDeny阻止所有请求。仅将此标志用于测试。
- AlwaysAllow允许所有请求。仅在你不需要 API 请求 的鉴权时才使用此标志。
可以选择多个鉴权模块。模块按顺序检查,以便较靠前的模块具有更高的优先级来允许 或拒绝请求。
从1.6版本起,Kubernetes 默认启用RBAC访问控制策略。从1.8开始,RBAC已作为稳定的功能。
想了解更多RBAC的内容可以参考:使用 RBAC 鉴权 | Kubernetes
实验环境
- 3台vm,每台至少2g。
- OS: CentOS 7.9
- kubernetes:v1.23
Ansible自动化部署K8S集群 或其他安装方法
一个集群包含三个节点,其中包括一个控制节点和两个工作节点
主机名 | 角色 | IP | 安装软件 |
---|---|---|---|
k8s-master.boysec.cn | 代理节点 | 10.1.1.100 | etcd、kueblet、kube-porxy、kube-apiserver、kube-controller-manager、kube-scheduler、Containerd |
k8s-node01.boysec.cn | 运算节点 | 10.1.1.120 | etcd、kueblet、kube-porxy、Containerd |
k8s-node02.boysec.cn | 运算节点 | 10.1.1.130 | etcd、kueblet、kube-porxy、Containerd |
hacker | 攻击主机 | 10.1.1.11 | kueblet |
k8s环境中的信息收集
信息收集与我们的攻击场景或者说进入的内网的起点分不开。一般来说内网不会完全基于容器技术进行构建。所以起点一般可以分为权限受限的容器和物理主机内网。
在K8s内部集群网络主要依靠网络插件,目前使用比较多的主要是Flannel和Calico
主要存在4种类型的通信:
- 同一Pod内的容器间通信
- 各Pod彼此间通信
- Pod与Service间的通信
- 集群外部的流量与Service间的通信
当我们起点是一个在k8s集群内部权限受限的容器时,和常规内网渗透区别不大,上传端口扫描工具探测即可。
在k8s环境中,内网探测可以高度关注的端口:
1 | kube-apiserver: 6443, 8080 |
配置不当引发的组件安全问题
API Server未授权访问
如未将system:anonymous
用户绑定到cluster-admin
用户组,从而使6443端口的利用要通过API Server的鉴权,直接访问会提示匿名用户鉴权失败:
运维人员配置不当,将system:anonymous
用户绑定到cluster-admin
用户组,从而使6443 端口允许匿名用户以管理员权限向集群内部下发指令。
1 | kubectl create clusterrolebinding system:anonymous --clusterrole=cluster-admin --user=system:anonymous |
再次访问:
攻击方法
1 | 查看default名称空间的Pod GET |
解决问题
经过查询发现:现在可惜也不行了/(ㄒoㄒ)/~~
对于websocket连接,首先进行http(s)调用,然后是使用HTTP Upgrade标头对websocket的升级请求。
curl/Postman不支持从http升级到websocket。因此错误。
解决办法就是用wscat工具发送包:npm install -g wscat
不过这里还是没解决这个问题,
尝试新的方法:
1 | kubectl --insecure-skip-tls-verify -s https://10.1.1.100:6443 get pods |
采用CDK攻击
CDK(Container DucK)是一款为容器环境定制的渗透测试工具,在已攻陷的容器内部提供零依赖的常用命令及PoC/EXP。集成Docker/K8s场景特有的 逃逸、横向移动、持久化利用方式,插件化管理。
项目地址:https://github.com/cdk-team/CDK/
1 | 利用cdk工具通过"system:anonymous"匿名账号尝试登录 |
Kubelet 未授权访问
Kubelet API 一般监听在2个端口:10250、10255。其中,10250端口是可读写的,10255是一个只读端口。
最常见的未授权访问一般是10255端口,但这个端口的利用价值偏低,只能读取到一些基本信息。
- Kubelet 是节点代理(Api 默认端口 10250),运行在集群的每个节点上,负责向 API Server 注册所在的节点。目前在k8s默认的安全配置下,Kubelet API是需要安全认证的。
- Kubelet 的配置文件是
/opt/kubernetes/cfg/kubelet-config.yml
- kubelet 是真正去运行 Pod 的组件,可以通过调用 API 来改变集群状态。例如启动和停止 Pod
既然 Kubelet 这么重要,那么如果存在未授权访问的问题,攻击者就可以向某个节点的 Kubelet 下发命令从而控制当前的所有的 Pod 有可能进一步控制整个集群。
复现步骤
- 可以直接控制该node下的所有pod
- 检索寻找特权容器,获取 Token
- 如果能够从pod获取高权限的token,则可以直接接管集群。
通过ansible
脚本安装的访问 https://10.1.1..100:10250/pods
地址会显示没有权限
修改配置文件
1 | vim /opt/kubernetes/cfg/kubelet-config.yml |
攻击方法
命令执行
可通过如下请求对指定 pod 进行利用
1 | curl -XPOST -k https://IP:10250/run/<NameSpace>/<PodName>/<appName> -d "cmd=id" |
凭证窃取
一个 pod 与一个服务账户相关联,该服务账户的凭证(token)被放入该pod中每个容器的文件系统树,在 /var/run/secrets/kubernetes.io/serviceaccount/token
如果服务账号(Service account )绑定了 cluster-admin (即集群的 admin 权限我们可以对所有namespace下实例进行操作) ,那么我们就可以通过 token 来进行一系列的操作
1 | # 获取whoami |
如果挂载到集群内的token具有创建pod的权限,可以通过token访问集群的api创建特权容器,然后通过特权容器逃逸到宿主机,从而拥有集群节点的权限
1 | [root@hacker ~]# kubectl --insecure-skip-tls-verify=true --server="https://10.1.1.100:6443" --token="eyJhbGci......" get pod |
容器逃逸
1 | [root@hacker ~]$ cat > taoyi.yaml <<EOF |