Logo Craft Homelab Docs Нейросети Хостинг Обучение Контакты
CVE-2026-98432: гонка в TCP-рукопожатии Linux и что проверить на своих серверах Трендовые github проекты в нашем телеграм канале. Подпишись →
6 ноября 2026 г.

Use-after-free в tcp_v4_syn_recv_sock: десять лет в ядре, две строки фикса

В октябре 2026 года в ядре Linux исправили баг, который срабатывает до того, как приложение получает хоть один байт: на этапе TCP-рукопожатия, без аутентификации, на любом открытом IPv4-порту. Уязвимость получила номер CVE-2026-98432 и в итоге оценку CVSS 9.8. Код с ошибкой попал в ядро 4.4 в январе 2016 года и с тех пор был практически в каждом дистрибутиве.

Откуда взялась гонка

В 2015 году сетевой стек упирался в блокировку listening-сокета: каждый пакет для ещё не установленного соединения брал lock на листенере, и на серверах с десятками тысяч новых соединений в секунду все ядра CPU ждали одну блокировку. Патч Эрика Дюмазе 079096f103fa («tcp/dccp: install syn_recv requests into ehash table») перенёс полуоткрытые соединения в общую хэш-таблицу ehash, и обрабатывать их стали параллельно. Нагрузка на листенер упала примерно вдвое.

Цена оптимизации: код, который раньше шёл последовательно, теперь могут одновременно выполнять два CPU над одними и теми же структурами. В одном месте не договорились, кто освобождает общую память.

Механика бага

Пока клиент не прислал финальный ACK, ядро хранит request-сокет — черновик соединения с адресами, портами, номерами последовательности и указателем ireq_opt на IP-опции. IP-опции из RFC 791 (Record Route, Timestamp и т. п.) позволяют дописать к 20-байтному заголовку IPv4 до 40 байт. В живом трафике они почти не встречаются, но ядро обязано их принять, сохранить и вернуть в SYN-ACK.

Когда приходит ACK, tcp_v4_syn_recv_sock() создаёт дочерний сокет — тот, что вернёт accept(). Опции при этом не копируются: дочерний сокет получает тот же указатель. Две структуры ссылаются на один кусок памяти без общей блокировки.

Сценарий атаки целиком контролирует отправитель:

  1. SYN с IP-опциями — ядро выделяет память под опции.
  2. ACK — CPU A создаёт дочерний сокет, который делит опции с черновиком.
  3. Повторный SYN (ретрансмит, штатное поведение TCP) — CPU B находит черновик и собирает новый SYN-ACK в ip_options_build(), читая опции по старому указателю.
  4. Обрыв соединения — CPU A уничтожает дочерний сокет, inet_sock_destruct() вызывает kfree() для опций.
  5. CPU B продолжает читать освобождённую память.

Окно между шагами 3 и 4 — микросекунды, и большинство попыток промахивается. Проигранная гонка при этом ничего не стоит: одно рукопожатие не состоялось, сервер работает, в логах пусто. Пробовать можно тысячи раз в секунду.

Дальше в дело вступает аллокатор SLUB. Он раскладывает объекты по кэшам одного размера и отдаёт свободные по принципу LIFO, так что освобождённый кусок, скорее всего, достанется следующему выделению того же размера. Сетевой стек выделяет объекты на каждый входящий пакет, поэтому атакующий может подложить на место опций свои данные. Если в поле optlen окажется чужое число, ядро скопирует в исходящий SYN-ACK соответствующее количество байт, включая соседние объекты из того же слэба. Это утечка памяти ядра наружу, а при подходящей раскладке — управляемая запись за границу объекта.

IPv6 не затронут: tcp_v6_syn_recv_sock() копирует опции.

Фикс

Коммит e3f33b0a1d89 меняет два файла, net/ipv4/af_inet.c и net/ipv4/tcp_ipv4.c, по одной строке в каждом: kfree() заменён на kfree_rcu(). Память освобождается после того, как все текущие RCU-читатели, включая CPU, собирающий SYN-ACK, закончат работу. В других местах ядро освобождало IP-опции именно так.

Хронология и разброс оценок

ДатаСобытие
5 октябряФикс принят в апстрим и уходит в stable-ветки
15 октябряОпубликована CVE-2026-98432, без оценки
16–22 октябряИсправленные ядра в Debian и Oracle UEK
24 октябряКоманда CVE ядра выставляет CVSS 9.8
27 октябряAnthropic публикует описание remote-root эксплойта
29 октябряRed Hat поднимает оценку с 5.9 до 8.1; Go Security Team выпускает рекомендации
1–2 ноябряФикс в RHEL 8/9/10, AlmaLinux, Oracle RHCK
2 ноябряUbuntu: для 22.04–26.10 фикс «в работе», 16.04–20.04 останутся без него

Факты у всех вендоров одинаковые, расходится ответ на вопрос «можно ли на практике выиграть гонку». Red Hat стартовал с 5.9, Oracle, SUSE и Amazon поставили 8.1 с высокой сложностью атаки, Ubuntu — 8.3. Если сканер уязвимостей и SLA на патчи у вас завязаны на вендорскую оценку, на EL-хостах баг две недели числился Moderate. RHEL-сервер с открытым 443 получил исправление через 27 дней после апстрим-фикса.

По заявлению Anthropic, внутренняя неопубликованная модель после начального промптинга удалённо, без учётных данных, получила утечку адреса ядра (обход KASLR) за 48 минут и выполнение команды от root ещё за пять. Код эксплойта не выложен, независимого воспроизведения нет, эксплуатация в дикой природе не зафиксирована, в CISA KEV баг не внесён. Сколько ушло на поиск самой гонки и сколько человеческой работы стоит за «начальным промптингом», неизвестно.

MPTCP, который включился сам

Показанная цепочка требует MPTCP-листенера. Начиная с Go 1.24 любой Go-сервис на Linux по умолчанию открывает MPTCP-листенер, если ядро это разрешает. API-шлюз, reverse proxy или экспортер метрик на Go держат такой листенер, хотя никто его специально не включал. Go Security Team отдельно предупредила: баг не специфичен для MPTCP, и системы без него тоже могут быть уязвимы к похожим атакам.

Что сделать на своих хостах

Обновить ядро и перезагрузиться. Исправленные ядра есть в Debian, Oracle Linux, RHEL и AlmaLinux. Для Ubuntu на 2 ноября фикс ещё не вышел. Установленный пакет без ребута ничего не закрывает, поэтому сверяйте версию запущенного ядра:

uname -r

Включить автоматические обновления безопасности: unattended-upgrades в Debian/Ubuntu, dnf-automatic в RHEL-семействе. Полезная метрика здесь одна — число дней между апстрим-фиксом (5 октября) и перезагрузкой ваших машин. Десяток серверов в homelab перезагружается за вечер без согласований, и это реальное преимущество маленькой инфраструктуры.

Посмотреть, кто слушает MPTCP:

ss -H -lMn

Каждая строка вывода — MPTCP-листенер. Если там есть сервисы, о которых вы не знали, это, скорее всего, Go-бинарники, собранные на 1.24 и новее.

Отбрасывать IPv4-пакеты с IP-опциями, если патч для вашего дистрибутива ещё не вышел. Легитимный трафик их практически не использует. В nftables заголовок без опций имеет длину 5 слов по 32 бита, поэтому правило может выглядеть так:

nft add rule inet filter input ip hdrlength != 5 meta l4proto tcp drop

Перед применением проверьте имя таблицы и цепочки в своём ruleset.

Убрать лишнее из интернета. Баг достижим только через открытый IPv4-порт. Админки — за VPN, базы данных и служебные сервисы — без прямого доступа снаружи, остальное закрыть файрволом.

Выводить из эксплуатации ядра без обновлений. Хост на LTS-ядре, до которого фикс не доедет (например, Ubuntu 16.04–20.04 без расширенной поддержки), остаётся самой вероятной точкой входа. Составьте список «какой хост, какой дистрибутив, какое ядро» и начните с машин, у которых есть публичные порты.

Не ждать CVSS и KEV. В этой истории вендорская оценка две недели отставала от публично описанной root-цепочки. Если приоритизация патчей у вас строится только на CVSS, для сетевых багов ядра с удалённым вектором ставьте обновление в работу в день выхода фикса в вашем дистрибутиве, не дожидаясь пересчёта оценки.

Хранить логи границы. Проигранная гонка выглядит как обычный оборванный handshake. Без записи входящего трафика на периметре на вопрос «атаковали ли нас» ответить будет нечем.

Если хост принадлежит провайдеру, ядро обновляет он. Спросите у него дату перезагрузки после 5 октября.