
쿠버네티스에서 CVE-2026-31431 커널 페이지 캐시 손상을 통한 컨테이너 탈출을 시연하는 개념 증명으로, 권한이 없는 컨테이너에서 노드 수준의 코드 실행을 달성합니다.
완전히 권한이 없는 컨테이너가 공유 컨테이너 이미지 레이어를 통해 CVE-2026-31431 Linux 커널 페이지-캐시 손상 버그를 악용하여 Kubernetes에서 노드 수준 코드 실행을 달성할 수 있음을 보여주는 개념 증명입니다.
면책 조항: 이 저장소는 교육 및 방어 목적으로만 게시되었습니다. 소유한 시스템이나 명시적 테스트 권한이 있는 시스템에서만 사용하십시오.
CVE-2026-31431("Copy Fail")은 페이지-캐시 Copy-on-Write(CoW) 경로의 Linux 커널 취약점입니다. AF_ALG splice 경쟁 조건으로 인해 권한이 없는 프로세스가 읽기 전용 파일의 페이지-캐시 페이지를 손상시킬 수 있습니다. 손상은 커널 페이지 캐시에 지속되며 이후 해당 파일을 읽거나 실행하는 모든 프로세스(다른 컨테이너 또는 호스트의 프로세스 포함)에 표시됩니다.
원래 취약점에 대한 자세한 내용은 copy.fail을 참조하십시오.
공격 체인은 페이지-캐시 손상, 컨테이너 간 전파, 권한 있는 실행의 세 단계로 구성됩니다.
커널의 AF_ALG(암호화) 하위 시스템은 사용자 공간 암호화 작업을 위한 소켓 기반 인터페이스를 제공합니다. 익스플로잇은 커널이 파일에서 AF_ALG 소켓으로 splice()를 처리하는 방식의 경쟁 조건을 악용합니다:
/usr/sbin/ipset)를 읽기 전용으로 엽니다.authesn(hmac(sha256),cbc(aes))에 바인딩된 AF_ALG AEAD 소켓을 생성합니다.MSG_MORE와 함께 AF_ALG 소켓을 통해 작은 페이로드 청크를 보내 커널에 더 많은 데이터가 올 것임을 알립니다.splice()합니다.익스플로잇은 대상 바이너리의 캐시된 전체 페이지가 사용자 정의 페이로드로 덮일 때까지 각 4바이트 창에 대해 이 작업을 반복합니다.
파일에 대한 쓰기 권한은 필요하지 않습니다. 디스크의 파일은 변경되지 않습니다 — 메모리 내 페이지 캐시만 손상됩니다.
컨테이너 런타임(containerd, CRI-O)은 overlay 파일시스템을 사용합니다. 두 컨테이너가 동일한 이미지 레이어를 공유하면 커널은 동일한 페이지-캐시 페이지에서 파일 읽기를 제공합니다.
이 PoC 이미지는 FROM registry.k8s.io/kube-proxy:v1.35.2로 빌드됩니다. 모든 Kubernetes 노드의 kube-proxy DaemonSet은 정확히 동일한 기본 레이어를 사용합니다. 결과적으로 두 컨테이너의 /usr/sbin/ipset은 동일한 페이지-캐시 페이지 집합에 매핑됩니다.
권한이 없는 PoC 컨테이너가 ipset의 페이지 캐시를 손상시키면 손상은 컨테이너 간 통신 없이 동일한 노드의 권한 있는 kube-proxy 컨테이너에 즉시 표시됩니다.
kube-proxy는 hostNetwork: true가 있는 권한 있는 DaemonSet으로 실행됩니다. iptables/ipset 규칙을 관리하기 위해 주기적으로 /usr/sbin/ipset을 호출합니다. 다음에 ipset을 실행할 때 커널은 손상된 페이지-캐시 페이지를 로드하여 kube-proxy의 전체 권한으로 공격자의 페이로드를 실행합니다:
이 PoC의 페이로드(payload/payload.c)는 단순히 호스트 루트 파일시스템을 마운트하고 노드 수준 코드 실행의 증거로 /root/res에 마커 파일을 기록합니다.
┌──────────────────────────┐ ┌──────────────────────────┐
│ PoC Container │ │ kube-proxy Container │
│ (unprivileged) │ │ (privileged) │
│ │ │ │
│ 1. Open /usr/sbin/ipset │ │ │
│ (read-only) │ │ │
│ │ │ │
│ 2. AF_ALG splice race │ │ │
│ corrupts page cache │ │ │
│ │ │ │ │
└──────────┼───────────────┘ └──────────────────────────┘
│ │
▼ │
┌─────────────────────┐ │
│ Kernel Page Cache │ │
│ /usr/sbin/ipset │◄────────────────────┘
│ (CORRUPTED) │ 3. kube-proxy executes ipset
│ contains attacker's │ → loads corrupted pages
│ payload bytes │ → payload runs as root
└─────────────────────┘ on the host
.
├── 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 # Validation payload (mount host fs, write marker)
│ └── nolibc/ # Kernel's tiny libc for static, no-dependency payloads
├── deploy/
│ └── poc.yaml # Kubernetes Deployment manifest
├── Dockerfile # Built FROM kube-proxy to share image layers
├── Makefile # Build orchestration
└── docs/ # Validation evidence from ACK (Alibaba Cloud)
x86_64-linux-gnu-gcc)imagePullPolicy: IfNotPresent(기본값) 사용)# 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
arm64 대상의 경우:
make build CC=aarch64-linux-gnu-gcc GOARCH=arm64
kubectl apply -f deploy/poc.yaml
Deployment는 단일 권한 없는 pod를 생성합니다. 다음을 수행합니다:
/bin/copyfail -target /usr/sbin/ipset을 실행하여 페이지 캐시를 손상시킵니다.kube-proxy가 다음에 ipset을 실행한 후(일반적으로 조정 루프로 인해 몇 초 내에 발생하거나 다음 재시작 시 발생), 노드를 확인합니다:
# SSH into the node, or use a privileged debug pod
cat /root/res
# Expected output: [*] success
호스트 파일시스템에 /root/res가 존재하면 공격자가 제공한 코드가 노드 수준 권한으로 실행되었음을 증명합니다 — kube-proxy의 권한 있는 컨테이너 컨텍스트 내부에서 기록되었습니다.
kubectl delete -f deploy/poc.yaml
# On the affected node(s), remove the marker and restart kube-proxy:
rm -f /root/res
systemctl restart kubelet # or delete the kube-proxy pod to force re-pull
kube-proxy는 다음과 같은 이유로 이상적인 대상입니다:
privileged: true, hostNetwork: true.imagePullPolicy: IfNotPresent 사용 — 공격자의 이미지가 풀되고 동일한 기본 레이어를 공유하면 overlay lower-dir 페이지가 공유됩니다.이미지에 예측 가능한 바이너리가 포함된 모든 권한 있는 DaemonSet은 동일한 방식으로 대상이 될 수 있습니다.
기본 페이로드(payload/payload.c)는 마커 파일을 기록하는 검증 전용 프로그램입니다. 사용자 정의 페이로드를 빌드하려면:
payload/payload.c를 편집합니다. 프로그램은 정적, 종속성 없는 바이너리를 위해 nolibc(커널의 최소 C 라이브러리)에 대해 빌드됩니다.make payload를 실행하여 크로스 컴파일합니다.//go:embed를 통해 Go 바이너리에 포함됩니다.tools/include/nolibc/)이 저장소의 Go 익스플로잇 코드는 연구 목적으로 있는 그대로 제공됩니다.
페이로드(payload/payload.c)는 copy-fail-c에서 파생되었으며 LGPL-2.1-or-later OR MIT의 이중 라이선스입니다. LICENSE-LGPL 및 LICENSE-MIT를 참조하십시오.