Sploitus

Exploit for Copy-Fail-CVE-2026-31431-Kubernetes-PoC

kitploit · 2026-08-24

Exploit Code

MARKDOWN369 lines
## 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)  
---|---|---  
![ACK](https://assets.kitploit.com/production/public/readmes/7473/0e4664604fd49de2e7f4a439179bfaf5c19a729717540d07ee80e39b451bd2af.png)| ![EKS](https://assets.kitploit.com/production/public/readmes/7473/6b0c4342c622a3c7c246878b82c8a844df3d9aae79fbb3b3b8514e899e2426ae.png)| ![GKE](https://assets.kitploit.com/production/public/readmes/7473/4d58c3b5245b8e43e20f93251cb0c710b26dc777219d31308266e0085cada03c.png)  
  
> **Отказ от ответственности:** Этот репозиторий опубликован только в образовательных и защитных целях. Используйте его исключительно в системах, которыми вы владеете или на тестирование которых имеете явное разрешение.

## Предыстория

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

![Результат PoC на ACK](https://assets.kitploit.com/production/public/readmes/7473/0e4664604fd49de2e7f4a439179bfaf5c19a729717540d07ee80e39b451bd2af.png)

### Amazon EKS

![Результат PoC на EKS](https://assets.kitploit.com/production/public/readmes/7473/6b0c4342c622a3c7c246878b82c8a844df3d9aae79fbb3b3b8514e899e2426ae.png)

### Google GKE

![Результат PoC на GKE](https://assets.kitploit.com/production/public/readmes/7473/4d58c3b5245b8e43e20f93251cb0c710b26dc777219d31308266e0085cada03c.png)

Во всех трёх случаях **непривилегированный** 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.