## https://sploitus.com/exploit?id=KITPLOIT:TOOLS-GITHUB-PERCIVALLL-COPY-FAIL-CVE-2026-31431-KUBERNETES-POC
# Copy Fail (CVE-2026-31431) — PoC побега из контейнера Kubernetes
Proof-of-concept, демонстрирующий, как **полностью непривилегированный контейнер** может добиться **выполнения кода на уровне узла** в Kubernetes, эксплуатируя ошибку повреждения page-cache ядра Linux CVE-2026-31431 через общие слои образов контейнеров.
Основной примитив атаки: **любой привилегированный DaemonSet, разделяющий слои образа с контейнером, контролируемым атакующим, может быть использован для побега из контейнера**. В этом PoC в качестве конкретного примера используется kube-proxy, но метод применим к любой привилегированной рабочей нагрузке в кластере.
Проверено на **Alibaba Cloud ACK** , **Amazon EKS** и **Google GKE** — непривилегированный pod записывает `[*] success` в файловую систему узла через привилегированный DaemonSet kube-proxy:
Alibaba Cloud 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) page-cache. Гонка `splice` через `AF_ALG` позволяет непривилегированному процессу повредить страницы page-cache **файла, открытого только для чтения**. Повреждение сохраняется в page-cache ядра и видно каждому процессу, который впоследствии читает или исполняет этот файл, — включая процессы в других контейнерах или на узле.
Полные сведения об исходной уязвимости см. на copy.fail.
## Принцип атаки
Атака использует три свойства, которые часто сосуществуют в кластерах Kubernetes:
1. **Повреждение page-cache ядра (CVE-2026-31431)** — непривилегированный процесс может перезаписать кэшированные в памяти страницы любого файла, который он может открыть только на чтение.
2. **Совместное использование слоёв образов** — контейнерные рантаймы (containerd, CRI-O) используют overlay-файловые системы, где одинаковые слои образов сопоставляются с одними и теми же страницами page-cache в разных контейнерах.
3. **Привилегированные DaemonSet** — во многих кластерах работают DaemonSet с повышенными привилегиями (`privileged: true`, `hostNetwork: true`, широкими capabilities и т.д.), которые периодически выполняют бинарные файлы из своего образа.
Когда эти условия совпадают, непривилегированный pod может повредить бинарный файл в общем слое образа, а привилегированный DaemonSet на том же узле неосознанно выполнит повреждённый бинарный файл со своими повышенными привилегиями — достигая полного выполнения кода на уровне узла.
**Цель уязвимости НЕ ограничивается kube-proxy.** Любой привилегированный DaemonSet (агенты мониторинга, CNI-плагины, сборщики логов, агенты безопасности и т.д.), чей образ контейнера разделяет слои с образом, контролируемым атакующим, является подходящей целью.
## Как это работает
Цепочка атаки состоит из трёх этапов: **повреждение page-cache** , **распространение между контейнерами** и **привилегированное выполнение**.
### 1\. Повреждение page-cache через гонку splice в AF_ALG
Подсистема ядра `AF_ALG` (crypto) предоставляет интерфейс на основе сокетов для криптографических операций из пользовательского пространства. Эксплойт злоупотребляет состоянием гонки в том, как ядро обрабатывает `splice()` из файла в сокет AF_ALG:
1. Откройте целевой бинарный файл **только на чтение**.
2. Создайте AEAD-сокет AF_ALG, привязанный к `authesncs(hmac(sha256),cbc(aes))`.
3. Отправьте небольшой фрагмент полезной нагрузки через сокет AF_ALG с флагом `MSG_MORE`, сообщая ядру, что ожидаются ещё данные.
4. С помощью `splice()` передайте содержимое целевого файла из fd → pipe → сокет AF_ALG.
5. Из-за ошибки CoW ядро **записывает байты полезной нагрузки атакующего в страницы page-cache целевого файла** вместо их корректной изоляции.
Эксплойт повторяет это для каждого 4-байтового окна, пока все кэшированные страницы целевого бинарного файла не будут перезаписаны специальной полезной нагрузкой.
Права на запись в файл не требуются. Файл на диске остаётся неизменным — повреждён только page-cache в памяти.
### 2\. Распространение между контейнерами через совместное использование слоёв образов
Контейнерные рантаймы используют overlay-файловые системы. Когда два контейнера используют один и тот же слой образа, ядро обслуживает чтение файлов из **одних и тех же страниц page-cache**.
Атакующий собирает образ PoC `FROM` того же базового образа, что и целевой привилегированный DaemonSet. Поскольку оба контейнера используют один и тот же lower-dir в overlay, бинарные файлы в общем слое сопоставляются с **идентичными страницами page-cache**.
Когда непривилегированный PoC-контейнер повреждает page-cache бинарного файла, повреждение немедленно становится видимым привилегированному контейнеру на том же узле — без какой-либо связи между контейнерами.
### 3\. Привилегированное выполнение целевым DaemonSet
Когда привилегированный DaemonSet в следующий раз выполнит любой повреждённый бинарный файл (в рамках своего обычного цикла работы), ядро загрузит повреждённые страницы page-cache. Полезная нагрузка атакующего выполняется с полными привилегиями DaemonSet, потенциально включая:
* Полный root на узле
* Все capabilities
* Доступ к namespace узла (network, PID, mount)
Полезная нагрузка в этом PoC (`payload/payload.c`) просто монтирует корневую файловую систему узла и записывает файл-маркер в `/root/res` как доказательство выполнения кода на уровне узла.
### Схема потока атаки
root@kitploit:~
┌──────────────────────────┐ ┌──────────────────────────┐
│ 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:
### Alibaba Cloud ACK

### Amazon EKS

### Google GKE

Во всех трёх случаях **непривилегированный** PoC-pod успешно записал файл-маркер `[*] success` в файловую систему узла, что доказывает выполнение кода на уровне узла через привилегированный DaemonSet kube-proxy.
Полные руководства (анализ слоёв образа, шаги сборки, развёртывание):
* **EKS** : docs/eks-poc.md
* **GKE** : docs/gke-poc.md
## kube-proxy как конкретный пример
В этом PoC в качестве цели используется kube-proxy, поскольку это один из самых распространённых привилегированных DaemonSet в кластерах Kubernetes. Предоставлены три варианта:
* **По умолчанию (ACK / апстрим)** : собран из `FROM registry.k8s.io/kube-proxy:v1.35.2` (см. `Dockerfile`)
* **EKS** : собран из `FROM public.ecr.aws/eks-distro-build-tooling/eks-distro-minimal-base-iptables:2026-03-11-1773190710.2023` (см. `Dockerfile.eks`)
* **GKE** : собран из `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`.
**Важные оговорки:**
* kube-proxy вызывает `ipset` только при настройке в режиме **ipvs**. Режим по умолчанию (`iptables`) не использует `ipset`. План по выводу ipvs из эксплуатации см. в kubernetes/enhancements#5495.
* Некоторые управляемые дистрибутивы Kubernetes (например, отдельные облачные провайдеры) запускают kube-proxy как **непривилегированный** контейнер, что ограничивает последствия побега.
* PoC нацелен на несколько бинарных файлов (`ipset`, `nft`, `xtables-legacy-multi`, `xtables-nft-multi`), чтобы охватить различные режимы прокси, но их вызов зависит от конфигурации кластера.
**Если в вашем кластере kube-proxy не привилегирован, принцип атаки всё равно работает** — вам просто нужно найти другой привилегированный DaemonSet, который разделяет слои образа с базовым образом, из которого вы можете собрать свой.
### Обобщение на другие цели
Чтобы адаптировать этот PoC для другого привилегированного DaemonSet:
1. Определите привилегированный DaemonSet, работающий в кластере (агенты мониторинга, CNI-плагины, сборщики логов и т.д.).
2. Соберите образ PoC `FROM` того же базового образа, который использует этот DaemonSet.
3. Определите бинарные файлы в общем слое, которые DaemonSet будет выполнять во время своей обычной работы.
4. Повредите page-cache этих бинарных файлов с помощью эксплойта.
## Структура репозитория
root@kitploit:~
.
├── 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
## Предварительные требования
* Go 1.25+
* Кросс-компилятор для nolibc-полезной нагрузки (по умолчанию: `x86_64-linux-gnu-gcc`)
* Docker / Buildx
* Кластер Kubernetes с **привилегированным DaemonSet** , который разделяет слои образа с PoC-образом (пример по умолчанию нацелен на kube-proxy)
* `imagePullPolicy: IfNotPresent` у целевого DaemonSet (значение по умолчанию в Kubernetes)
* Ядро Linux **до** исправления CVE-2026-31431
## Сборка
### ACK / апстрим Kubernetes
root@kitploit:~
# 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
### Amazon EKS
root@kitploit:~
# 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):
root@kitploit:~
make build-eks CC=aarch64-linux-gnu-gcc GOARCH=arm64
### Google GKE
root@kitploit:~
# 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`:
root@kitploit:~
make docker-build-gke CC=aarch64-linux-gnu-gcc GOARCH=arm64 PLATFORM=linux/arm64
## Использование
### Развертывание PoC
root@kitploit:~
# 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. Он:
1. Запускает `/bin/copyfail`, чтобы повредить page-cache целевых бинарных файлов в общем слое образа.
2. Переходит в бесконечный сон, чтобы pod оставался запущенным для наблюдения.
### Проверка побега
После того как целевой привилегированный DaemonSet в следующий раз выполнит повреждённый бинарный файл (для kube-proxy это обычно происходит в течение секунд благодаря циклу согласования), проверьте **узел** :
root@kitploit:~
# 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.
### Очистка
root@kitploit:~
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`) — это программа только для проверки, которая записывает файл-маркер. Чтобы собрать собственную полезную нагрузку:
1. Отредактируйте `payload/payload.c`. Программа собирается с `nolibc` (минимальной C-библиотекой ядра) для статического бинарного файла без зависимостей.
2. Выполните `make payload` для кросс-компиляции.
3. Скомпилированная полезная нагрузка встраивается в Go-бинарный файл через `//go:embed`.
## Затронутые версии
* **Ядро Linux** : все версии до патча CVE-2026-31431.
* **Kubernetes** : любая версия, использующая неисправленное ядро узла. Уязвимость находится в ядре, а не в самом Kubernetes. Kubernetes лишь предоставляет контекст выполнения (общие слои образов + привилегированные DaemonSet), который повышает воздействие с локального повреждения page-cache до полного побега из контейнера.
## Меры по смягчению
* **Обновите ядро.** Это окончательное исправление.
* **Включите изоляцию слоёв образов.** Некоторые рантаймы поддерживают снапшоты файловой системы для каждого контейнера, которые предотвращают совместное использование page-cache.
* **Минимизируйте привилегированные DaemonSet.** Сократите количество рабочих нагрузок с повышенными привилегиями; используйте принцип наименьших привилегий.
* **Удалите ненужные capabilities** у DaemonSet, которым не требуется строго `privileged: true`.
* **Ограничьте планирование pod** , чтобы не допускать попадания недоверенных рабочих нагрузок на узлы, где работают привилегированные DaemonSet с общими базовыми образами.
* **Используйте отдельные базовые образы** для привилегированных рабочих нагрузок, чтобы снизить вероятность совместного использования слоёв с недоверенными контейнерами.
### Примеры мер по смягчению
* **Встроенное правило смягчения vArmor** : copy-fail-mitigation блокирует вектор эксплойта, запрещая контейнерам создавать сокеты `AF_ALG`. Правило доступно через механизмы AppArmor и BPF.
* **eBPF-смягчение для Kubernetes** : iwanhae/copyfail-ebpf-k8s предоставляет пример смягчения на основе eBPF для Kubernetes для CVE-2026-31431.
## Благодарности
* **Обнаружение и раскрытие CVE-2026-31431** : Theori / Xint
* **Кроссплатформенная C-полезная нагрузка** : Tony Gies (LGPL-2.1-or-later OR MIT)
* **nolibc** : selftests ядра Linux (`tools/include/nolibc/`)
## Лицензия
Go-код эксплойта в этом репозитории предоставляется как есть, в исследовательских целях.
Полезная нагрузка (`payload/payload.c`) является производной от copy-fail-c и распространяется по двойной лицензии **LGPL-2.1-or-later** OR **MIT**. См. LICENSE-LGPL и LICENSE-MIT.