
Подвержен ли ваш Linux-хост уязвимости CVE-2026-98432?
Проверяет, может ли Linux-хост быть уязвим к CVE-2026-98432 — use-after-free в обработке рукопожатия IPv4 TCP в ядре. Часть проекта shouldipanic.ch.
SYN-пакет с опциями IPv4 (Record Route, Timestamp) достигает кода, который читает опции после того, как они, возможно, уже освобождены. Достаточно любого открытого порта IPv4 TCP, вход не требуется. Уязвимость присутствует во всех ядрах начиная с 4.4 (2016) вплоть до исправленного выпуска каждой ветки: 5.10.272, 5.15.223, 6.1.190, 6.6.159, 6.12.113, 6.18.56, 7.2.10, 7.3. CNA ядра оценивает её в 9.8; сообщалось о работающем удалённом эксплойте для получения root против серверов с MPTCP-листенерами (не опубликован). Исправление невидимо в сетевом трафике: исправленное ядро отвечает точно так же, как неисправленное.
Это определяет, что может сказать каждый режим:
--on-host читает работающее ядро на самой машине: его версию,
changelog пакета и то, ждёт ли более новое ядро перезагрузки.Server и показывает, выпустил ли этот дистрибутив
исправленное ядро. Это сильная подсказка, а не вердикт: за
обратным прокси или внутри контейнера ядро принадлежит другой машине.kernel98scan существует в двух формах:
kernel98scan.py: один файл Python без зависимостей. На хосте
--on-host даёт настоящий вердикт; при указании адресов он выполняет
фингерпринтинг дистрибутивов по диапазонам IP, со сканированием портов и обнаружением vhost.
curl -LO https://github.com/akaihigurashi/kernel98scan/releases/latest/download/kernel98scan.py
python3 kernel98scan.py --on-host # real verdict for THIS machine
Python 3.8 или новее, ничего устанавливать не нужно. Примеры:
python3 kernel98scan.py --on-host # read the running kernel
python3 kernel98scan.py 203.0.113.10 # outside: distribution + fix status
python3 kernel98scan.py 203.0.113.0/24 # a whole range from outside
python3 kernel98scan.py 203.0.113.10 --ip-options # + do IP-options packets get through?
python3 kernel98scan.py 203.0.113.10 -o out.csv # CSV output
python3 kernel98scan.py -h # all options
На хосте (--on-host, код выхода 2 / 1 / 0 / 3):
| Статус | Значение |
|---|---|
VULNERABLE | Ничто не указывает на исправление в работающем ядре |
CHECK | Исправленное ядро установлено, но всё ещё работает старое: требуется перезагрузка |
FIXED | В changelog ядра упоминается CVE, или обычное upstream-ядро имеет исправленную версию |
Ядра вендоров (Ubuntu, RHEL, Proxmox) переносят исправления на старые номера версий, поэтому
для них учитывается только changelog. Он также показывает, включён ли MPTCP (описанный
эксплойт проходит через MPTCP-листенеры) и tcp_migrate_req.
Снаружи:
| Статус | Значение |
|---|---|
POSSIBLY VULNERABLE | Баннер показывает дистрибутив Linux: либо в нём ещё нет исправленного ядра (Ubuntu, RHEL и их пересборки на сегодня), либо оно есть, но его нужно установить и загрузиться с него |
UNKNOWN | Дистрибутив не виден, или отвечает прокси/CDN |
NOT LINUX | Заголовок Server указывает на не-Linux хост |
VULNERABLE и PATCHED никогда не сообщаются снаружи. Статус исправления по
дистрибутивам — это таблица в check.py (FIX_STATUS, FIXED_TABLE),
обновляемая с каждым релизом вендора.
| Опция | Что делает |
|---|---|
--on-host | Читает работающее ядро на этой машине и выдаёт реальный вердикт |
--ip-options | Одно соединение, SYN которого несёт IP-опцию Record-Route (см. ниже) |
-o FILE | Также записывает все результаты в CSV |
-a / -v | Показывать скрытые результаты / показывать доказательства |
-d | Отладка: каждый запрос, заголовок и совпадение |
-q | Без баннера (для скриптов) |
-V | Вывести версию |
-h | Все опции |
--ip-options (по желанию, только из командной строки)Открывает одно обычное соединение и одно, SYN которого несёт пустую IP-опцию
Record-Route, и сообщает, доходят ли такие пакеты до хоста (reach), отбрасываются
ли они по пути (filtered) или порт закрыт (noport). Именно такую форму пакетов
использует уязвимость, поэтому опция отключена по умолчанию и никогда не запускается на
веб-странице: используйте её на своих хостах, например чтобы убедиться, что ваш файрвол
отбрасывает IP-опции. Одно подключение не может выиграть гонку, необходимую для уязвимости, а
результат filtered может означать любую сеть на пути.
nft add table ip cve98432
nft add chain ip cve98432 pre '{ type filter hook prerouting priority -300; }'
nft add rule ip cve98432 pre meta l4proto tcp ip hdrlength != 5 drop
sysctl -w net.mptcp.enabled=0 # if you don't need MPTCP
Отбрасывает IPv4 TCP-пакеты, содержащие любые IP-опции. Не сохраняется после перезагрузки и не заменяет исправленное ядро.
docker run -d --name kernel98scan --restart unless-stopped \
-p 127.0.0.1:8092:8092 \
-e PANIC_ORIGIN=https://kernel98scan.example.ch \
-v kernel98scan-data:/data \
ghcr.io/akaihigurashi/kernel98scan:latest
Или с помощью Compose: скачайте docker-compose.yml, задайте
PANIC_ORIGIN, затем выполните docker compose up -d. Контейнер слушает только
localhost; разместите перед ним обратный прокси с TLS
(пример nginx).
| Переменная | По умолчанию | Назначение |
|---|---|---|
PANIC_ORIGIN | – | Публичный адрес. Запросы с других origin отклоняются. Задайте его. |
PANIC_OPERATOR / PANIC_CONTACT | – | Необязательный раздел контактов внизу страницы |
PANIC_IMPRINT_URL | – | Необязательная ссылка на существующий импрессум |
PANIC_TRUSTED_PROXY | 127.0.0.1,::1 | Прокси, чей заголовок X-Real-IP считается доверенным |
PANIC_DENY_HOSTS / PANIC_DENY_IPS | – | Никогда не проверять эти домены / адреса |
PANIC_AUDIT_DIR | /data/audit | Ежедневные файлы журнала аудита |
PANIC_RETENTION_DAYS | 30 | Файлы аудита старше этого срока удаляются |
Страница имеет те же защиты, что и php2scan: флажок согласия, журнал аудита, отказ клиентам VPN/Tor/дата-центров, ограничение частоты запросов, кэш результатов, только публичные имена хостов с привязкой соединения к проверяемому адресу, хосты, которые никогда не проверяются (известные сервисы, 100 самых посещаемых сайтов, правительственные сайты, страны под санкциями), и отсутствие доступа из стран под санкциями. Веб-страница загружает только стартовую страницу; она никогда не отправляет IP-опции.