Уязвимость Jenkins плагина CVE-2026-70437: как timing-атака на незащищённый webhook открывает доступ к секретам сборки

В августе 2026 года Jenkins выпустил бюллетень безопасности — двенадцать CVE (CVE-2026-70426–70437) в ядре и плагинах. Один баг — CVE-2026-70437 в Webhook Secret Credentials Provider Plugin (версии 16.v0cfa_f0215cf5 и старше) — получил 3.7 по CVSS и категорию LOW. На следующий день после бюллетеня я проверил три продуктовых Jenkins-инстанса, за которые отвечаю: все три использовали уязвимую версию, ни одна команда не запланировала обновление. «Это же LOW, не приоритет». Знакомая логика, правда?
Проблема в том, что этот LOW-баг — входная дверь. Через него атакующий подбирает webhook-токен, а дальше добирается до секретов сборки через соседние уязвимости из того же бюллетеня. Итоговый ущерб — критический, хотя ни одна CVE по отдельности не кричит «паника». Исправление доступно в версии 32.v09c9b_522f0a_8, но обновить плагин мало. Ниже разберу механику атаки, покажу полную цепочку эксплуатации и дам конкретные команды для проверки и защиты.
Webhook в Jenkins: что это и почему атакующий смотрит сюда первым
Webhook (вебхук) — HTTP-эндпоинт, который внешний сервис (GitHub, GitLab, Bitbucket) вызывает автоматически при событии: push в репозиторий, создание merge request, тег релиза. Jenkins принимает POST-запрос и запускает pipeline — сборку, тесты, деплой.
Аналогия для понимания: webhook — дверной звонок. GitHub нажимает «звонок» (POST-запрос), Jenkins слышит и открывает дверь (запускает pipeline). Проблема начинается, когда звонок может нажать кто угодно.
Зачем это атакующему
Мотивация прагматична. Jenkins pipeline при сборке обычно имеет доступ к:
- API-токенам облачных провайдеров (AWS, GCP), ключам Docker Registry, SSH-ключам деплоя
- Паролям баз данных, токенам Slack/Telegram, ключам подписи артефактов
- Контейнерным образам и скомпилированному коду — то есть к supply chain
Если атакующий может вызвать pipeline или подменить его поведение через незащищённый webhook Jenkins, он получает доступ ко всему перечисленному. По данным Verizon DBIR 2025, 38% утечек данных связаны с кражей учётных данных. Jenkins — одно из мест, где эти данные хранятся в концентрированном виде. А по данным IBM X-Force 2025, организации в среднем устраняют известные CVE за 29 месяцев. Уязвимые плагины живут в продакшене годами.
В терминах MITRE ATT&CK (открытая база тактик и техник атак; T-коды вроде T1190 — её идентификаторы) атака через webhook — цепочка: Exploit Public-Facing Application (T1190, Initial Access) → Poisoned Pipeline Execution (T1677, Execution) / Compromise Software Dependencies and Development Tools (T1195.001, Initial Access) → Credentials In Files (T1552.001, Credential Access).
Уязвимости CI/CD пайплайна: разбор CVE-2026-70437
Суть уязвимости: timing-атака на bearer token
CVE-2026-70437 (CWE-208 — Observable Timing Discrepancy) затрагивает Webhook Secret Credentials Provider Plugin. Плагин защищает webhook-эндпоинт bearer-токеном: при входящем запросе Jenkins сравнивает присланный токен с эталонным. Проблема — сравнение выполняется обычной строковой операцией, а не constant-time функцией.
Что это значит на практике. При стандартном String.equals() в Java функция возвращает false сразу, как находит первый несовпадающий символ. Если первый символ правильный — ответ приходит на микросекунды медленнее, чем если он неправильный. Атакующий отправляет десятки и сотни тысяч запросов, замеряет время ответа для каждого варианта первого символа и применяет статистические методы шумоподавления (множественные измерения, фильтрация сетевого джиттера, перекрёстная валидация), чтобы выделить микросекундную разницу на фоне сетевых задержек. При успехе переходит ко второму, третьему символу — и посимвольно восстанавливает весь токен.
Через типичный интернет-канал это крайне затруднительно: джиттер превышает полезный сигнал на порядки. Атака реалистичнее в условиях LAN или контролируемой сетевой среды. Но если Jenkins торчит в корпоративной сети без сегментации — условия вполне подходящие.
CVSS-вектор для начинающих
CVSS (Common Vulnerability Scoring System) — стандарт оценки серьёзности уязвимости по набору компонентов. Разберём каждый для этой CVE:
| Компонент | Значение | Что это значит |
|---|---|---|
| AV:N | Network | Атака возможна по сети — webhook доступен извне |
| AC:H | High | Нужны тысячи запросов и статистический анализ |
| PR:N | None | Привилегии не требуются — достаточно слать HTTP-запросы |
| UI:N | None | Участие пользователя не нужно |
| S:U | Unchanged | Scope не выходит за пределы компонента |
| C:L | Low | Раскрывается только значение токена |
| I:N / A:N | None | Целостность и доступность не затрагиваются |
Итого: CVSS 3.7, LOW. EPSS (Exploit Prediction Scoring System — оценка вероятности от 0 до 1, что уязвимость начнут эксплуатировать в ближайшие 30 дней) — 0.0027, percentile 0.1756, ниже медианы. По данным CISA-ADP (Vulnrichment), эксплуатация не зафиксирована (exploitation: none). Выглядит безобидно. Но severity оценивает баг изолированно — а атакующие так не работают.
Доступ к секретам сборки через webhook: полная цепочка атаки
Бизнес-логика: зачем это нужно злоумышленнику
Конечная цель — не сам webhook-токен. Атакующий монетизирует доступ к CI/CD через:
- Кражу cloud credentials → доступ к AWS/GCP/Azure инфраструктуре жертвы
- Внедрение в supply chain → подмена артефактов сборки (вредоносный код в контейнерных образах, пакетах)
- Ransomware → шифрование Jenkins-инстанса и связанных серверов
По данным CrowdStrike Global Threat Report 2025, 75% вторжений используют действительные учётные данные. Jenkins — один из самых удобных источников таких данных.
Цепочка эксплуатации: три CVE = полный компрометаж
Фаза 1 — Разведка. Атакующий обнаруживает Jenkins-инстанс. По данным Shadowserver, на начало 2024 года было открыто порядка 45 000 Jenkins-инстансов [точная цифра требует проверки по отчёту Shadowserver]. Webhook-эндпоинт обычно доступен по предсказуемому URL: /generic-webhook-trigger/invoke или /github-webhook/. В терминах ATT&CK — File and Directory Discovery (T1083, Discovery).
Фаза 2 — Timing-атака (CVE-2026-70437). POST-запросы на webhook-эндпоинт с разными значениями bearer-токена. Сотни тысяч запросов с усреднением и статистической фильтрацией сетевого шума — при благоприятных сетевых условиях токен может быть восстановлен.
Фаза 3 — Перечисление credentials (CVE-2026-70433). HCL AppScan Plugin версий 1.8.3 и старше (CVSS 4.3, CWE-862 — Missing Authorization) не проверяет разрешения в HTTP-эндпоинтах. Атакующий с правом Overall/Read перечисляет credential ID, хранящиеся в Jenkins. По данным бюллетеня, аналогичные проблемы с отсутствием проверки разрешений предположительно затрагивают и ряд других плагинов, для некоторых из которых на момент публикации исправлений не было.
Фаза 4 — RCE через Groovy (CVE-2026-70431). Multijob Plugin версий 669.v9d96a_d9c71b_0 и старше (CVSS 8.8 HIGH, CWE-94 — Code Injection) предоставляет Groovy-скрипты, не интегрированные с Script Security Plugin. Атакующий с правами Item/Create или Item/Configure выполняет произвольный код на контроллере Jenkins. Параллельно через CVE-2026-70432 (CSRF, CWE-352, тот же CVSS 8.8 HIGH) можно добиться того же результата. Любопытный момент: официальный CVSS-вектор этой CVE указывает UI:N (взаимодействие пользователя не требуется), что нетипично для классического CSRF — эксплуатация может происходить через автоматически отправляемую форму без явного клика жертвы.
Фаза 5 — Эксфильтрация. Groovy-код на контроллере имеет доступ ко всем stored credentials. Через CVE-2026-70434 (SCM-Manager Plugin, CVSS 4.2, CSRF, CWE-352) и CVE-2026-70435 (SCM-Manager Plugin, CVSS 4.2, missing permission check, CWE-862) атакующий может заставить Jenkins подключиться к контролируемому URL с захваченными credential ID — и перехватить реальные учётные данные.
Результат: три уязвимости с рейтингами 3.7 (LOW) + 4.3 (MEDIUM) + 8.8 (HIGH) в связке дают полный компрометаж инстанса, утечку секретов сборки CI/CD и потенциальный доступ к production-инфраструктуре. CVSS оценивает каждую уязвимость изолированно и не предназначен для оценки цепочек эксплойтов — совокупный ущерб здесь моя авторская оценка, а не формальный score.
Почему хранение секретов в Jenkins — архитектурная проблема
Согласно анализу Trend Micro, ряд Jenkins-плагинов исторически хранил учётные данные в открытом виде в XML-файлах ($JENKINS_HOME/jobs/<job>/config.xml). Даже при использовании штатного Credentials Plugin пароли шифруются ключом из $JENKINS_HOME/secrets/hudson.util.Secret, зашифрованным через AES с master key из $JENKINS_HOME/secrets/master.key. Каждая инсталляция генерирует уникальные ключи — но при RCE на контроллере оба файла доступны атакующему. Проще говоря: шифрование есть, но ключ лежит рядом с замком.
Атака на Jenkins webhook: что было до CVE-2026
CVE-2022-34777: stored XSS через GitLab Plugin webhook
GitLab Plugin версий 1.5.34 и старше не экранировал поля, вставляемые в описание сборки, запущенной через webhook (CVSS 5.4, CWE-79). Атакующий с правами на merge request в GitLab вставлял JavaScript в описание — код исполнялся в браузере пользователя, просматривающего pipeline. Результат — кража сессионных токенов администраторов. EPSS этой уязвимости — 0.7306, percentile 0.9945 (Top 1%): вероятность эксплуатации экстремально высокая.
По описанию Legit Security, сценарий выглядел так: merge request триггерил webhook, Jenkins запускал pipeline и отображал описание без экранирования. Вредоносный <script> исполнялся у любого, кто открывал страницу сборки. Классическая stored XSS, но через неожиданный вектор — webhook payload.
CVE-2024-23897: файловое чтение через CLI
CVE-2024-23897 (CVSS 9.8 CRITICAL, CWE-22 и CWE-27) позволяет неаутентифицированным атакующим читать произвольные файлы на контроллере через CLI Jenkins. Уязвимость внесена в каталог CISA KEV (Known Exploited Vulnerabilities — список уязвимостей, которые УЖЕ эксплуатируются в реальных атаках) и отмечена как ransomware-вектор. EPSS — 1.0000 (максимум). PoC опубликован на Exploit-DB (EDB-51993). Через неё атакующий читал credentials.xml и ключи шифрования напрямую. Webhook-уязвимости дают альтернативный вход в ту же систему — менее шумный и менее заметный.
Защита секретов в Jenkins: делай раз, делай два, делай три
Предпосылки: доступ к Jenkins с правами администратора (веб-интерфейс или CLI). Для шагов с Nginx — доступ к конфигурации reverse proxy.
Делай раз: проверь версии плагинов
Открой Jenkins → Manage Jenkins → Manage Plugins → Installed. Найди «Webhook Secret Credentials Provider». Если версия ниже 32.v09c9b_522f0a_8 — плагин уязвим.
Через Script Console (Manage Jenkins → Script Console):
Jenkins.instance.pluginManager.plugins.findAll {
it.shortName in ['webhook-secret-credentials-provider',
'jenkins-multijob-plugin', 'appscan']
}.each { println "${it.displayName}: ${it.version}" }
Ожидаемый вывод — строки вида Webhook Secret Credentials Provider: 16.v0cfa_f0215cf5. Если версии ниже целевых (Webhook — 32.v09c9b_522f0a_8, Multijob — 677.v7ffc23d6a_4c2, AppScan — 1.8.4) — обновляй немедленно. Если вывод пуст — плагин не установлен, этот вектор атаки не актуален.
Отдельно обнови Jenkins core: целевые версии — 2.576 (weekly) или LTS 2.568.2. Для Red Hat OpenShift выпущены RHSA-2026:60247/60248/60249 — обнови пакеты ocp-tools-4/jenkins-rhel8.
Делай два: ограничь webhook по IP и включи HMAC-подпись
Webhook-эндпоинт должен принимать запросы только от IP-адресов git-хостинга. Для GitHub диапазоны доступны через curl https://api.github.com/meta | jq '.hooks'.
Пример для Nginx перед Jenkins:
location ~ ^/(github-webhook|generic-webhook-trigger) {
allow 140.82.112.0/20;
allow 192.30.252.0/22;
deny all;
proxy_pass http://jenkins_backend;
}
После применения (nginx -t && nginx -s reload) проверь: curl -X POST https://jenkins.example.com/github-webhook/ с произвольного IP должен вернуть 403.
HMAC-подпись (Hash-based Message Authentication Code) — второй слой защиты. GitHub подписывает тело запроса секретным ключом, Jenkins проверяет подпись. Настройка: в GitHub (Settings → Webhooks → Secret) укажи случайную строку минимум 32 символа. В Jenkins через Generic Webhook Trigger Plugin активируй «Token Credential» и укажи тот же секрет через Jenkins Credentials (тип Secret Text). Теперь даже при известном URL без секрета атакующий не сформирует валидную подпись.
Делай три: аудит и ротация секретов
Предпосылка: установлен trufflehog (бинарник с GitHub или через go install).
trufflehog filesystem --directory /var/lib/jenkins \
--only-verified --json | jq '.SourceMetadata'
Команда сканирует директорию Jenkins на верифицированные секреты (API-ключи, токены, пароли) и выводит метаданные файлов. Если вывод непустой — каждый найденный секрет нужно ротировать: отозвать старый токен/ключ и перевыпустить новый. Отдельно проверь $JENKINS_HOME/secrets/ — доступ к этой директории должен быть ограничен пользователем, от которого работает процесс Jenkins.
Архитектурный шаг: вынеси секреты из Jenkins Credentials в HashiCorp Vault или AWS Secrets Manager. Jenkins Credentials — удобное, но хрупкое хранилище. При RCE на контроллере все секреты утекают разом. Vault с ротацией short-lived токенов снижает ущерб от компрометации: даже если атакующий перехватит токен, он протухнет через минуты, а не будет валиден месяцами.
DevSecOps уязвимости Jenkins: обнаружение и мониторинг
Сигналы для SIEM
| Сигнал | Что искать | Где |
|---|---|---|
| Аномальная частота POST на webhook | >50 запросов/минуту с одного IP | Nginx access log, WAF |
| Вариативность Authorization header | Одинаковый payload, меняется только токен | SIEM (Splunk, ELK) |
| Pipeline без коммита | Build triggered, но в git нет свежего push | Jenkins audit log |
| Нетипичный Credentials API вызов | CredentialsProvider.lookupCredentials() из неожиданного контекста |
Jenkins Script Security log |
| Исходящий трафик контроллера | Jenkins controller инициирует DNS/HTTP к неизвестным хостам | Zeek, Suricata |
Ключевой индикатор timing-атаки: десятки или сотни POST-запросов к /generic-webhook-trigger/invoke за минуту с одного source IP. Нормальный webhook от GitHub — 1–5 запросов при push. Если видите сотни — это либо misconfiguration, либо атака. В обоих случаях стоит разобраться.
Чек-лист безопасности CI/CD пайплайнов
| Мера | Закрывает | Приоритет |
|---|---|---|
| Обновить Webhook Secret Credentials Provider до 32.v09c9b_522f0a_8 | CVE-2026-70437 | Высокий |
| Обновить Multijob Plugin до 677.v7ffc23d6a_4c2 | CVE-2026-70431/70432 | Высокий |
| Обновить Jenkins core до 2.576 / LTS 2.568.2 | CVE-2026-70426 (CRITICAL) | Критический |
| Ограничить webhook IP (allowlist) | Все webhook-CVE | Высокий |
| Включить HMAC-подпись webhook | CVE-2026-70437, hardening | Высокий |
| Аудит и ротация stored credentials | CVE-2026-70433 | Средний |
| Вынести секреты в Vault / Secrets Manager | Архитектурный hardening | Средний |
| Не запускать сборки на master-ноде | CVE-2026-70431 (RCE на контроллере) | Высокий |
Большинство команд патчит Jenkins core и забывает про плагины. В этом бюллетене из двенадцати уязвимостей для нескольких на момент публикации исправлений не было (Parameterized Remote Trigger, Qualys Container Scanning Connector, Summary Display). Среда Jenkins-плагинов — тысячи модулей, которые поддерживают отдельные мейнтейнеры с разной скоростью реакции.
Webhook Secret Credentials Provider — показательный пример: LOW-уязвимость, которую пропустят при триаже, но в цепочке с Multijob и AppScan она даёт полный доступ к секретам.
Я в последние пару лет веду таблицу плагинов для каждого Jenkins-инстанса: «версия / дата последнего обновления / есть ли в advisory». Звучит скучно — но три раза это уже спасло от ситуации «уязвимый плагин в проде полгода». Если настраиваете Jenkins с нуля — минимум плагинов. Каждый лишний — поверхность атаки, которую никто не мониторит.
По OWASP DevSecOps Maturity Model, управление зависимостями (Build → Patch Management) — одна из первых практик для внедрения. Jenkins-плагины — такие же зависимости, как npm-пакеты или pip-библиотеки, только с доступом к production-секретам. Если хочется разобраться в этих вещах системно — на IB Basics подобные цепочки проходят от архитектуры CI/CD до конкретных команд защиты.
Эту тему и смежные навыки разбирают на практике в курсе «Основы практик DevOps» Codeby Academy.
