
PoC: CVE-2026-31431のページキャッシュ破損と共有イメージレイヤーにより、完全に非特権な状態からコンテナエスケープを達成し、ノードレベルのコード実行へ至ります。Alibaba Cloud ACK、Amazon EKS、Google GKEで検証済み。
共有コンテナイメージレイヤーを介して CVE-2026-31431 Linux カーネルのページキャッシュ破損バグを悪用し、完全に非特権のコンテナが Kubernetes 上でノードレベルのコード実行を達成できることを示す概念実証です。
中核となる攻撃プリミティブは次のとおりです:攻撃者が制御するコンテナとイメージレイヤーを共有する特権 DaemonSet は、コンテナエスケープのために武器化され得る。この PoC は具体例として kube-proxy を使用していますが、この手法はクラスター上の任意の特権ワークロードに一般化できます。
Alibaba Cloud ACK、Amazon EKS、Google GKE で検証済み — 非特権 Pod が特権 kube-proxy DaemonSet を介してホストファイルシステムに [*] success を書き込みます:
| Alibaba Cloud ACK(カーネル 6.6.88) | Amazon EKS(カーネル 6.12.79) | Google GKE(カーネル 6.12.68) |
|---|---|---|
![]() | ![]() | ![]() |
免責事項: このリポジトリは教育的および防御的目的のみで公開されています。所有しているシステム、または明示的なテスト許可を得たシステムでのみ使用してください。
CVE-2026-31431("Copy Fail")は、ページキャッシュのコピーオンライト(CoW)パスにおける Linux カーネルの脆弱性です。AF_ALG の splice レースにより、非特権プロセスが読み取り専用ファイルのページキャッシュページを破損できます。この破損はカーネルのページキャッシュに残り、その後そのファイルを読み取るまたは実行するすべてのプロセス(他のコンテナやホスト上のプロセスを含む)から見えます。
元の脆弱性の詳細については、copy.fail を参照してください。
この攻撃は、Kubernetes クラスターで一般的に共存する3つの特性を悪用します:
privileged: true、hostNetwork: true、広範なケーパビリティなど)を持つ DaemonSet を実行し、それらは定期的にイメージからバイナリを実行します。これらの条件が揃うと、非特権 Pod は共有イメージレイヤー内のバイナリを破損させ、同じノード上の特権 DaemonSet がその破損したバイナリを昇格した権限で知らずに実行することになり、完全なノードレベルのコード実行が達成されます。
脆弱性の標的は kube-proxy に限定されません。 コンテナイメージが攻撃者制御のイメージとレイヤーを共有する任意の特権 DaemonSet(監視エージェント、CNI プラグイン、ログコレクター、セキュリティエージェントなど)が実行可能な標的です。
攻撃チェーンには3つの段階があります:ページキャッシュの破損、コンテナ間伝播、特権実行。
カーネルの AF_ALG(crypto)サブシステムは、ユーザー空間の暗号操作のためのソケットベースのインターフェースを提供します。このエクスプロイトは、ファイルから AF_ALG ソケットへの splice() の処理におけるカーネルのレースコンディションを悪用します:
authesncesn(hmac(sha256),cbc(aes)) にバインドされた AF_ALG AEAD ソケットを作成します。MSG_MORE を使用して AF_ALG ソケット経由で小さなペイロードチャンクを送信し、カーネルにさらなるデータが来ることを伝えます。splice() します。このエクスプロイトは、ターゲットバイナリのキャッシュページ全体がカスタムペイロードで上書きされるまで、4バイトウィンドウごとにこれを繰り返します。
ファイルへの書き込み権限は不要です。ディスク上のファイルは変更されず、メモリ内のページキャッシュのみが破損します。
コンテナランタイムは overlay ファイルシステムを使用します。2つのコンテナが同じイメージレイヤーを共有する場合、カーネルは同じページキャッシュページからファイル読み取りを提供します。
攻撃者は、ターゲットの特権 DaemonSet と同じベースイメージを FROM として PoC イメージを構築します。両方のコンテナが同じ 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 プラットフォームで正常に検証されています:



3つのケースすべてにおいて、非特権の PoC Pod が [*] success マーカーファイルをホストファイルシステムに正常に書き込み、特権 kube-proxy DaemonSet を通じたノードレベルのコード実行を証明しました。
完全なウォークスルー(イメージレイヤー分析、ビルド手順、デプロイ)については:
この PoC は、Kubernetes クラスターで最も一般的な特権 DaemonSet の1つであるため、kube-proxy をターゲットとして使用します。3つのバリアントが用意されています:
registry.k8s.io/kube-proxy:v1.35.2 を FROM としてビルド(Dockerfile を参照)public.ecr.aws/eks-distro-build-tooling/eks-distro-minimal-base-iptables:2026-03-11-1773190710.2023 を FROM としてビルド(Dockerfile.eks を参照)us-central1-artifactregistry.gcr.io/gke-release/gke-release/kube-proxy:v1.35.3-gke.1234000 を FROM としてビルド(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 として PoC イメージをビルドします。.
├── 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 を作成します。この Pod は:
/bin/copyfail を実行して、共有イメージレイヤー内のターゲットバイナリのページキャッシュを破損させます。ターゲットの特権 DaemonSet が次に破損したバイナリを実行した後(kube-proxy の場合、通常は reconcile ループにより数秒以内に発生します)、ノードを確認します:
# 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 から不要なケーパビリティを削除します。AF_ALG ソケットを作成するのを防ぐことでエクスプロイトベクターをブロックします。このルールは AppArmor および BPF エンフォーサーで利用できます。tools/include/nolibc/)このリポジトリ内の Go エクスプロイトコードは、研究目的で現状のまま提供されます。
ペイロード(payload/payload.c)は copy-fail-c から派生しており、LGPL-2.1-or-later OR MIT のデュアルライセンスです。LICENSE-LGPL と LICENSE-MIT を参照してください。
| Property | Value |
|---|
| プラットフォーム | Alibaba Cloud Container Service for 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) |
| Property | Value |
|---|
| プラットフォーム | 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) |
| Property | Value |
|---|
| プラットフォーム | Google Kubernetes Engine (GKE) |
| Kubernetes | v1.35.3-gke.1234000 |
| ノード OS | 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 |