记一次Kubernetes“僵尸”挖矿病毒的排查与歼灭全录

摘要

本文复盘了一次真实的 Kubernetes 生产环境安全事故。攻击者利用了极其隐蔽的 RBAC 配置错误(匿名用户绑定管理员权限),在核心的 kube-systemkube-node-leasekube-public 命名空间下部署了伪装性极强的恶意 DaemonSet 和 Deployment。

病毒利用 quay.io/pubproxy/pause 恶意镜像,伪装成 K8s 基础架构容器,在全集群节点上疯狂占用资源挖矿。

本文将带你重走排查之路:从 CPU 报警、与"不死"容器的拉锯战,到最终揪出那个致命的 anonymous-admin 绑定。


一、 突发:CPU 飙升与诡异的"系统进程"

一切始于凌晨的监控告警。安全中心突然发出红色警报,集群内的多台 Worker 节点 CPU 占用率瞬间飙升至 99%

1. 异常现象

登录到其中一台受损节点(Node-1),top 命令赫然显示一个名为 pause 的进程占据了榜首。

告警详情:

  • 样本家族Miner:Linux/BitCoinMiner.gyf
  • 进程路径/usr/local/bin/pause
  • 执行命令./pause

2. 初步困惑

熟悉 Kubernetes 的人都知道,pause 容器(Infra Container)是 K8s Pod 的网络和存储"挂架",它的逻辑非常简单,通常处于休眠状态,CPU 占用率应该接近于 0

一个疯狂消耗 CPU 的 pause 进程?这绝对不是正经的 K8s 组件。这就像是一个穿着警察制服的暴徒。


二、 深入:剥开伪装的外衣

1. 节点层面的取证

为了搞清楚这个进程的来历,我在节点上查看了进程树:

bash 复制代码
# 查看进程树
pstree -p 1009349

结果显示,这个挖矿进程 PID 1009349 的父进程是一个 entrypoint.sh 脚本,而这个脚本运行在一个 Docker 容器中。进一步检查该容器的信息:

bash 复制代码
crictl inspect <Container-ID>

关键发现:

  • 镜像名称quay.io/pubproxy/pause:latest
  • 注意 :官方或阿里云的 pause 镜像通常是 registry.k8s.io/pauseregistry.aliyuncs.com/google_containers/pausepubproxy 这个名字本身就透着一股可疑的味道。

2. 发现"内鬼" Pod

通过容器 ID 反查 Kubernetes Pod,结果让人背脊发凉。这些恶意容器并非孤立存在,而是归属于 Kubernetes 管理的 Pod。

bash 复制代码
kubectl get pods -A | grep -E 'api-proxy|kube-controller-scheduler|nginx-deploys|kube-publice'

输出中混杂着大量看似极其"正规"的恶意 Pod,分布在多个系统命名空间:

  1. kube-system
    • api-proxy-* (DaemonSet)
    • kube-controller-scheduler-* (DaemonSet) ------ 听起来像是 Controller Manager 和 Scheduler 的合体,极易误导新手。
  2. kube-node-lease
    • nginx-deploys-* (Deployment) ------ 这个命名空间本应只存放 Lease 对象,不应有 Pod!
  3. kube-public
    • kube-publice-* (Deployment) ------ 注意 publice 多了个 'e',典型的拼写混淆。

攻击者的伪装艺术:

  • 利用盲区 :他们将 Pod 部署在 kube-systemkube-node-lease 等系统命名空间,运维人员通常不敢随意清理这里的资源。
  • 名称混淆:使用听起来合法的名称。
  • 镜像伪装 :使用名为 pause 的二进制文件挖矿,企图在进程列表中蒙混过关。

三、 拉锯:杀不死的"僵尸"容器

在找到恶意 Pod 后,我尝试在节点上直接清理。

第一回合:手动删除容器

bash 复制代码
docker rm -f <Malicious-Container-ID>

结果:无效。几秒钟后,一个新的容器凭空出现,继续挖矿。

第二回合:删除本地镜像

bash 复制代码
docker rmi quay.io/pubproxy/pause:latest

结果:失败。提示镜像正被运行中的容器占用。即便强制删除,Kubelet 也会立即重新拉取镜像。

分析

这不仅仅是简单的容器入侵。这些 Pod 的 OwnerReferences 显示它们是由 DaemonSetDeployment 控制的。

K8s 的控制器会不知疲倦地将它们"复活",确保副本数达到预期。


四、 溯源:谁给黑客开了后门?

既然恶意对象是 DaemonSet,那么是谁创建了它们?攻击者是如何拥有 kube-system 的写权限的?

我检查了集群的 RBAC(基于角色的访问控制)配置,试图寻找权限过大的 ServiceAccount 或 User。

bash 复制代码
kubectl get clusterrolebindings -o wide | grep admin

输出结果揭示了那个致命的漏洞:

text 复制代码
NAME              ROLE                        AGE   SUBJECTS
anonymous-admin   ClusterRole/cluster-admin   22d   system:anonymous

真相大白!

  • 绑定名称anonymous-admin
  • 权限cluster-admin(集群最高权限,相当于 Linux 的 Root)
  • 主体system:anonymous(匿名用户)

这意味着,互联网上任何一个人,只要能 ping 通这个集群的 API Server (6443 端口),不需要证书、不需要 Token、不需要密码,就被默认视为管理员,拥有对集群生杀予夺的大权。

攻击路径复盘:

  1. 攻击者扫描全网,发现开放的 6443 端口。
  2. 尝试匿名访问,发现拥有管理员权限。
  3. 利用权限在 kube-system 下创建恶意 DaemonSet,在 kube-node-lease 下创建恶意 Deployment。
  4. 控制器指挥所有节点拉取恶意镜像进行挖矿。

五、 歼灭:雷霆手段与清理流程

找到根源后,修复工作必须迅速且彻底。

1. 堵住漏洞(关门)

立刻删除那个致命的 ClusterRoleBinding:

bash 复制代码
kubectl delete clusterrolebinding anonymous-admin

此时,攻击者已经失去了对集群的控制权。

2. 清除毒源(斩首)

在 Master 节点删除所有恶意的控制器。这会触发级联删除,K8s 会自动向所有节点发送停止恶意 Pod 的指令。

bash 复制代码
kubectl delete daemonset api-proxy -n kube-system
kubectl delete daemonset kube-controller-scheduler -n kube-system
kubectl delete deployment nginx-deploys -n kube-node-lease
kubectl delete deployment kube-publice -n kube-public

3. 节点扫尾(清场)

虽然控制层面删除了,但为了防止节点上有残留进程或僵尸容器,我们在所有 Worker 节点(Node-1, Node-2, Node-3)上执行了强制清理命令:

bash 复制代码
# 停止并删除所有基于恶意镜像的容器
docker ps -a --filter ancestor=quay.io/pubproxy/pause:latest --format '{{.ID}}' | xargs -r docker rm -f

# 彻底删除本地的恶意镜像
docker rmi -f quay.io/pubproxy/pause:latest

4. 绝杀(占位防御)

为了防止某种未知的机制(如被篡改的 Kubelet 配置)再次尝试拉取该恶意镜像,我们使出了一招"偷梁换柱":

我们在节点上拉取了一个极小的 alpine 镜像,并将其强行标记为恶意镜像的名字。

bash 复制代码
docker pull alpine:latest
docker tag alpine:latest quay.io/pubproxy/pause:latest

这样,即便病毒试图"借尸还魂",它启动的也只是一个只有几 MB 的、人畜无害的 Alpine 容器,而不再是挖矿程序。


六、 总结与安全建议

这次事件给所有 K8s 运维人员敲响了警钟。Kubernetes 的强大也意味着配置错误的代价是巨大的。

入侵指标 (IOCs) 速查:

  • 恶意镜像quay.io/pubproxy/pause:latest
  • 恶意对象
    • DaemonSet: api-proxy, kube-controller-scheduler (in kube-system)
    • Deployment: nginx-deploys (in kube-node-lease), kube-publice (in kube-public)
    • ClusterRoleBinding: anonymous-admin

给你的 K8s 集群做个体检吧:

  1. 立刻检查 RBAC
    • 运行 kubectl get clusterrolebindings
    • 确保没有将 cluster-admin 绑定给 system:anonymoussystem:unauthenticated
  2. 网络隐身
    • 不要让 API Server (6443) 裸奔在公网上。
    • 使用安全组限制特定 IP 访问,或通过 VPN/堡垒机访问。
  3. 关注系统命名空间
    • kube-node-leasekube-public 通常不应包含大量的 Deployment 或 Pod。定期审计这些"角落"。
  4. 镜像准入控制
    • 部署 OPA Gatekeeper 或 Kyverno,禁止从非受信的镜像仓库拉取镜像。

写在最后:安全没有银弹,只有一次次在攻防中筑起的防线。希望这篇复盘能帮助你的集群避开同样的坑。

相关推荐
eventer1234 小时前
在国产ARM64环境下从源码编译Greptime DB及构建Docker镜像实践
数据库·docker·容器
楓叶子5 小时前
K8S部署
云原生·容器·kubernetes
Ribou5 小时前
LDAP安装docker版
运维·docker·容器
一只栖枝5 小时前
K8s 认证级别怎么选?适配不同运维场景
云原生·容器·kubernetes·k8s·cka
gOODiDEA5 小时前
Kubernetes集群的搭建与DevOps实践(上)- 架构设计篇
云原生·kubernetes·devops·架构设计·技术选型
Yeliang Wu5 小时前
k8s上部署open-webUI
云原生·容器·kubernetes·openwebui
NetInside_6 小时前
基于 Gartner 2025 报告:数字体验监测(DEM)核心价值与企业落地指南
运维·云原生
哲Zheᗜe༘6 小时前
K8S-Ingress资源对象
云原生·容器·kubernetes
Yeliang Wu7 小时前
算力自由:用K8s和Ollama打造你的专属AI基础设施
人工智能·容器·kubernetes