发布于2025年12月5日12月5日 ## 简介 K8s 安全性依赖于基于角色的访问控制。 在k8s环境中,k8s的‘服务账户令牌’(service account tokens)包含“应用程序用来进行身份验证和执行操作”的权限。本文解释了如何在容器中单独使用这些令牌,研究当今许多流行应用程序所请求的特定权限,并展示如何利用它们的“服务帐户”来进一步危害kubernetes 环境。 本文探讨了3 个部分: - 如何将“k8s 权限”绑定到k8s 的“服务帐户令牌”(service account token)。 - 如何使用这些令牌来渗透k8s 集群。 - 检测并防御“服务帐户令牌”(service account token)被盗。 ## 基础知识 ### k8s架构图 ### k8s 组件 k8s组件通常分为: (1)Master(控制面板)组件:协调集群中的所有活动,负责调度pod、处理进程、与云环境交互、存储所有集群数据,最重要的应该是公共的k8s API,供其他人通过API服务器组件访问。 (2)Node组件(也称为worker节点组件):通过kubelet、kube-proxy和container-runtime运行k8s pod。 ### RBAC-基于角色的访问控制 在k8s 中,RBAC 控制用户或服务帐户可以访问哪些API 资源,以及对这些API 的哪些操作进行授权。 k8s API,RESTful 接口,可通过HTTP 访问。 可以使用命令行工具访问,例如k8s环境下的cURL和命令行工具kubectl。 动作,也称为动词,由: 个get、create、apply、update、patch、delete、proxy、list、watch 组成。 作用:描述动词作用于哪些API。 “角色绑定”(rolebindings):描述哪些用户和服务帐户应用了(哪些)角色。即“用户”和“角色”是绑定的。 应用角色绑定后,该帐户就可以访问与其关联的角色被授权的所有资源。 换句话说,服务帐户或用户与角色关联,并且确定角色对指定API 具有的RBAC 权限。 ### 服务帐户 所谓的“pod权限”是什么? 说明:k8s pod 中的“服务帐户”(服务帐户)与一个角色关联,并且该角色对指定API 具有RBAC 权限。 从k8s v1.22 开始,pod 包含预计卷,其中包含提供API 访问的令牌。如果未指定服务帐户,将为每个Pod 添加一个默认服务帐户。这些服务帐户具有最小的“权限”。 但许多应用程序会创建并安装具有高权限的附加服务帐户。 如何查看服务帐号? 方法一可以从某个pod查看服务账号,路径为:`/var/run/secrets/k8s.io/serviceaccount` 方法2 另一个位置是运行pod的主机上的路径:`/var/lib/kubelet/pods/**/volumes/k8s.io~projected/**` 此位置包含此服务器上安装了服务帐户令牌的每个Pod 的CA 证书、命名空间和令牌。这些文件的目的是使用k8s API 进行身份验证并执行服务帐户已被授权执行的操作。 尽管主机仅包含在其上运行的pod 的服务帐户,但这些主机仍然可以包含数十或数百个服务帐户,只要它们足够大。 ## 研究环境 架构图 说明: 每个pod 或应用程序的所有服务帐户令牌都存储在每个节点上的“/var/lib/kubelet/pods/”文件夹中。 主要组件及其版本: Kubernetes: v1.26.1 Kubeadm: v1.26.1 Containerd: v1.6.15 Calico: v3.25.0 Etcd: 3.5.6-0 攻击思路: 攻击者从一台或多台主机窃取所有服务帐户,然后枚举服务帐户的权限,使用服务帐户对k8s API 进行身份验证,并在集群中升级权限。 ### 权限 k8s RBAC 由什么组成?它由许多为k8s 角色配置的“权限”组成。 本文中描述了以下权限。 ```` 特权动词 Secrets: 获取、列出、观看、创建 Pod/工作负载: 创建、更新、执行 持久卷/VolumeClaims: 创建 角色/集群角色: 升级、绑定 证书签名请求: 创建、更新 令牌请求: 创建 在System:Masters 组中创建服务帐户。 控制ETCD并修改集群数据。 ```` ### 研究环境中存在的应用程序 本次搭建的研究环境中,安装了以下应用程序。 枚举并测试每个请求服务帐户的应用程序,看看该应用程序请求的权限是否可以用于渗透k8s集群。 ### 主机枚举 k8s环境可以通过多种方式实现枚举。 (1)如果攻击者入侵单个pod,他可以在pod中找到这个路径“/var/run/secrets”来访问k8s API并通过身份验证。 (2)攻击者执行“容器逃逸”(container escape)并获得对主机的访问权限。 此时,攻击者可以执行一个简单的shell脚本,如下,kubelet-enum.sh ```` 对于$(find /var/lib/kubelet/pods -name namespace -exec dirname {} \\;) 中的mydir;做 echo \'检查$mydir\' # 设置环境变量 导出证书=$mydir/ca.crt 导出令牌=$mydir/令牌 导出命名空间=$mydir/命名空间 导出apiserver=$(netstat -n | grep 6443 | awk '{ print $5 }' | uniq) echo \'$cert $namespace $token $apiserver\' kubectl --certificate-authority=$cert --token=$(cat $token) --namespace=$(cat $namespace) --server=https://$apiserver auth can-i --list 完成 ```` 该脚本的作用是: 枚举攻击者控制的主机上每个Pod 所拥有的“权限”。 具体说明: 该脚本检查/var/lib/kubelet/pods 中的每个pod,并为CA 证书(ca.crt)、令牌和命名空间创建环境变量。 还可以使用netstat命令查找apisserver地址并创建环境变量。 (您可能需要自行更改具体的apiserver地址。) 最后,使用kubectl auth can-i 命令列出经过身份验证的用户可以在命名空间中执行的操作。 以下是运行脚本后的输出。 Argo-cd集群管理权限只集中在两个重要的列上。简化后的结果是: ```` 资源动词 *.* [*] [*] selfsubjectaccessreviews.authorization.k8s.io [创建] selfsubjectrulesreviews.authorization.k8s.io [创建] ```` 释义: 它解释了可以对哪些资源(即Pod、服务、配置映射、命名空间、机密等)采取哪些操作。 具体来说,第一行,第一个资源的Resources列显示“*.*”,对应的动词是“[*]” 这意味着从受感染主机获得的“服务帐户令牌”(service account token)在其命名空间中拥有完全的管理员权限。 该服务帐户令牌还具有完整的集群管理权限,可以像这样进行验证: `kubectl --certificate -authority=$cert --token=$(cat $token) --server=https://$apiserver auth can-i '\\*' '\\*' –all-namespaces` **攻击者获得了这个服务帐户令牌,导致整个k8s集群被“完全控制”**。攻击者现在可以执行任何所需的“操作”来控制所有k8s 节点并执行攻击后操作:维护权限、提取数据和删除安全保护。 这个令牌从哪里来? 该令牌是在安装Argo-CD 时创建的(通过k8s Helm 包管理器)。 Argo-CD 项目是最流行的k8s GitOps 工具之一,是由云原生计算基金会(CNCF) 制作的项目。 Argo-CD的作用是管理k8s集群,因此它可能需要高权限的服务帐户令牌。 Argo-CD 的例子说明了理解这些情况的必要性: (1)了解各种应用程序需要哪些权限。 (2) 应用程序Pod 被调度到哪些主机? 为什么会有风险? Argo-CD pod 被调度到某个工作节点。 由于没有将Argo-CD pod(具有高权限服务帐户令牌的pod)调度到安全加固节点的步骤,因此Argo-CD pod将与一些“脆弱的pod”在一起。 换句话说,攻击者有可能从“易受攻击的pod”逃逸到主机,然后访问Argo-CD pod(具有高权限服务帐户令牌的pod)。 因此,如果不审查应用程序请求的RBAC权限,也不规划其调度位置,那么权限高的应用程序将被调度到与其他易受攻击的Pod相同的主机上,这是非常危险的。 ### 集群接管 在k8s环境中,攻击者利用RBAC提供的多种方法来扩大控制范围,接管k8s环境。 下面详细介绍如何利用研究环境中应用程序利用的高危权限进行提权、窃取数据、接管集群等操作。 攻击者接管k8s集群的流程如下: ### k8s 的秘密 k8s 机密是用于存储敏感数据的对象。 k8s 机密可以独立于Pod 创建,有助于将密码、令牌、密钥和其他凭据传递给Pod。 秘密类型有哪些? Kubernetes 为一些常见的使用场景提供了几种内置类型。 Kubernetes 执行的合法性检查及其施加的限制因每种类型而异。 “不透明”的秘密是什么? 默认的Secret 类型是“Opaque” Secret,这也是最常见的Secret 类型。 当您使用“kubectl”创建Secret 时,必须使用“generic”子命令来指示您正在创建“Opaque”类型的Secret。例如,以下命令创建一个不透明类型的空Secret: ```` kubectl 创建秘密通用空秘密 kubectl 获取秘密空秘密 ```` 输出是这样的: ```` 姓名类型数据年龄 空秘密不透明0 2m6s ```` “DATA”列显示Secret 中保存的数据条目数。 在此示例中,“0”表示您刚刚创建了一个空的不透明秘密。 ‘Opaque’秘密有什么用? Opaque 可用于定义任意用户数据。 Helm 应用程序创建的大多数机密都是不透明机密。这些不透明的秘密包含许多应用程序的访问凭据。虽然不透明秘密并不总是会导致“集群管理员”,但这些不透明秘密可能允许攻击者获得对k8s 集群中托管的那些应用程序的高权限访问权限。 什么是ServiceAccount 令牌Secret? ServiceAccount token Secret,即`kubernetes.io/service-account-token`secret,用于存储标记ServiceAccount的token凭证。 ServiceAccount token Secret 是一种传统机制,为Pod 提供长期有效的ServiceAccount“凭证”(credentials)。版本“分水岭”: 在k8sv1.22 之前,这些ServiceAccount 令牌密钥是自动创建的,并且寿命很长,不会过期。 在k8s v1.22 及更高版本中,推荐的方法是使用TokenRequest API 获取短暂的、自动轮换的ServiceAccount 令牌。 ServiceAccount 令牌Secret 有何用途? 攻击者可以使用ServiceAccount 令牌Secret 进行权限升级和集群接管。 能够创建机密的攻击者可以为任何“服务帐户”创建机密。 如果攻击者拥有“get”、“watch”或“list” Secrets 权限,则攻击者可以获得该Secret 并使用某个Secret 作为服务帐户的身份验证凭据。 攻击者获取k8s Secret的流程如下: 通过helm 安装的多个应用程序具有“获取”、“列出”和“监视”机密的权限。 如果应用程序拥有秘密的所有“动词”,则意味着也可以创建秘密。 您可以使用以下“.yaml”配置来创建新的ServiceAccount 令牌密钥。必须指定集群中的服务帐户之一,因此选择服务帐户“argocd-application-controller”,因为它具有集群管理权限。 ```` api版本: v1 kind: 秘密 元数据: name: 研究-argo-代币 注释:k8s.io/service-account.name: \'argocd-application-controller\' type: k8s.io/service-account-token ```` 假设攻击者在枚举阶段获得的所有令牌都可以用于创建和列出“服务帐户”(服务帐户)。在这种情况下,攻击者可以为所有服务帐户创建额外的机密,并使用它们进行身份验证,从而有效地赋予他们任何服务帐户的“特权”(这就是为他们创建机密的原因)。 为名为“argocd-application-controller”的服务帐户创建的密钥为该服务帐户提供“cluster-admin”权限。 ### k8s Pod 在k8s 中创建pod 的方法有很多种。各种Pod 创建方法称为“工作负载资源”。这些“工作负载资源”包括Pod、Deployments、ReplicaSet、DaemonSet、Jobs、CronJobs 和ReplicationController。 如果服务帐户令牌有权创建多个工作负载资源中的任何一个,那么这是攻击者尝试权限升级的另一种方式。 当攻击者创建Pod 时,他们可以向其添加特权标志,从Pod 中删除所有“安全保护”,并使攻击者能够访问主机的文件系统。攻击者还可以指定他们想要使用哪个“服务帐户”来让Pod 执行各种命令,例如反弹shell,或者为Pod 指定恶意映像。 从k8s Pod开始,接管集群管理的步骤如下: ```` 1、攻击者获得可以创建pod的sa(如果可以在kube-system命名空间中创建pod就更好了) 2、使用指定的高权限‘服务账号’创建Pod 3. 具有执行权限的攻击者可以进入pod抓取token,或者让pod运行其他命令。 4.攻击者使用新的token进行认证 5.集群管理 ```` 使用以下配置创建pod 可以为集群管理员提供使用argocd-application-controller\'service account\' 并挂载其令牌的功能。 Pod.yaml 配置,使用高权限“服务帐户”: ```` apiVersion: v1 kind: Pod 元数据: 创建时间戳: null 标签: run: 研究舱 name: 研究舱 规格: serviceAccountName: argocd-application-controller automountServiceAccountToken: true 容器: - image: nginx name: 研究舱 资源: {} dnsPolicy: 集群优先 restartPolicy: 始终 状态: {} ```` 攻击者可以执行pod 并获得shell 访问权限,下载kubectl 二进制文件,然后以完整的集群管理员权限从pod 中运行kubectl 二进制文件。可以指定其他服务帐户,攻击者可以测试多个服务帐户,直到获得所需的权限。 ### k8s 卷 k8s 卷可以挂载到主机文件系统上。 攻击者可以通过多种不同的方式在主机文件系统上创建卷并从容器逃逸到主机。 (1) 常见的做法是创建一个pod,并将hostPath 卷指定到主机上的敏感位置,甚至指定到根的文件系统。利用hostPath 进行容器逃逸很常见,因此可以启用许多防御措施来阻止它。 (2) 一种不太常见的方法是创建PeressentVolume 和PersistentVolumeClaim 来执行相同的攻击。持久卷(PV) 是集群中的一块存储,可以由管理员提前准备,也可以使用存储类动态准备。持久卷是集群资源,就像节点是集群资源一样。 PV 持久卷与普通Volume 一样使用卷插件实现,只不过它们具有独立于任何使用PV 的Pod 的生命周期。存储的实现细节都记录在这个API 对象中,无论是NFS、iSCSI 还是其背后的特定于云的存储系统。持久卷声明(PVC)表达了用户对存储的请求。概念上与Pod 类似。 Pod 消耗节点资源,PVC 声明消耗PV 资源。 Pod 可以请求特定数量的资源(CPU 和内存);同样,PVC 声明可以请求特定的大小和访问模式(例如,您可以要求PV 卷可以以ReadWriteOnce、ReadOnlyMany 或ReadWriteMany 模式之一挂载,请参阅访问模式)。 利用PersistentVolumes 作为攻击媒介需要RBAC 权限来创建PersistentVolumes、PersistentVolumeClaims 和Pod 或其他工作负载资源。 一旦创建了挂载了hostPath 的持久卷,并且将持久卷声明绑定到该持久卷,攻击者就可以在pod 配置中指定该卷并获得对主机文件系统的访问权限。然后可以浏览该卷,并且攻击者可以尝试chroot 到该卷,从而更轻松地与主机文件系统交互并运行命令。 ### 角色和“集群角色” 角色和“集群角色”(Clusterroles)是指定用户或“服务帐户”拥有哪些API 资源和动词的对象,因此角色和“集群角色”是攻击者提权的重点。 K8s 有内部防御措施来防止角色的创建或更新以及“角色绑定”。 RBAC API 不允许创建或更新角色或角色绑定,除非用户已拥有角色中包含的所有权限。 如果攻击者获得“服务帐户”令牌,
创建帐户或登录后发表意见