PoC:通过 CVE-2026-31431 页缓存损坏 + 共享镜像层,实现完全非特权容器逃逸至 Kubernetes 节点级代码执行。已在阿里云 ACK、Amazon EKS 和 Google GKE 上验证。
一个概念验证(PoC),演示了完全无特权的容器如何通过共享容器镜像层利用 CVE-2026-31431 Linux 内核页缓存损坏漏洞,在 Kubernetes 上实现节点级代码执行。
核心攻击原语是:任何与攻击者控制的容器共享镜像层的特权 DaemonSet 都可被武器化,用于容器逃逸。本 PoC 以 kube-proxy 作为具体示例,但该技术可推广到集群中的任何特权工作负载。
已在阿里云 ACK、Amazon EKS 和 Google GKE 上验证——一个无特权 Pod 通过特权 kube-proxy DaemonSet 向主机文件系统写入 [*] success:
| 阿里云 ACK (kernel 6.6.88) | Amazon EKS (kernel 6.12.79) | Google GKE (kernel 6.12.68) |
|---|---|---|
![]() | ![]() | ![]() |
免责声明: 本仓库仅用于教育和防御目的。请仅在您拥有或已获明确授权测试的系统上使用。
CVE-2026-31431(“Copy Fail”)是 Linux 内核中页缓存写时复制(Copy-on-Write, CoW)路径上的一个漏洞。通过 AF_ALG splice 竞态,无特权进程可以破坏只读文件的页缓存页。这种破坏会持续存在于内核页缓存中,并且对之后读取或执行该文件的每个进程都可见——包括其他容器或主机上的进程。
关于原始漏洞的完整细节,请参阅 copy.fail。
该攻击利用了 Kubernetes 集群中通常同时存在的三个特性:
privileged: true、hostNetwork: true、广泛的 capabilities 等),这些 DaemonSet 会定期执行其镜像中的二进制文件。当这些条件同时具备时,无特权 Pod 可以破坏共享镜像层中的二进制文件,而同一节点上的特权 DaemonSet 会在不知情的情况下以其提升的权限执行被破坏的二进制文件——从而实现完整的节点级代码执行。
漏洞目标不仅限于 kube-proxy。 任何容器镜像与攻击者控制的镜像共享层的特权 DaemonSet(监控代理、CNI 插件、日志采集器、安全代理等)都是可行的攻击目标。
攻击链包含三个阶段:页缓存损坏、跨容器传播和特权执行。
内核的 AF_ALG(加密)子系统为用户空间加密操作提供基于套接字的接口。该漏洞利用的是内核在处理从文件到 AF_ALG 套接字的 splice() 时存在的竞态条件:
authencesn(hmac(sha256),cbc(aes)) 的 AF_ALG AEAD 套接字。MSG_MORE 通过 AF_ALG 套接字发送一小块载荷数据,告知内核还有更多数据。splice() 将目标文件内容从 fd → pipe → AF_ALG 套接字传输。漏洞利用程序对每个 4 字节窗口重复此过程,直到目标二进制文件的整个缓存页都被自定义载荷覆盖。
无需对文件具有写权限。磁盘上的文件保持不变——只有内存中的页缓存被破坏。
容器运行时使用 overlay 文件系统。当两个容器共享同一镜像层时,内核会从相同的页缓存页为它们的文件读取提供服务。
攻击者构建其 PoC 镜像时,FROM 与目标特权 DaemonSet 相同的基础镜像。由于两个容器共享相同的 overlay lower-dir,共享层中的二进制文件将映射到相同的页缓存页。
当无特权 PoC 容器破坏二进制文件的页缓存时,同一节点上的特权容器立即可见该破坏——并且无需任何跨容器通信。
当特权 DaemonSet 下次执行任何被破坏的二进制文件时(在其正常运行周期内),内核会加载被破坏的页缓存页。攻击者的载荷将以 DaemonSet 的完整权限运行——可能包括:
本 PoC 中的载荷(payload/payload.c)仅挂载主机根文件系统,并向 /root/res 写入标记文件,作为节点级代码执行的证明。
┌──────────────────────────┐ ┌──────────────────────────┐
│ PoC Container │ │ Privileged DaemonSet │
│ (unprivileged) │ │ (e.g. kube-proxy, │
│ │ │ monitoring agent, etc.)│
│ 1. Open target binary │ │ │
│ (read-only) │ │ │
│ │ │ │
│ 2. AF_ALG splice race │ │ │
│ corrupts page cache │ │ │
│ │ │ │ │
└──────────┼───────────────┘ └──────────────────────────┘
│ │
▼ │
┌─────────────────────┐ │
│ Kernel Page Cache │ │
│ │◄────────────────────┘
│ Shared-layer binary │ 3. DaemonSet executes the
│ (CORRUPTED) │ corrupted binary
│ contains attacker's │ → loads corrupted pages
│ payload bytes │ → payload runs with
└─────────────────────┘ DaemonSet's privileges
该 PoC 已在以下托管 Kubernetes 平台上成功验证:



在以上三种情况中,无特权 PoC Pod 均成功地将 [*] success 标记文件写入主机文件系统——证明通过特权 kube-proxy DaemonSet 实现了节点级代码执行。
有关完整演练(镜像层分析、构建步骤、部署):
本 PoC 以 kube-proxy 为目标,因为它是 Kubernetes 集群中最常见的特权 DaemonSet 之一。提供了三种变体:
FROM registry.k8s.io/kube-proxy:v1.35.2(参见 Dockerfile)FROM public.ecr.aws/eks-distro-build-tooling/eks-distro-minimal-base-iptables:2026-03-11-1773190710.2023(参见 Dockerfile.eks)FROM us-central1-artifactregistry.gcr.io/gke-release/gke-release/kube-proxy:v1.35.3-gke.1234000(参见 Dockerfile.gke)所有变体都会破坏诸如 /usr/sbin/ipset、/usr/sbin/nft、/usr/sbin/xtables-legacy-multi 和 /usr/sbin/xtables-nft-multi 之类的二进制文件。
重要注意事项:
ipset。默认模式(iptables)不使用 ipset。有关 ipvs 弃用计划,请参阅 kubernetes/enhancements#5495。ipset、nft、xtables-legacy-multi、xtables-nft-multi)以覆盖不同的代理模式,但这些二进制文件是否被调用取决于集群配置。如果你的集群中 kube-proxy 不是特权容器,攻击原理仍然成立 —— 你只需要识别一个不同的特权 DaemonSet,它共享镜像层且基础镜像可供你基于其构建。
要将此 PoC 适配到另一个特权 DaemonSet:
FROM)。.
├── cmd/copyfail/main.go # Entry point; embeds compiled payload
├── internal/
│ ├── exploit/
│ │ ├── exploit.go # Core exploit: AF_ALG splice race loop
│ │ └── patch.go # Splits payload into 4-byte patch windows
│ └── alg/
│ └── alg.go # AF_ALG AEAD socket abstraction
├── payload/
│ ├── payload.c # ACK/upstream payload (mount /dev/vda3 ext4)
│ ├── payload-eks.c # EKS payload (NVMe/Xen device auto-detection)
│ ├── payload-gke.c # GKE payload (COS/Ubuntu device auto-detection)
│ └── nolibc/ # Kernel's tiny libc for static, no-dependency payloads
├── deploy/
│ ├── poc.yaml # Kubernetes Deployment manifest (ACK/upstream)
│ ├── poc-eks.yaml # EKS Deployment manifest
│ └── poc-gke.yaml # GKE Deployment manifest
├── Dockerfile # ACK/upstream: FROM registry.k8s.io/kube-proxy
├── Dockerfile.eks # EKS: FROM eks-distro-minimal-base-iptables
├── Dockerfile.gke # GKE: FROM gke-release/kube-proxy
├── Makefile # Build orchestration (includes *-eks and *-gke targets)
└── docs/
├── eks-poc.md # EKS PoC full walkthrough
├── gke-poc.md # GKE PoC full walkthrough
├── ack-poc-res.png # ACK validation screenshot
├── eks-poc-res.png # EKS validation screenshot
└── gke-poc-res.png # GKE validation screenshot
x86_64-linux-gnu-gcc)imagePullPolicy: IfNotPresent(Kubernetes 默认值)# Build payload + Go binary
make build
# Build Docker image
make docker-build
# Build and push to GHCR
make docker-push IMAGE=ghcr.io/<you>/copy-fail-poc TAG=latest
# Build EKS payload + Go binary + Docker image
make docker-build-eks
# Build and push to GHCR
make docker-push-eks IMAGE=ghcr.io/<you>/copy-fail-poc
对于 arm64 目标(Graviton):
make build-eks CC=aarch64-linux-gnu-gcc GOARCH=arm64
# Build GKE payload + Go binary + Docker image
make docker-build-gke
# Build and push to GHCR
make docker-push-gke IMAGE=ghcr.io/<you>/copy-fail-poc
对于 arm64 节点:
make docker-build-gke CC=aarch64-linux-gnu-gcc GOARCH=arm64 PLATFORM=linux/arm64
# ACK / upstream Kubernetes
kubectl apply -f deploy/poc.yaml
# Amazon EKS
kubectl apply -f deploy/poc-eks.yaml
# Google GKE
kubectl apply -f deploy/poc-gke.yaml
该 Deployment 会创建一个无特权 Pod。它会:
/bin/copyfail 来破坏共享镜像层中目标二进制文件的页缓存。当目标特权 DaemonSet 下次执行被破坏的二进制文件后(对于 kube-proxy,由于其协调循环,这通常会在数秒内发生),请检查节点:
# SSH into the node, or use a privileged debug pod
# ACK / EKS (writable root filesystem)
cat /root/res
# Expected output: [*] success
# GKE COS nodes (read-only root, writable stateful partition)
cat /mnt/stateful_partition/copyfail-res
# Expected output: [*] success
主机文件系统上存在标记文件,证明攻击者提供的代码在特权 DaemonSet 的容器上下文内以节点级权限执行。
kubectl delete -f deploy/poc.yaml # or poc-eks.yaml / poc-gke.yaml
# On the affected node(s), remove the marker and restart the target DaemonSet:
rm -f /root/res # ACK / EKS
rm -f /copyfail-res /mnt/stateful_partition/copyfail-res # GKE COS nodes
# For kube-proxy: delete the pod to force image layer re-read
kubectl delete pod -n kube-system -l k8s-app=kube-proxy --field-selector spec.nodeName=<node>
默认载荷(payload/payload.c)是一个仅用于验证的程序,它会写入一个标记文件。要构建自定义载荷:
payload/payload.c。该程序基于 nolibc(内核的最小 C 库)构建,以生成静态、无依赖的二进制文件。make payload 进行交叉编译。//go:embed 嵌入到 Go 二进制文件中。privileged: true 的 DaemonSet 中删除不必要的 capabilities。AF_ALG 套接字来阻断该利用向量。该规则可通过 AppArmor 和 BPF 执行器使用。tools/include/nolibc/)本仓库中的 Go 漏洞利用代码按原样提供,仅供研究目的。
载荷(payload/payload.c)衍生自 copy-fail-c,并根据 LGPL-2.1-or-later 或 MIT 双重许可。请参阅 LICENSE-LGPL 和 LICENSE-MIT。
| 属性 | 值 |
|---|
| 平台 | 阿里云容器服务 Kubernetes 版(ACK) |
| Kubernetes | v1.35.2 |
| 节点内核 | 6.6.88-4.2.alnx4.x86_64 |
| kube-proxy | registry-cn-*.ack.aliyuncs.com/acs/kube-proxy:v1.35.2-aliyun.1 |
| 基础镜像 | registry.k8s.io/kube-proxy:v1.35.2(上游) |
| 根设备 | /dev/vda3 (ext4) |
| 属性 | 值 |
|---|
| 平台 | Amazon Elastic Kubernetes Service(EKS) |
| Kubernetes | v1.35.4 |
| 节点内核 | 6.12.79-101.147.amzn2023.x86_64 |
| kube-proxy | ***.dkr.ecr.***.amazonaws.com.cn/eks/kube-proxy:v1.35.3-eksbuild.2 |
| 基础镜像 | public.ecr.aws/eks-distro-build-tooling/eks-distro-minimal-base-iptables:2026-03-11-1773190710.2023 |
| 根设备 | /dev/nvme0n1p1 (xfs) |
| 属性 | 值 |
|---|
| 平台 | Google Kubernetes Engine(GKE) |
| Kubernetes | v1.35.3-gke.1234000 |
| 节点操作系统 | Container-Optimized OS (COS) 125,BUILD_ID 19216.220.72 |
| 节点内核 | 6.12.68+ x86_64 |
| kube-proxy | us-central1-artifactregistry.gcr.io/gke-release/gke-release/kube-proxy:v1.35.3-gke.1234000 |
| 基础镜像 | 与 kube-proxy 相同(GKE 供应商管理的 Artifact Registry 镜像) |
| 根设备 | /dev/dm-0(ext2,只读);/dev/sda1(ext4,可写的有状态分区) |
| 标记路径 | /mnt/stateful_partition/copyfail-res |