
PoC: CVE-2026-31431 페이지 캐시 손상 + 공유 이미지 레이어를 통한 Kubernetes에서 완전 비특권 컨테이너 이스케이프에서 노드 수준 코드 실행까지. Alibaba Cloud ACK, Amazon EKS 및 Google GKE에서 검증됨.
공유 컨테이너 이미지 레이어를 통해 CVE-2026-31431 리눅스 커널 페이지 캐시 손상 버그를 악용하여 완전히 권한이 없는 컨테이너가 Kubernetes에서 노드 수준 코드 실행을 달성할 수 있는 방법을 보여주는 개념 증명입니다.
핵심 공격 원리는 다음과 같습니다: 공격자가 제어하는 컨테이너와 이미지 레이어를 공유하는 모든 권한 있는 DaemonSet이 컨테이너 탈출에 무기화될 수 있습니다. 이 PoC는 kube-proxy를 구체적인 예로 사용하지만, 이 기술은 클러스터의 모든 권한 있는 워크로드에 일반화됩니다.
Alibaba Cloud ACK, Amazon EKS, Google GKE에서 검증됨 — 권한 없는 파드가 권한 있는 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")은 페이지 캐시 Copy-on-Write(CoW) 경로의 리눅스 커널 취약점입니다. AF_ALG splice 경합 조건을 통해 권한 없는 프로세스가 읽기 전용 파일의 페이지 캐시 페이지를 손상시킬 수 있습니다. 손상은 커널 페이지 캐시에 지속되며 이후 그 파일을 읽거나 실행하는 모든 프로세스(다른 컨테이너나 호스트의 프로세스 포함)에서 볼 수 있습니다.
원래 취약점에 대한 자세한 내용은 copy.fail을 참조하세요.
이 공격은 Kubernetes 클러스터에서 일반적으로 공존하는 세 가지 속성을 악용합니다:
privileged: true, hostNetwork: true, 광범위한 capabilities 등)으로 DaemonSet을 실행하며, 이들은 주기적으로 이미지에서 바이너리를 실행합니다.이러한 조건이 일치하면, 권한 없는 파드가 공유 이미지 레이어의 바이너리를 손상시킬 수 있고, 동일한 노드의 권한 있는 DaemonSet이 손상된 바이너리를 상승된 권한으로 알지 못한 채 실행하여 완전한 노드 수준 코드 실행을 달성합니다.
취약점 대상은 kube-proxy에 국한되지 않습니다. 공격자가 제어하는 이미지와 이미지 레이어를 공유하는 모든 권한 있는 DaemonSet(모니터링 에이전트, CNI 플러그인, 로그 수집기, 보안 에이전트 등)이 실행 가능한 대상입니다.
공격 체인은 세 단계로 구성됩니다: 페이지 캐시 손상, 컨테이너 간 전파, 권한 있는 실행.
커널의 AF_ALG(암호화) 서브시스템은 사용자 공간 암호화 작업을 위한 소켓 기반 인터페이스를 제공합니다. 익스플로잇은 커널이 파일에서 AF_ALG 소켓으로 splice()를 처리하는 방식의 경합 조건을 남용합니다:
authencesn(hmac(sha256),cbc(aes))에 바인딩된 AF_ALG AEAD 소켓을 생성합니다.MSG_MORE 플래그와 함께 AF_ALG 소켓을 통해 작은 페이로드 청크를 전송하여 커널에 더 많은 데이터가 올 것임을 알립니다.splice()합니다.익스플로잇은 전체 대상 바이너리의 캐시된 페이지가 사용자 정의 페이로드로 덮어쓰여질 때까지 각 4바이트 윈도우에 대해 이 과정을 반복합니다.
파일에 대한 쓰기 권한은 필요하지 않습니다. 디스크의 파일은 변경되지 않으며, 메모리 내 페이지 캐시만 손상됩니다.
컨테이너 런타임은 오버레이 파일 시스템을 사용합니다. 두 컨테이너가 동일한 이미지 레이어를 공유할 때, 커널은 동일한 페이지 캐시 페이지에서 파일 읽기를 제공합니다.
공격자는 대상 권한 있는 DaemonSet과 동일한 기본 이미지를 FROM하여 PoC 이미지를 빌드합니다. 두 컨테이너가 동일한 오버레이 하위 디렉터리를 공유하기 때문에 공유 레이어의 바이너리는 동일한 페이지 캐시 페이지에 매핑됩니다.
권한 없는 PoC 컨테이너가 바이너리의 페이지 캐시를 손상시키면, 손상은 즉시 동일한 노드의 권한 있는 컨테이너에 표시됩니다 — 컨테이너 간 통신은 전혀 없습니다.
권한 있는 DaemonSet이 다음에 손상된 바이너리를 실행할 때(정상 작동 주기를 통해), 커널은 손상된 페이지 캐시 페이지를 로드합니다. 공격자의 페이로드는 DaemonSet의 전체 권한으로 실행되며, 여기에는 다음이 포함될 수 있습니다:
이 PoC의 페이로드(payload/payload.c)는 단순히 호스트 루트 파일 시스템을 마운트하고 노드 수준 코드 실행의 증명으로 마커 파일을 /root/res에 기록합니다.
┌──────────────────────────┐ ┌──────────────────────────┐
│ PoC 컨테이너 │ │ 권한 있는 DaemonSet │
│ (권한 없음) │ │ (예: kube-proxy, │
│ │ │ 모니터링 에이전트 등) │
│ 1. 대상 바이너리 열기 │ │ │
│ (읽기 전용) │ │ │
│ │ │ │
│ 2. AF_ALG splice 경합 │ │ │
│ 조건으로 페이지 캐시 │ │ │
│ 손상 │ │ │
│ │ │ │ │
└──────────┼───────────────┘ └──────────────────────────┘
│ │
▼ │
┌─────────────────────┐ │
│ 커널 페이지 캐시 │ │
│ │◄────────────────────┘
│ 공유 레이어 바이너리 │ 3. DaemonSet이 손상된
│ (손상됨) │ 바이너리 실행
│ 공격자의 페이로드 │ → 손상된 페이지 로드
│ 바이트 포함 │ → 페이로드가 DaemonSet의
└─────────────────────┘ 권한으로 실행됨
PoC는 다음 관리형 Kubernetes 플랫폼에서 성공적으로 검증되었습니다:



세 경우 모두, 권한 없는 PoC 파드가 호스트 파일 시스템에 [*] success 마커 파일을 성공적으로 기록했습니다 — 이는 권한 있는 kube-proxy DaemonSet을 통한 노드 수준 코드 실행을 증명합니다.
전체 워크스루(이미지 레이어 분석, 빌드 단계, 배포)는 다음을 참조하세요:
이 PoC는 가장 일반적인 권한 있는 DaemonSet 중 하나인 kube-proxy를 대상으로 사용합니다. 세 가지 변형이 제공됩니다:
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하여 PoC 이미지를 빌드합니다..
├── cmd/copyfail/main.go # 진입점; 컴파일된 페이로드 포함
├── internal/
│ ├── exploit/
│ │ ├── exploit.go # 핵심 익스플로잇: AF_ALG splice 경합 조건 루프
│ │ └── patch.go # 페이로드를 4바이트 패치 윈도우로 분할
│ └── alg/
│ └── alg.go # AF_ALG AEAD 소켓 추상화
├── payload/
│ ├── payload.c # ACK/업스트림 페이로드 (/dev/vda3 ext4 마운트)
│ ├── payload-eks.c # EKS 페이로드 (NVMe/Xen 장치 자동 감지)
│ ├── payload-gke.c # GKE 페이로드 (COS/Ubuntu 장치 자동 감지)
│ └── nolibc/ # 커널의 작은 libc, 정적 종속성 없는 페이로드용
├── deploy/
│ ├── poc.yaml # Kubernetes Deployment 매니페스트 (ACK/업스트림)
│ ├── poc-eks.yaml # EKS Deployment 매니페스트
│ └── poc-gke.yaml # GKE Deployment 매니페스트
├── Dockerfile # ACK/업스트림: FROM registry.k8s.io/kube-proxy
├── Dockerfile.eks # EKS: FROM eks-distro-minimal-base-iptables
├── Dockerfile.gke # GKE: FROM gke-release/kube-proxy
├── Makefile # 빌드 오케스트레이션 (*-eks 및 *-gke 대상 포함)
└── docs/
├── eks-poc.md # EKS PoC 전체 워크스루
├── gke-poc.md # GKE PoC 전체 워크스루
├── ack-poc-res.png # ACK 검증 스크린샷
├── eks-poc-res.png # EKS 검증 스크린샷
└── gke-poc-res.png # GKE 검증 스크린샷
x86_64-linux-gnu-gcc)imagePullPolicy: IfNotPresent (Kubernetes 기본값)# 페이로드 + Go 바이너리 빌드
make build
# Docker 이미지 빌드
make docker-build
# GHCR에 빌드 및 푸시
make docker-push IMAGE=ghcr.io/<you>/copy-fail-poc TAG=latest
# EKS 페이로드 + Go 바이너리 + Docker 이미지 빌드
make docker-build-eks
# GHCR에 빌드 및 푸시
make docker-push-eks IMAGE=ghcr.io/<you>/copy-fail-poc
arm64 대상(Graviton)의 경우:
make build-eks CC=aarch64-linux-gnu-gcc GOARCH=arm64
# GKE 페이로드 + Go 바이너리 + Docker 이미지 빌드
make docker-build-gke
# 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 / 업스트림 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는 하나의 권한 없는 파드를 생성합니다. 다음을 수행합니다:
/bin/copyfail을 실행하여 공유 이미지 레이어의 대상 바이너리 페이지 캐시를 손상시킵니다.대상 권한 있는 DaemonSet이 다음에 손상된 바이너리를 실행한 후(kube-proxy의 경우 일반적으로 조정 루프로 인해 몇 초 내에 발생), 노드를 확인합니다:
# SSH로 노드에 접속하거나 권한 있는 디버그 파드 사용
# ACK / EKS (쓰기 가능한 루트 파일 시스템)
cat /root/res
# 예상 출력: [*] success
# GKE COS 노드 (읽기 전용 루트, 쓰기 가능한 상태 파티션)
cat /mnt/stateful_partition/copyfail-res
# 예상 출력: [*] success
호스트 파일 시스템에 마커 파일이 존재한다는 것은 공격자가 제공한 코드가 노드 수준 권한으로 실행되었음을 증명합니다 — 권한 있는 DaemonSet의 컨테이너 컨텍스트 내에서.
kubectl delete -f deploy/poc.yaml # 또는 poc-eks.yaml / poc-gke.yaml
# 영향을 받은 노드에서 마커 제거 및 대상 DaemonSet 재시작:
rm -f /root/res # ACK / EKS
rm -f /copyfail-res /mnt/stateful_partition/copyfail-res # GKE COS 노드
# kube-proxy의 경우: 파드를 삭제하여 이미지 레이어 재읽기 강제
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 OR MIT로 이중 라이선스됩니다. LICENSE-LGPL 및 LICENSE-MIT를 참조하세요.
| 속성 | 값 |
|---|
| 플랫폼 | 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) |
| 속성 | 값 |
|---|
| 플랫폼 | 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 |
| 노드 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 |