
PoC: vollständig unprivilegierter Container-Escape zu Codeausführung auf Knotenebene auf Kubernetes via CVE-2026-31431 Page-Cache-Korruption und gemeinsame Image-Layer. Validierte Implementierung auf Alibaba Cloud ACK, Amazon EKS und Google GKE.
Ein Proof-of-Concept, das demonstriert, wie ein vollständig unprivilegierter Container eine Codeausführung auf Knotenebene in Kubernetes erreichen kann, indem es den Linux-Kernel-Page-Cache-Bug CVE-2026-31431 durch gemeinsam genutzte Container-Image-Layer ausnutzt.
Die grundlegende Angriffsprimitive ist: Jedes privilegierte DaemonSet, das Image-Layer mit einem angreiferkontrollierten Container teilt, kann für einen Container-Ausbruch genutzt werden. Dieses PoC verwendet kube-proxy als konkretes Beispiel, aber die Technik verallgemeinert sich auf jede privilegierte Arbeitslast im Cluster.
Validieren auf Alibaba Cloud ACK, Amazon EKS und Google GKE – ein unprivilegierter Pod schreibt [*] success in das Host-Dateisystem über das privilegierte kube-proxy DaemonSet:
| Alibaba Cloud ACK (kernel 6.6.88) | Amazon EKS (kernel 6.12.79) | Google GKE (kernel 6.12.68) |
|---|---|---|
![]() | ![]() | ![]() |
Haftungsausschluss: Dieses Repository wird ausschließlich zu Bildungs- und Verteidigungszwecken veröffentlicht. Verwenden Sie es ausschließlich auf Systemen, die Ihnen gehören oder für die Sie ausdrückliche Berechtigung zum Testen haben.
CVE-2026-31431 ("Copy Fail") ist eine Linux-Kernel-Sicherheitslücke im Page-Cache-Copy-on-Write (CoW)-Pfad. Ein AF_ALG-Splice-Race erlaubt es einem unprivilegierten Prozess, die Page-Cache-Seiten einer schreibgeschützten Datei zu beschädigen. Die Beschädigung bleibt im Kernel-Page-Cache bestehen und ist für jeden Prozess sichtbar, der die Datei anschließend liest oder ausführt – einschließlich Prozessen in anderen Containern oder auf dem Host.
Weitere Einzelheiten zur ursprünglichen Sicherheitslücke finden Sie unter copy.fail.
Der Angriff nutzt drei Eigenschaften aus, die in Kubernetes-Clustern häufig gemeinsam auftreten:
privileged: true, hostNetwork: true, breite Capabilities usw.), die regelmäßig Binärdateien aus ihrem Image ausführen.Wenn diese Bedingungen zusammentreffen, kann ein unprivilegierter Pod eine Binärdatei in einem gemeinsamen Image-Layer beschädigen, und ein privilegiertes DaemonSet auf demselben Knoten wird die beschädigte Binärdatei unwissentlich mit seinen erhöhten Privilegien ausführen – was eine vollständige Codeausführung auf Knotenebene erreicht.
Das Angriffsziel ist NICHT auf kube-proxy beschränkt. Jedes privilegierte DaemonSet (Überwachungsagenten, CNI-Plugins, Log-Sammler, Sicherheitsagenten usw.), dessen Container-Image Layer mit einem angreiferkontrollierten Image teilt, ist ein mögliches Ziel.
Die Angriffskette besteht aus drei Phasen: Page-Cache-Beschädigung, containerübergreifende Verbreitung und privilegierte Ausführung.
Das AF_ALG (Krypto)-Subsystem des Kernels stellt eine socketbasierte Schnittstelle für kryptografische Operationen im userspace bereit. Der Exploit missbraucht eine Race-Condition in der Art und Weise, wie der Kernel splice() von einer Datei in einen AF_ALG-Socket behandelt:
authencesn(hmac(sha256),cbc(aes)) gebunden ist.MSG_MORE, um dem Kernel mitzuteilen, dass weitere Daten erwartet werden.splice() Sie den Inhalt der Zieldatei von einem fd → Pipe → AF_ALG-Socket.Der Exploit wiederholt dies für jedes 4-Byte-Fenster, bis alle zwischengespeicherten Seiten der Zielbinärdatei mit einem benutzerdefinierten Payload überschrieben sind.
Es ist keine Schreibberechtigung für die Datei erforderlich. Die Datei auf der Festplatte bleibt unverändert – nur der im Arbeitsspeicher befindliche Page-Cache wird beschädigt.
Container-Laufzeiten verwenden Overlay-Dateisysteme. Wenn zwei Container denselben Image-Layer teilen, bedient der Kernel ihre Dateilesevorgänge von denselben Page-Cache-Seiten.
Der Angreifer baut sein PoC-Image FROM demselben Basis-Image wie das privilegierte Ziel-DaemonSet. Da beide Container dasselbe Overlay-Lower-Verzeichnis teilen, werden Binärdateien im gemeinsamen Layer auf identische Page-Cache-Seiten abgebildet.
Wenn der unprivilegierte PoC-Container den Page-Cache einer Binärdatei beschädigt, ist die Beschädigung sofort für den privilegierten Container auf demselben Knoten sichtbar – ohne jegliche containerübergreifende Kommunikation.
Wenn das privilegierte DaemonSet als nächstes eine beschädigte Binärdatei ausführt (durch seinen normalen Betriebszyklus), lädt der Kernel die beschädigten Page-Cache-Seiten. Der Payload des Angreifers läuft mit den vollen Privilegien des DaemonSets – möglicherweise einschließlich:
Der Payload in diesem PoC (payload/payload.c) mountet einfach das Host-Root-Dateisystem und schreibt eine Markierungsdatei nach /root/res als Beweis für die Codeausführung auf Knotenebene.
┌──────────────────────────┐ ┌──────────────────────────┐
│ 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
Das PoC wurde erfolgreich auf den folgenden verwalteten Kubernetes-Plattformen validiert:



In allen drei Fällen schrieb ein unprivilegierter PoC-Pod erfolgreich die Markierungsdatei [*] success in das Host-Dateisystem – was die Codeausführung auf Knotenebene durch das privilegierte kube-proxy DaemonSet beweist.
Für die vollständigen Anleitungen (Image-Layer-Analyse, Build-Schritte, Bereitstellung):
Dieses PoC verwendet kube-proxy als Ziel, da es eines der häufigsten privilegierten DaemonSets in Kubernetes-Clustern ist. Es werden drei Varianten bereitgestellt:
FROM registry.k8s.io/kube-proxy:v1.35.2 (siehe Dockerfile)FROM public.ecr.aws/eks-distro-build-tooling/eks-distro-minimal-base-iptables:2026-03-11-1773190710.2023 (siehe Dockerfile.eks)FROM us-central1-artifactregistry.gcr.io/gke-release/gke-release/kube-proxy:v1.35.3-gke.1234000 (siehe Dockerfile.gke)Alle Varianten beschädigen Binärdateien wie /usr/sbin/ipset, /usr/sbin/nft, /usr/sbin/xtables-legacy-multi und /usr/sbin/xtables-nft-multi.
Wichtige Einschränkungen:
ipset nur auf, wenn es im ipvs-Modus konfiguriert ist. Der Standardmodus (iptables) verwendet ipset nicht. Siehe kubernetes/enhancements#5495 für den ipvs-Deprecation-Plan.ipset, nft, xtables-legacy-multi, xtables-nft-multi) ab, um verschiedene Proxy-Modi abzudecken, aber ob sie aufgerufen werden, hängt von der Clusterkonfiguration ab.Wenn kube-proxy in Ihrem Cluster nicht privilegiert ist, gilt das Angriffsprinzip dennoch – Sie müssen lediglich ein anderes privilegiertes DaemonSet identifizieren, das Image-Layer mit einem Basis-Image teilt, von dem aus Sie bauen können.
Um dieses PoC an ein anderes privilegiertes DaemonSet anzupassen:
FROM demselben Basis-Image, das von diesem DaemonSet verwendet wird..
├── 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 auf dem Ziel-DaemonSet (der Kubernetes-Standard)# 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
Für arm64-Ziele (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
Für arm64-Knoten:
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
Das Deployment erstellt einen einzelnen unprivilegierten Pod. Dieser:
/bin/copyfail aus, um den Page-Cache von Zielbinärdateien im gemeinsamen Image-Layer zu beschädigen.Nachdem das privilegierte Ziel-DaemonSet als nächstes eine beschädigte Binärdatei ausführt (bei kube-proxy geschieht dies normalerweise innerhalb von Sekunden aufgrund der Reconciliation-Schleife), überprüfen Sie den Knoten:
# 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
Das Vorhandensein der Markierungsdatei auf dem Host-Dateisystem beweist, dass vom Angreifer bereitgestellter Code mit privilegierten Rechten auf Knotenebene ausgeführt wurde – aus dem Container-Kontext des privilegierten DaemonSets heraus.
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>
Der Standard-Payload (payload/payload.c) ist ein reines Validierungsprogramm, das eine Markierungsdatei schreibt. Um einen benutzerdefinierten Payload zu erstellen:
payload/payload.c. Das Programm wird gegen nolibc (die minimale C-Bibliothek des Kernels) erstellt, um eine statische, abhängigkeitsfreie Binärdatei zu erhalten.make payload aus, um zu cross-kompilieren.//go:embed in die Go-Binärdatei eingebettet.privileged: true benötigen.AF_ALG-Sockets erstellen. Die Regel ist über die AppArmor- und BPF-Enforcer verfügbar.tools/include/nolibc/)Der Go-Exploit-Code in diesem Repository wird wie besehen für Forschungszwecke bereitgestellt.
Der Payload (payload/payload.c) stammt von copy-fail-c und ist dual lizenziert unter LGPL-2.1-or-later ODER MIT. Siehe LICENSE-LGPL und LICENSE-MIT.
| Eigenschaft | Wert |
|---|
| Plattform | Alibaba Cloud Container Service for Kubernetes (ACK) |
| Kubernetes | v1.35.2 |
| Knoten-Kernel | 6.6.88-4.2.alnx4.x86_64 |
| kube-proxy | registry-cn-*.ack.aliyuncs.com/acs/kube-proxy:v1.35.2-aliyun.1 |
| Basis-Image | registry.k8s.io/kube-proxy:v1.35.2 (upstream) |
| Root-Gerät | /dev/vda3 (ext4) |
| Eigenschaft | Wert |
|---|
| Plattform | Amazon Elastic Kubernetes Service (EKS) |
| Kubernetes | v1.35.4 |
| Knoten-Kernel | 6.12.79-101.147.amzn2023.x86_64 |
| kube-proxy | ***.dkr.ecr.***.amazonaws.com.cn/eks/kube-proxy:v1.35.3-eksbuild.2 |
| Basis-Image | public.ecr.aws/eks-distro-build-tooling/eks-distro-minimal-base-iptables:2026-03-11-1773190710.2023 |
| Root-Gerät | /dev/nvme0n1p1 (xfs) |
| Eigenschaft | Wert |
|---|
| Plattform | Google Kubernetes Engine (GKE) |
| Kubernetes | v1.35.3-gke.1234000 |
| Knoten-OS | Container-Optimized OS (COS) 125, BUILD_ID 19216.220.72 |
| Knoten-Kernel | 6.12.68+ x86_64 |
| kube-proxy | us-central1-artifactregistry.gcr.io/gke-release/gke-release/kube-proxy:v1.35.3-gke.1234000 |
| Basis-Image | Gleiches wie kube-proxy (GKE-provider-managed Artifact Registry image) |
| Root-Gerät | /dev/dm-0 (ext2, schreibgeschützt); /dev/sda1 (ext4, beschreibbare stateful partition) |
| Marker-Pfad | /mnt/stateful_partition/copyfail-res |