Две недели назад исследователи раскрыли Copy Fail — новую и исключительно опасную уязвимость локального повышения привилегий в Linux. Copy Fail эксплуатирует повреждение памяти ядра без внедрения кода в работающее ядро. Она даёт атакующему повторяемую, контролируемую четырёхбайтовую запись в page cache любого читаемого файла, то есть позволяет переписать закэшированное содержимое файлов в файловой системе Linux.
Copy Fail путает код ядра, обрабатывающий IPSec ESP Extended Sequence Numbers (authencesn). Этот код доступен непривилегированным пользователям через AF_ALG сокеты — интерфейс к подсистеме криптографии ядра. Атакующий заставляет authencesn думать, что он работает с одноразовой рабочей памятью, хотя на самом деле изменяет ссылку на page cache.
Page cache общий для всех контейнеров. Контейнерные изоляции (mount, network, PID и т.д.) не создают собственных address_space — они разделяют закэшированные folios, когда обращаются к одному и тому же inode на хосте. В Kubernetes корневая файловая контейнера обычно собрана из overlayfs: нижние слои (образы) повторно используются по хешу слоя. Если два контейнера на одной ноде используют один и тот же unpacked слой, их нижние файлы привязаны к одному хостовому inode и address_space. Copy Fail записывает прямо в этот общий нижний слой, минуя copy-up и обычное отслеживание записи.
Два сценария атаки:
Сценарий 1: кросс-контейнерное отравление. Из скомпрометированного пода (или даже просто с правами create pods) атакующий выбирает файл в широко используемом слое (например, Python-модуль в python:3.12-slim). Через Copy Fail он переписывает закэшированные байты. Как только другой под на той же ноде с тем же образом (или собранный из того же базового слоя) импортирует этот модуль, CPython скомпилирует уже изменённый код. Атакующий может внедрить бэкдор, не затрагивая образ на диске — все сканеры образов и дисковые проверки не увидят изменений, они живут только в page cache. Если атакующий может создать под и указать nodeAffinity, он может целенаправленно попасть на ноду жертвы, используя тот же базовый слой.
Сценарий 2: побег из контейнера. Использует тот факт, что runc после исправления CVE-2019-5736 монтирует свой бинарник read-only в каждый контейнер через bind mount. Атакующий изнутри контейнера запускает процесс, который заставляет runc выполниться (например, перезаписывает /bin/sh на #!/proc/self/exe). Находит PID этого runc в своём namespace, открывает /proc/<pid>/exe и применяет Copy Fail для записи в page cache, заменяя ELF-заголовок хостового бинарника на вредоносный. При следующем вызове runc (например, kubectl exec администратора) хост выполнит уже изменённый код с правами root. Демонстрация показала получение обратной оболочки на хосте EC2 в AWS.
Обнаружение и защита: Стандартные сканеры образов и дисков неэффективны. Runtime EDR с хешированием страниц работающих процессов может обнаружить изменения. Лучшая защита — установить патч ядра (a664bf3d603d), заблокировать AF_ALG через seccomp (большинству нагрузок он не нужен) и для жёсткой изоляции использовать VMs, microVMs (Kata, Fargate) или gVisor — они дают отдельные page cache.