Перейти к содержимому

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

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

В августе 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.