Распечатанный лог консоли Jenkins на кремовой бумаге лежит на светлом дубовом столе, строка с ключом AWS подчёркнута янтровыми чернилами. Рядом латунное пресс-папье и перьевая ручка, мягкий дневной...


Понедельник, 9:15 утра. CloudTrail показывает 47 вызовов sts:GetCallerIdentity от IAM-ключа, который по документам принадлежит сервисному аккаунту Jenkins. Вызовы идут из IP-диапазона, не принадлежащего нашей инфраструктуре. Через 20 минут - алерт GuardDuty: тот же ключ запускает EC2-инстансы в регионе ap-southeast-1, где у нас нет ни одного ресурса. Источник утечки нашли через час: AWS_SECRET_ACCESS_KEY выводился в консольный лог Jenkins-джобы через env | sort в отладочном шаге, который разработчик добавил «на пять минут» три недели назад. По данным GitGuardian State of Secrets Sprawl Report 2024, за год на публичном GitHub появилось 12,8 миллионов новых секретов - и значительная часть скомпрометированных сред приходится не на рабочие станции, а на CI/CD-раннеры.

Как атакующие крадут секреты из CI/CD-пайплайна​

CI/CD-пайплайн - система с максимальными привилегиями: она деплоит в продакшн, пушит образы в реестры, управляет инфраструктурой. Скомпрометированный CI/CD-секрет - не абстрактная «утечка данных», а прямой доступ к production-среде. По данным IBM Cost of a Data Breach Report 2023, средняя стоимость инцидента - $4,45 млн. Для российских компаний с 2024 года добавляются оборотные штрафы за утечку персональных данных - до 3% годовой выручки. Мотивация считать секреты в CI/CD - вполне денежная. Подробнее - в нашем обзоре атаки на цепочку поставок.

Полная цепочка атаки по матрице MITRE ATT&CK выглядит так: Reconnaissance (T1593.003, Code Repositories - сканирование публичных репозиториев) → Credential Access (T1552.001, Credentials In Files - извлечение из конфигурации; T1552.004, Private Keys) → Initial Access (T1078, Valid Accounts - утёкшие credentials используются как легитимные) → Impact (криптомайнинг, эксфильтрация данных, ransomware). На каждом этапе можно ловить - и на каждом обычно не ловят.

GitHub и GitLab: hardcoded secrets и poisoned pipelines​

Боты прочёсывают публичный GitHub непрерывно. От появления секрета в коде до первой попытки его использования проходят минуты. Не часы - минуты. Если атакующий получил доступ к приватному репозиторию и вытаскивает секреты - это T1213.003 (Code Repositories, Collection).

Типовые точки утечки API-ключей через GitHub:
  • Hardcoded secrets в YAML-конфигурации - ключ в env-блоке .github/workflows/deploy.yml. По данным GitGuardian, приватные репозитории содержат hardcoded-секреты значительно чаще, чем публичные. Логика «раз приватный - можно не прятать» убивает
  • pull_request_target в GitHub Actions - event имеет доступ к секретам репозитория, и если workflow чекаутит код автора PR - прямой путь к эксфильтрации
  • Docker-слои - COPY .env /app/.env с последующим RUN rm /app/.env бесполезно: секрет остаётся в промежуточном слое образа. Удалил файл - а слой-то уже собран
  • Org-level секреты с чрезмерным scope - один org-secret доступен всем репозиториям. Компрометация одного репо = утечка всех org-секретов
Отдельная категория - Poisoned Pipeline Execution (OWASP CICD-SEC-4, соотносится с T1195.002, Compromise Software Supply Chain). Атакующий модифицирует YAML-конфигурацию пайплайна через pull request или компрометацию зависимости и заставляет раннер слить секреты. Ребята из Synacktiv демонстрировали этот подход инструментом Nord Stream: автоматическое создание ветки, внедрение pipeline YAML, запуск сборки, сбор секретов из output и зачистка следов. Красиво и страшно.

Jenkins: утечка через build logs и credentials store​

Русскоязычные материалы по secrets sprawl почти не покрывают Jenkins, хотя именно он остаётся одной из самых опасных поверхностей для утечки токенов. На практике - чаще всего именно через Jenkins и утекает.

Build logs - главная проблема. Jenkins по умолчанию маскирует только credentials, явно переданные через withCredentials {}. Всё остальное - printenv, set -x в shell-скриптах, verbose-режим Terraform (TF_LOG=DEBUG) - выводит секреты в консольный лог открытым текстом. Лог доступен всем с правами Job/Read, а на практике часто и анонимно, если Jenkins стоит за VPN без дополнительной аутентификации. Я видел Jenkins-инсталляции, где build-логи были доступны вообще без логина - через прямую ссылку.

Credentials store хранит секреты на мастер-ноде в credentials.xml. При дефолтной конфигурации ключ шифрования (master.key и hudson.util.Secret) лежит в $JENKINS_HOME/secrets/. Получив доступ к файловой системе мастера - через SSRF, RCE в плагине или скомпрометированный агент - атакующий расшифровывает все credentials. Это T1555.006 (Cloud Secrets Management Stores, Credential Access), применённый к on-premise системе.

Master-agent коммуникация - ещё один вектор. Jenkins-агенты подключаются к мастеру по JNLP или SSH. Скомпрометированный агент может запрашивать credentials с мастера через внутренний API. Ограничить это можно через Slave → Master Access Control, который по умолчанию не настроен. По умолчанию - это ключевое слово для всего Jenkins hardening.

[Применимо: внутренний пентест, legacy Jenkins 2.x без hardening]

Сканирование секретов в репозиториях: Gitleaks vs TruffleHog​

Два основных open-source инструмента для обнаружения hardcoded secrets в коде - Gitleaks и TruffleHog. Оба бесплатны и интегрируются в CI, но архитектурно работают по-разному.

Требования к окружению:
  • ОС: Linux, macOS, Windows
  • RAM: 2 ГБ минимум, 4 ГБ для репозиториев с историей свыше 10 000 коммитов
  • Git 2.x+, Docker опционально (для TruffleHog через trufflesecurity/trufflehog)
  • Сеть: TruffleHog с верификацией требует интернет; Gitleaks работает полностью offline
КритерийGitleaksTruffleHog
Метод детекцииRegex-паттерны + entropy (опция)Regex + entropy + верификация через API провайдера
False positivesСредний уровеньНизкий (верификация отсекает невалидные)
Скорость на 50k коммитовСекундыМинуты (API-вызовы)
Offline-работаПолнаяЧастичная (без верификации)
Кастомные правила.gitleaks.tomlRegex через конфиг
Когда использоватьPre-commit хуки, быстрый CI-checkCI pipeline с верификацией, полный аудит
Когда НЕ использоватьАудит с подтверждением валидности ключейPre-commit (слишком медленный)

В боевых условиях работает связка: Gitleaks в pre-commit (быстрый, offline, мгновенная обратная связь), TruffleHog в CI-пайплайне (медленнее, зато верифицирует - шума на порядок меньше).

Три слоя secret scanning в DevSecOps-пайплайне​

Слой 1: Pre-commit - блокировка до попадания в git-историю. Хук с Gitleaks останавливает коммит при обнаружении паттерна секрета:
YAML:
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.21.2
    hooks:
      - id: gitleaks
После pre-commit install каждый git commit проходит через Gitleaks. Ограничение: хук работает только на машине разработчика и обходится через --no-verify. Это первая линия, а не замена CI-сканированию. Разработчик, который торопится в пятницу вечером, --no-verify напечатает быстрее, чем вы думаете.

Слой 2: CI-пайплайн - сканирование в Pull Request. TruffleHog или Gitleaks запускается как шаг CI при каждом PR. В GitHub Actions - trufflehog git file://. --since-commit=$BASE_SHA --fail. В GitLab CI - gitleaks detect --log-opts="$CI_MERGE_REQUEST_DIFF_BASE_SHA..$CI_COMMIT_SHA". Сканируются только новые коммиты. Нюанс: actions/checkout@v4 нужно вызывать с fetch-depth: 0, иначе при shallow clone --since-commit не сработает - и вы будете думать, что всё чисто, а на деле сканер просто не видит историю.

Слой 3: Continuous scanning - полная git-история по расписанию. Команда trufflehog git file://./repo --only-verified в scheduled pipeline ловит секреты, закоммиченные до внедрения сканирования. По данным GitGuardian, значительная доля исторических секретов остаётся валидной годами - и именно они самые опасные, потому что про них все забыли.

Remediation: удаление секретов из git history и ротация ключей доступа

Обнаружить секрет - половина работы. Удалить его из git history и провести ротацию под давлением инцидента - вторая, и более болезненная.

Удаление файла через git rm и новый коммит не удаляет секрет из истории - он остаётся доступен через git log --all --full-history. Два инструмента решают задачу: BFG Repo-Cleaner (требуется bare-clone: git clone --mirror <url> repo.git, затем bfg --replace-text passwords.txt repo.git) и git filter-repo (git filter-repo --replace-text expressions.txt с поддержкой regex). BFG быстрее и проще для типовых кейсов. git filter-repo даёт больше контроля - когда секрет встречается в разных форматах. После очистки обязателен git reflog expire --expire=now --all && git gc --prune=now --aggressive.

Ограничения обоих инструментов - и их нужно понимать до того, как начнёте:
  • Переписывают историю - все хеши коммитов меняются, открытые PR ломаются, нужен force push
  • Форки сохраняют старую историю с секретом - необходимо уведомить владельцев форков
  • GitHub кеширует коммиты по SHA: даже после force push старый коммит доступен по прямой ссылке до обращения в GitHub Support
Порядок ротации при инциденте:
  1. Немедленная ревокация - отозвать ключ через консоль провайдера (AWS IAM, GCP IAM, GitLab PAT). Не через час - сейчас
  2. Генерация нового ключа с минимальными привилегиями, scope строго под одну задачу
  3. Обновление всех consumer'ов - CI/CD variables, Vault, Kubernetes Secrets
  4. Аудит использования - CloudTrail, GCP Audit Logs, audit log GitLab/GitHub: что делали с ключом между утечкой и ревокацией
  5. Очистка git history через BFG или git filter-repo
  6. Postmortem - как секрет попал в историю, почему не сработал pre-commit
Порядок именно такой. Сначала ревокация, потом всё остальное. Я видел команды, которые начинали с очистки git history, пока ключ продолжал работать - атакующий за это время успевал развернуть инфраструктуру для майнинга.

Мониторинг утечек секретов GitHub Actions и Jenkins в SIEM​

Сканирование репозиториев - превентивная мера. SOC нужны правила для обнаружения последствий утечки: использования скомпрометированных credentials.

Sigma-правила и корреляция для SOC​

В репозитории SigmaHQ есть правила, покрывающие TTPs утечки ключей доступа в pipeline:
  • proc_creation_lnx_susp_git_clone.yml и proc_creation_win_git_susp_clone.yml (T1593.003) - подозрительное клонирование репозиториев на хостах, где Git не штатный инструмент
  • github_outside_collaborator_detected.yml (T1213.003) - добавление внешнего коллаборатора в приватный репозиторий
  • github_self_hosted_runner_changes_detected.yml (T1213.003) - изменения конфигурации self-hosted runner'ов, потенциальная подготовка к эксфильтрации
  • lnx_auditd_find_cred_in_files.yml (T1552.001) - поиск credentials в файлах на Linux-хостах (grep по паттернам password, secret, key)
Для Jenkins-инфраструктуры нужна кастомная корреляция - готовых Sigma-правил тут мало:
  • Аномальный доступ к credentials - запрос через Jenkins API (/credentials/store/) с IP вне whitelist build-агентов
  • Массовое чтение build logs - один пользователь читает логи более 20 джоб за час (паттерн автоматического сбора секретов из логов)
  • Модификация pipeline + немедленный build - изменение Jenkinsfile или .gitlab-ci.yml с запуском сборки в течение минуты (паттерн Poisoned Pipeline Execution)
Для GitHub/GitLab через audit log API: событие repo.download_zip или git.clone для приватного репозитория от IP вне корпоративного диапазона; создание Personal Access Token с scope repo и немедленный API-вызов к /repos/{org}/{repo}/contents/; изменение visibility репозитория с private на public.

Ключевая связка - CI/CD audit log + CloudTrail/GCP Audit Logs: если IAM-ключ, используемый в CI/CD, вызывает API из нового региона или IP - baseline-аномалия. Правило: ключ сервисного аккаунта Jenkins → sts:AssumeRole или iam:CreateAccessKey из нетипичного источника → алерт Critical. Если у вас этого правила нет - заведите сегодня.

Чеклист: предотвращение утечки credentials в CI/CD​

📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме

Большинство команд, с которыми я работал, внедряют аудит секретов в исходном коде «слой за слоем»: сначала CI-gate (самый высокий ROI - ловит всё до мерджа), потом pre-commit (снижает нагрузку на CI), потом scheduled scan (закрывает исторический долг). Jenkins hardening часто откладывают - и зря: именно Jenkins-логи оказываются источником утечки чаще, чем git-коммиты, в организациях с приватными репозиториями.

Привычка полагаться на один инструмент - типичная ошибка. Gitleaks в pre-commit не заменяет TruffleHog в CI, а оба не заменяют мониторинг использования скомпрометированных credentials в SIEM. На практике я видел организации с идеально настроенным secret scanning, которые месяцами не замечали, что утёкший три года назад ключ всё ещё валиден и используется из Юго-Восточной Азии - потому что никто не коррелировал CloudTrail с инвентарём CI/CD-credentials.

Разрыв между «найти секрет в коде» и «обнаружить его использование атакующим» - вот где ломается защита. SOC думает, что secret scanning - проблема DevOps. DevOps считает, что мониторинг - задача SOC. Ни один из них не видит полной картины: от коммита до эксплуатации. Пока эти две функции не научатся работать с одним потоком событий, утечки секретов в CI/CD останутся одним из самых дешёвых для атакующего и самых дорогих для защитника векторов. На codeby.net есть тред, где коллеги разбирают интеграцию Jenkins и GitLab audit log в SIEM-корреляцию - конкретные парсеры и правила под эту связку.
 
Мы в соцсетях:

Взломай свой первый сервер и прокачай скилл — Начни игру на HackerLab

Похожие темы

🚀 Первый раз на Codeby?
Гайд для новичков: что делать в первые 15 минут, ключевые разделы, правила
Начать здесь →
🧭 Навигатор · ИБ 2026
Не знаешь, какой трек твой?
5 направлений ИБ, реальные зарплаты и точка входа для каждого — в одном треде.
JuniorSenior+
100K → 600K+ ₽ /мес
Открыть навигатор →
🔴 Свежие CVE, 0-day и инциденты
То, о чём ChatGPT ещё не знает — обсуждаем в реальном времени
Threat Intel →
💼 Вакансии и заказы в ИБ
Pentest, SOC, DevSecOps, bug bounty — работа и проекты от проверенных компаний
Карьера в ИБ →

HackerLab