ПубликацииCVE-2026-31431 (Copy Fail): как Appbox снизил риск

CVE-2026-31431 (Copy Fail): как Appbox снизил риск

5 мин чтения
от rid

На прошлой неделе была раскрыта серьезная уязвимость ядра Linux, CVE-2026-31431. Рассказываем, как Appbox снизил риск и развернул исправление ядра Debian.

CVE-2026-31431 (Copy Fail) - как мы снизили риск

На прошлой неделе была раскрыта серьезная локальная уязвимость повышения привилегий в ядре Linux: CVE-2026-31431, получившая название "Copy Fail". Она затрагивает широкий диапазон версий ядра в современных дистрибутивах Linux, а публичный эксплойт появился вскоре после раскрытия.

Я хочу прямо рассказать, что произошло, что это значит для Appbox и что мы уже сделали. Короткая версия: благодаря тому, как устроены наши контейнеры, приложения, запущенные с remapping пользовательских пространств имен, уже были защищены от превращения этой уязвимости в root-доступ на хосте еще до развертывания исправленных ядер. Вчера (4 мая 2026 года) мы также провели экстренное обслуживание и обновили каждый сервер до исправленного ядра, так что эта ошибка теперь полностью устранена во всей нашей инфраструктуре.

Что такое "Copy Fail"?

CVE-2026-31431 — это ошибка в криптографическом API ядра AF_ALG. При привязке к определенному шифру (authencesn(hmac(sha256),cbc(aes))) и злоупотреблении тем, как ядро сплайсит страницы во временный буфер, непривилегированный локальный пользователь может записывать произвольные 4-байтовые фрагменты напрямую в page cache файлов, которые он не должен иметь права изменять, включая SUID-бинарники вроде /usr/bin/su.

После перезаписи копии su в page cache маленьким вредоносным ELF следующая попытка запуска выполняет код атакующего как UID 0. Для хоста это полный компромисс.

Если нужны жесткие технические детали (разбор shellcode, трассировки syscall и все остальное), у Andrea Veri отличный материал: CVE-2026-31431: Copy Fail vs. rootless containers. Он также показал на практике, с eBPF и доказательствами через uid_map, именно то, о чем мы поговорим дальше: пользовательские пространства имен нейтрализуют этот эксплойт.

Почему приложения Appbox уже были защищены

Эксплойт рассчитывает на то, что setuid(0) действительно дает root-доступ на хосте. В контейнере с включенными пользовательскими пространствами имен это работает иначе.

Пользовательские пространства имен отображают UID-пространство контейнера на другой, непривилегированный диапазон UID на хосте. Поэтому, когда эксплойт срабатывает внутри контейнера и setuid(0) возвращает успех, получившаяся "root"-оболочка отображается на обычного непривилегированного пользователя хоста, а не на настоящего root. Shellcode выполняется, приглашение меняется на #, но на хосте это выглядит так:

podman     27943  0.0  0.0   2984  2028 pts/1    S+   22:15   0:00 sleep 100

И все. Нет доступа к файлам хоста, нет /etc/shadow, нет root-доступа на хосте. Граница namespace у контейнера не дает этому превратиться в escape с root-доступом к хосту.

На уровне платформы каждый Appbox получает собственный Docker daemon, запущенный с включенным user namespace remapping. В псевдокоде это выглядит примерно так:

start_docker_daemon(
  userns_remap = appbox_id,
  container_namespace = appbox_id
)

Это отображение подкрепляется отдельным диапазоном subordinate UID/GID на хосте:

write "/etc/subuid" -> "<appbox-id>:<host-subuid-start>:65536"
write "/etc/subgid" -> "<appbox-id>:<host-subgid-start>:65536"

При этом определения приложений все еще могут явно переопределять UsernsMode, если приложению нужна особая обработка:

container_config = {
  userns_mode: app.userns_mode,
  network_mode: app.network_mode,
  ...
}

Поэтому точное описание такое: user namespace remapping является частью платформенного значения по умолчанию, а исключения для отдельных приложений задаются явно. Есть небольшое число приложений, которым нужны другие настройки, потому что они предоставляют функции, требующие дополнительных capabilities, и такие случаи обрабатываются отдельно.

Иными словами: для приложений, работающих в обычной remapped-конфигурации, Copy Fail не мог превратить компрометацию контейнера в root-доступ на хосте.

Что мы все равно хотели исправить

Пользовательские пространства имен останавливают escape на хост, но не устраняют саму ошибку внутри контейнера. Атакующий все еще мог бы повысить привилегии до "container root" в границах приложения, что, хоть и изолировано, все равно является плохой ситуацией. Само ядро оставалось уязвимым, и мы хотели это убрать.

Как только мы узнали об ошибке, мы сразу начали оценивать нашу экспозицию. Хотя user namespace remapping уже блокировал сценарий escape на хост для нашей обычной конфигурации приложений, мы все равно хотели как можно быстрее устранить базовую ошибку ядра.

Что мы сделали вчера

В воскресенье вечером мы запланировали экстренное обслуживание на понедельник, 4 мая 2026 года — самое раннее окно, в которое мы могли развернуть обновление ядра по всему парку серверов.

Во время этого окна:

  • Каждый хост был обновлен официальными пакетами ядра Debian, содержащими исправление "Copy Fail".
  • Цель была простой: полностью убрать уязвимый путь в ядре, а не только полагаться на уже существующую изоляцию namespace.

Обслуживание завершено. Каждый хост Appbox теперь работает на исправленном ядре.

Что это значит для вас

  • Вам не нужно было ничего делать. Ваши приложения продолжали работать, а единственным заметным для клиентов эффектом могли быть короткие перебои во время окна обслуживания.
  • Даже до вчерашнего исправления user namespace remapping означал, что эта ошибка не могла превратить компрометацию обычного приложения в root-доступ на хосте. Именно для таких сценариев и существует эта модель изоляции.
  • В дальнейшем наша политика не меняется: пользовательские пространства имен остаются включенными по умолчанию, а немногочисленные исключения проходят дополнительную проверку безопасности.

Общая картина

Это одна из тех CVE, где заголовок ("локальное повышение привилегий, публичный эксплойт, затронуто каждое современное ядро") звучит катастрофически, и для многих shared-tenant окружений ситуация действительно была такой. Для нас она не стала катастрофой по той же причине, по которой мы годы назад приняли архитектурные решения, иногда стоившие нам части совместимости: мы запускаем приложения с принципом наименьших привилегий, встроенным прямо в контейнерный слой, а не прикрученным постфактум.

Защита в глубину действительно работает. Ошибка на уровне ядра? Ее поймал слой namespace. Ошибка на уровне namespace? Capability drops и seccomp отлавливают большую часть оставшегося. Мы не делаем вид, что какой-то один слой идеален. Мы просто следим, чтобы всегда был следующий.

Если у вас есть вопросы об этой уязвимости или о том, как настроены ваши конкретные приложения, пожалуйста, свяжитесь с нами.


Есть вопросы? Напишите на support@appbox.co или создайте тикет на billing.appbox.co.

rid

rid

Software Engineer | Writer | Designer