Ещё несколько циклов назад цифра в пятьсот зафиксированных уязвимостей на один релиз ядра Linux считалась высокой. Сейчас счётчик приближается к двум тысячам, и для администраторов это не абстрактная статистика, а вполне ощутимая проблема: ритм выхода патчей безопасности вырос кратно, а доля записей, реально требующих экстренного вмешательства на конкретном сервере, в этом потоке заметно снизилась. Разобраться, что патчить в первую очередь, а что можно поставить в обычный график обновлений, стало отдельной технической задачей.
Как вырос счётчик уязвимостей ядра от пятисот до почти двух тысяч за релиз
По данным, которые Грег Кроа-Хартман представил перед конференцией Kernel Recipes 2026, динамика выглядит так: релизы с 6.9 по 6.19 держались на уровне около пятисот зафиксированных CVE за цикл, начиная с версии 7.0 показатель перешагнул тысячу, а 7.2 пробил отметку в полторы тысячи. Для готовящегося релиза 7.3 прогнозируется приближение к двум тысячам записей, если текущий темп сохранится. При этом сама кодовая база ядра выросла до отметки в сорок один миллион строк, и чем больше исходного кода, тем больше потенциальных мест для обнаружения проблем, даже если абсолютное число реальных дефектов в пересчёте на тысячу строк кода не увеличилось.
Почему языковые модели находят так много новых CVE в сорока миллионах строк кода
Основной драйвер роста, который называют и сами разработчики ядра, - это массовое применение ИИ и языковых моделей для автоматического анализа исходного кода. Такие инструменты методично проходят по всей кодовой базе, включая старые и малопопулярные драйверы, годами не привлекавшие внимания живых ревьюеров, и генерируют отчёты о потенциальных уязвимостях в объёме, недоступном для ручного аудита силами обычной команды мейнтейнеров. Большая часть находок при этом оказывается низкоприоритетной и относится к коду staging-драйверов или устройств, которые физически отсутствуют на подавляющем большинстве серверов и рабочих станций.
Побочным эффектом этого потока стала перегрузка мейнтейнеров сетевой подсистемы, которые публично отмечали, что объём входящих патчей, сгенерированных или частично составленных с помощью ИИ, требует непропорционально много времени на ревью по сравнению с их реальной ценностью для безопасности. Есть и обратная сторона процесса: избыточная активность автоматизированного поиска уязвимостей в этом году стала одним из аргументов в пользу ускоренной чистки откровенно устаревшего кода - драйверов для железа, которое давно не производится и не эксплуатируется, что снижает общую поверхность атаки на кодовую базу в долгосрочной перспективе.
Показательно, что рост числа CVE совпал по времени с растущим применением тех же языковых моделей уже на стороне самих разработчиков ядра, а не только сторонних исследователей безопасности. Ключевые мейнтейнеры признавали, что используют ИИ-ассистентов для отладки сложных регрессий и составления описаний к патчам в подсистемах с особенно высокой нагрузкой в текущем цикле, поскольку вручную формулировать сопроводительные тексты к тысяче с лишним изменений за одно окно слияния попросту нереалистично по времени. Получается двусторонний эффект: одни и те же инструменты одновременно ускоряют обнаружение проблем в старом коде и помогают быстрее закрывать найденное, но общий объём работы, который нужно провести через ручное человеческое ревью, от этого не сокращается, а зачастую даже растёт.
Как устроен официальный процесс присвоения CVE ядру и при чём тут список рассылки linux cve announce
Важно понимать механику самого присвоения номеров CVE ядру, потому что она отличается от привычной практики коммерческого софта. Согласно официальной документации проекта, идентификатор CVE не выдаётся заранее под предполагаемую уязвимость: он автоматически присваивается уже после того, как исправление попало в стабильное дерево ядра, и команда, отвечающая за нумерацию, привязывает CVE непосредственно к идентификатору коммита с фиксом. Это значит, что формально любое исправление, затрагивающее потенциально небезопасный код, может задним числом получить статус CVE, даже если реальная эксплуатируемость проблемы была близка к нулю. Полный список присвоенных идентификаторов публикуется в архиве рассылки linux-cve-announce, доступной по адресу lore.kernel.org/linux-cve-announce, и именно оттуда удобнее всего забирать свежие записи для последующей автоматической обработки.
Для администраторов это означает, что сырой счётчик CVE на релиз плохо коррелирует с реальным риском для конкретной инсталляции. Часть уязвимостей закрывает проблемы в подсистемах, которые физически не загружены на сервере, часть требует локального доступа с повышенными привилегиями для эксплуатации, а часть относится к архитектурам или конфигурациям, никогда не встречающимся в продакшене конкретной компании. Именно поэтому простое отслеживание количества новых записей без фильтрации по актуальному профилю системы быстро превращается в бесполезный ритуал.
Почему высокий CVSS не значит высокий риск и что даёт система EPSS
Классический показатель CVSS описывает потенциальную тяжесть уязвимости, но не вероятность того, что её реально начнут эксплуатировать в ближайшее время. По данным исследований, легших в основу разработки альтернативной модели, среди уязвимостей с оценкой CVSS семь баллов и выше реальная попытка эксплуатации фиксируется лишь у 2,3 процента записей, то есть подавляющее большинство ресурсов, потраченных на патчинг исключительно по критерию высокого CVSS, уходит впустую с точки зрения реального снижения риска.
Именно для устранения этого разрыва организация FIRST развивает систему EPSS - Exploit Prediction Scoring System, которая с помощью модели машинного обучения присваивает каждой уязвимости вероятность её эксплуатации в ближайшие тридцать дней в виде числа от нуля до единицы. Модель учитывает метаданные записи, доступность публичного эксплойт-кода, данные honeypot-сетей и поведенческие сигналы от систем threat intelligence, а сами оценки обновляются ежедневно. Публичный API FIRST доступен без авторизации по адресу api.first.org/data/v1/epss и принимает параметр cve со списком интересующих идентификаторов через запятую, что делает его удобным для встраивания в скрипты приоритизации без необходимости заводить отдельный аккаунт или ключ доступа.
Как собрать список реально загруженных модулей и сверить его со свежими CVE
Первый практический шаг любой приоритизации - понять, какие подсистемы ядра вообще присутствуют и активны на конкретном сервере, вместо того чтобы держать в уме весь список из тысяч потенциально затронутых компонентов. Базовая команда для этого простая:
lsmod | awk 'NR>1 {print $1}' > loaded-modules.txt
Список активных модулей стоит дополнить статически встроенными в ядро подсистемами, которые lsmod не покажет, поскольку они не оформлены как отдельные загружаемые модули. Для этого удобно свериться с файлом конфигурации сборки текущего ядра:
zcat /proc/config.gz 2>/dev/null | grep '^CONFIG_.*=y' | cut -d= -f1 > builtin-subsystems.txt
Дальше нужно вытянуть свежие записи из архива linux-cve-announce, распарсить в каждой из них список затронутых файлов или подсистем и сопоставить с получившимися списками loaded-modules.txt и builtin-subsystems.txt. Проекты вроде linux_kernel_cves на GitHub уже хранят эти данные в структурированном JSON и избавляют от необходимости парсить письма рассылки вручную, а обновлённая база подтягивается обычным git pull перед каждым циклом проверки.
Как построить скрипт приоритизации уязвимостей на основе EPSS и профиля системы
Логика итогового скрипта сводится к трём последовательным фильтрам, которые применяются к полному списку новых CVE ядра за интересующий период:
- отсеять записи, чьи затронутые файлы или подсистемы не пересекаются ни с loaded-modules.txt, ни с builtin-subsystems.txt, поскольку код, который физически не выполняется на сервере, не создаёт риска независимо от формальной тяжести уязвимости;
- для оставшихся записей запросить актуальный EPSS-балл через API FIRST одним пакетным вызовом на все идентификаторы сразу, чтобы не упереться в лимит запросов, и отсортировать список по убыванию вероятности эксплуатации;
- для верхней части отсортированного списка дополнительно проверить оценку CVSS и характер требуемого доступа, локальный или сетевой, чтобы отделить уязвимости, эксплуатируемые удалённо без аутентификации, от тех, что требуют уже присутствующего в системе привилегированного процесса.
Минимальный прототип такого запроса на Python выглядит следующим образом и годится как основа для более полного инструмента с логированием и сохранением истории:
import requests
def epss_scores(cve_list):
ids = ",".join(cve_list)
response = requests.get(
"https://api.first.org/data/v1/epss",
params={"cve": ids},
timeout=10,
)
response.raise_for_status()
data = response.json()["data"]
return {item["cve"]: float(item["epss"]) for item in data}
def prioritize(cve_list, loaded_subsystems, cve_subsystem_map):
relevant = [c for c in cve_list if cve_subsystem_map.get(c) in loaded_subsystems]
scores = epss_scores(relevant)
return sorted(relevant, key=lambda c: scores.get(c, 0), reverse=True)
Такой скрипт не заменяет полноценную систему управления уязвимостями, но за пару минут выполнения превращает список из нескольких сотен новых записей в короткий приоритетный перечень из десяти-двадцати позиций, действительно требующих внимания на этой неделе.
Публичный доступ к API FIRST ограничен тысячей запросов в минуту на анонимного клиента, и при регулярных проверках имеет смысл кешировать полученные оценки локально хотя бы на сутки, а не запрашивать заново весь список CVE при каждом запуске скрипта. Для этого достаточно сохранять ответ API в простой файл вида epss-cache.json с меткой времени и перед новым запросом проверять, не устарела ли уже сохранённая запись для конкретного идентификатора. При работе с большими списками, охватывающими несколько сотен CVE за раз, API также поддерживает постраничную выдачу через параметры limit и offset, что снимает риск упереться в ограничение по длине строки запроса, если передавать все идентификаторы одним списком через запятую.
Что делать администратору дальше после первой сортировки списка
Верхние строки отфильтрованного и отсортированного по EPSS списка стоит патчить вне обычного графика обновлений, особенно если затронутая подсистема доступна из сети без предварительной аутентификации. Для записей из середины списка разумно дождаться ближайшего планового окна обслуживания, а для CVE, не прошедших фильтр по загруженным модулям, достаточно зафиксировать сам факт существования уязвимости в системе отслеживания на случай, если конфигурация сервера впоследствии изменится и затронутый модуль будет подключён. Такой трёхуровневый подход, основанный на реальном профиле системы и вероятностной оценке эксплуатации, а не на голом количестве присвоенных идентификаторов, позволяет держать нагрузку на команду безопасности предсказуемой даже при продолжающемся росте общего числа CVE на релиз ядра.
Стоит держать в уме и прямую оговорку из официальной документации ядра: закрытие всех известных CVE само по себе не гарантирует защищённости системы, поскольку часть рисков связана не с конкретными пронумерованными уязвимостями, а с общей конфигурацией и настройками безопасности запущенного ядра. По этой причине приоритизацию патчей по EPSS и профилю загруженных модулей стоит рассматривать как дополнение к базовой гигиене конфигурации - отключению неиспользуемых модулей, ограничению прав загрузки новых драйверов и включению штатных механизмов самозащиты ядра, а не как самостоятельную и исчерпывающую стратегию безопасности.