Как выглядит тихий апокалипсис в CI/CD
По моему опыту, самые опасные атаки — не те, что гремят на весь интернет, а те, которые маскируются под рутину. Вот и здесь: злоумышленник не ломал стены, не перебирал пароли. Он просто... перенаправил тег.
Что произошло на самом деле. Есть такой репозиторий — xygeni/xygeni-action. Официальное действие для GitHub, которым пользуются больше 137 проектов. Доверенное, проверенное, все его дёргают в своих пайплайнах. И кто-то (до сих пор неясно, кто именно) получил доступ к учётке сопровождающего. Как? Может, пароль слабый, может, фишинг — детали скрывают, но суть ясна: доступ слили.
И знаете, что меня как эксперта всегда бесит? Что хакеры действуют тоньше, чем мы думаем. Вместо того чтобы переписать код и сразу спалиться, они пошли в обход. В репозиторий кинули три пулл-реквеста с вредоносным кодом — стандартная практика, мол, хотим улучшить продукт. Их, естественно, отклонили. Но это была лишь дымовая завеса.
Главный трюк. Злоумышленник просто переместил существующий тег v5 на свой коммит. Тег — это же не каменная стена, его можно перезаписать. И любой проект, где в workflows написано xygeni/xygeni-action@v5, молча начал тянуть бэкдор.
Честно говоря, когда я впервые увидел такие схемы пару лет назад, у меня глаз дёргался. Сейчас — уже привык, но легче не стало.
Анатомия: как прятали бэкдор на виду
Давайте я на пальцах объясню, почему это сработало так гладко. Представьте, что вы — системный администратор, смотрите логи сборки. Всё идёт зелёненьким, тесты проходят, артефакты собираются. Красота.
А в это время между строк action.yml, между легитимной установкой сканера и финальной проверкой, вставили маленький скрипт. Называется он красиво — «сбор телеметрии версии сканера». Ну кто ж против телеметрии? Мы же сами её любим собирать.
Что делал скрипт на самом деле:
После запуска он тихонько стучался на сервер по адресу 91.214.78.178 (уже в чёрных списках, но поздно). Передавал:
- имя узла
- имя пользователя
- версию ОС
А дальше — классика удалённого администрирования, только со знаком минус. Скрипт каждые несколько секунд проверял, нет ли новых команд с сервера. Если появлялись — выполнял их через eval, результат сжимал и отправлял обратно.
И самое хитрое — основной процесс проверки безопасности продолжал работать. То есть для внешнего наблюдателя action выполнял свои функции, ничего не падало, ничего не орало в логах. Просто фоном крутилась маленькая чёрная метка.
Что любопытно: по моим прикидкам, за три минуты такого «мирного сосуществования» можно было вытащить из раннера всё, что угодно. Токены GitHub? Пожалуйста. Ключи доступа к продакшену? Легко. Исходники закрытых репозиториев? Да без проблем.
А вы проверяли, что реально лежит в ваших зависимостях?
Вот это, кстати, момент, который я всегда поднимаю на встречах с CISO. Мы привыкли доверять тегам версий. Ну как же — v5, стабильная версия, все её юзают. А то, что тег можно переставить на любой коммит, почему-то вылетает из головы.
В реальности всё чуть иначе. Тег — это просто указатель. Как стрелка на двери туалета: сегодня она показывает «свободно», а завтра «занято», хотя дверь та же. И если вы полагаетесь на стрелку, а не на физический ключ (хеш коммита), вы в зоне риска.
Я сам два раза попадал в похожую ситуацию. Один раз — с npm-пакетом, где автор пересобрал мажорную версию, и у нас отвалилась сборка. Хорошо, что просто отвалилась, а не подгрузила майнер. Но нервы потрепала знатно.
Почему это не единичный случай, а система
Если вы думаете, что Xygeni — какая-то разовая история, то зря. Это уже вторая крупная атака на цепочку поставок через GitHub Actions за последний год. Помните tj-actions/changed-files? В 2025-м была похожая схема, только там злоумышленники вообще секреты тысяч репозиториев выкачали.
И знаете, что объединяет эти случаи? Не столько техническая сложность, сколько человеческий фактор. Мы, разработчики и админы, любим удобство. Нам проще написать @v5, чем выискивать длинный хеш коммита и вставлять его. А хакеры это обожают.
Как выглядит цепочка атаки:
- Компрометация аккаунта мейнтейнера (пароль, сессия, фишинг)
- Создание «безобидных» PR, которые отклоняют (для отвода глаз)
- Перемещение тега на вредоносный коммит
- Маскировка кода под легитимную активность
- Сбор данных с раннеров жертв
Кстати, о раннерах. GitHub Actions выполняет код на раннерах, и если это self-hosted runner внутри вашего периметра — злоумышленник получает доступ уже к вашей внутренней сети. Представляете масштаб?
Личный опыт: как мы отлавливали такое у клиента
Расскажу случай из практики. Работаем с одной госкорпорацией (назовём её «Рога и Копыта КИИ»). Внедряли им EDR, мониторинг, всё по ФСТЭК. И тут на одном из тендеров вижу: в их CI/CD пайплайне используется действие, которое дёргает тег @latest. Я спрашиваю: «А вы уверены, что latest — это то, что вы думаете?» На меня смотрят как на параноика.
Пришлось проводить мини-расследование. Оказалось, что автор действия за месяц до этого перевыпустил latest на новую версию, которая, к счастью, просто ломала сборку, а не воровала данные. Но если бы там был бэкдор? Мы бы сейчас не пили кофе, а разбирали компрометацию периметра.
Что я вынес тогда: доверие к тегам должно быть нулевым. Только конкретные коммиты. Да, это неудобно. Да, обновлять сложнее. Но безопасность — это всегда выбор между комфортом и спокойным сном.
10 жёстких правил защиты цепочки поставок в 2026
Если после предыдущих абзацев вы ещё не побежали проверять свои репозитории, давайте конкретно. Вот что мы реально делаем в Secure Defence, когда приходим к заказчику и видим GitHub Actions.
1. Забудьте про теги версий. Вообще.
Серьёзно. Тег v5 сегодня — одно, завтра — другое. Используйте полный хеш коммита. Пример: вместо actions/checkout@v3 пишите actions/checkout@a81bbbf. Да, нужно лезть в репозиторий, смотреть коммиты. Но это единственный способ гарантировать, что вы тянете именно тот код, который проверили.
Из жизни: мы внедрили это правило у себя, и первые две недели все матерились. Потом привыкли. Теперь это стандарт.
2. Регулярно чистите подозрительные PR
Злоумышленники любят закидывать удочку через пулл-реквесты. Даже если их отклоняют, они могут содержать код, который потом активируют через смену тега. Проверяйте закрытые PR — нет ли там чего-то, что пытались протащить под видом фичи.
3. 2FA — не панацея, но обязательно
Да, двухфакторку обходят. Да, есть сессионные куки, фишинг в реальном времени. Но если у сопровождающего даже 2FA нет — это просто подарок хакеру. Включайте везде, где можно.
4. Мониторинг изменений тегов
Это, кстати, редко кто делает. А зря. GitHub позволяет отслеживать, когда и кто менял теги. Настройте алерты на такие события. Если вдруг тег v5 обновился вчера, а нового релиза не было — повод бить тревогу.
5. Self-hosted раннеры — отдельная боль
Если вы используете свои раннеры, они должны жить в изоляции. Никакого доступа к внутренним сервисам по умолчанию. Потому что если злоумышленник получит шелл на раннере — считайте, сеть скомпрометирована.
6. Аудит секретов
Проверяйте, какие секреты вообще доступны в workflows. Часто вижу, что в пайплайн льют кучу лишнего: ключи от прод-базы, токены админов. Минимизируйте — давайте только то, что нужно для конкретной задачи.
7. Политика обновлений
Сделайте правило: все сторонние actions обновляются только после проверки. Нет, «автоматический апдейт через dependabot» — не наш метод. Мы должны видеть, что именно поменялось в коде между версиями.
8. Логирование и SIEM
Каждый запуск action, особенно с self-hosted раннеров, должен уходить в SIEM. Мало ли, вдруг оттуда кто-то начнёт стучаться на подозрительные IP. Кстати, адрес 91.214.78.178 уже в наших списках, но появятся новые.
9. Учения для команд
Раз в квартал проводим «день хаоса». Симулируем атаку на цепочку поставок, смотрим, кто как отреагировал. Честно, первые разы было стыдно — люди не знали, что делать. Сейчас уже норм.
10. Страховка через mirror-репозитории
Если совсем критично — поднимайте свои mirror-репозитории проверенных actions. Тяните код с GitHub к себе, проверяйте, и только потом используйте. Да, геморрой. Да, не для всех. Но для банков и ОКИИ — обязаловка.
А что делать, если уже поздно?
Представьте, что вы читаете это и понимаете: мы тоже использовали @v5 от Xygeni. Или похожее. Паниковать рано, но действовать надо быстро.
Алгоритм первой помощи:
- Блокировка. Если есть подозрение, что раннеры могли скомпрометировать — отключайте их от сети. Пусть лучше сборка встанет, чем данные утекут.
- Анализ логов. Проверьте, какие IP-адреса были на раннерах за последние недели. Не стучался ли кто на 91.214.78.178 или другие подозрительные диапазоны.
- Ротация секретов. Все ключи и токены, которые использовались в workflows — менять. Да, больно. Да, много работы. Но если токен утек, менять придётся в любом случае, только позже и в аврале.
- Форензик. Сохраните образы раннеров, если есть возможность. Возможно, потом придётся разбираться, что именно украли.
- Коммуникация. Если вы работаете с клиентами или партнёрами — предупредите их. Честность сейчас дороже репутационных потерь от сокрытия.
Взгляд в будущее: куда катятся цепочки поставок
Если говорить честно, ситуация будет только ухудшаться. Чем больше мы автоматизируем, тем больше точек входа для атак. И GitHub Actions — лишь верхушка айсберга. Тот же npm, PyPI, Docker Hub — везде похожие проблемы.
Что нас ждёт в ближайшие пару лет:
- Ужесточение требований регуляторов. ФСТЭК уже присматривается к CI/CD как к объектам критической инфраструктуры. Ждите проверок.
- Рост числа атак на мейнтейнеров. Хакеры поймут, что проще взломать одного разработчика популярной библиотеки, чем ломиться в защищённый периметр банка.
- Появление новых стандартов подписи кода. Возможно, дойдём до того, что каждый коммит будет требовать аппаратной подписи.
Вместо заключения: мой вам совет
Знаете, я за 10 лет в SOC повидал много атак. От банального фишинга до сложных APT. И могу сказать одно: самые дорогие инциденты случаются из-за мелочей, на которые забивали. Перемещённый тег, забытый токен в коде, доступ к админке без 2FA.
Не будьте теми, кто потом говорит «мы не думали, что это так серьёзно». Проверьте свои GitHub Actions уже сегодня. Замените теги на коммиты. Посмотрите, кто имеет доступ к мейнтейнерским аккаунтам.
И да, если нужна помощь — мы этим живём. Помогаем и банкам, и госкорпорациям, и просто ребятам, которые не хотят проснуться знаменитыми из-за утечки.
✦─────────
FAQ: 10 вопросов, которые мне задают после таких статей
1. А если я уже использую теги, всё пропало?
Не всё, но риск есть. Срочно переходите на коммиты и проверьте логи на подозрительную активность.
2. Как часто обновлять привязку к коммитам?
Каждый раз, когда вы хотите обновиться. Да, ручками. Но можно автоматизировать проверку изменений между коммитами.
3. GitHub сам предупреждает о таких уязвимостях?
Иногда — да, в security advisories. Но лучше не надеяться, а контролировать самому.
4. Что делать, если мы используем сотни actions?
Приоритизируйте: сначала критичные, где есть доступ к секретам, потом остальные. И постепенно мигрируйте.
5. Self-hosted раннеры безопаснее?
Нет, просто риски другие. Они внутри вашей сети, поэтому компрометация опаснее. Но контроль над ними выше.
6. Как защититься от фишинга мейнтейнеров?
Аппаратные ключи (YubiKey), обучение, мониторинг подозрительных входов. Банально, но работает.
7. А если злоумышленник уже был в системе?
Форензик, ротация всего и вся, анализ точек входа. И молитва, если честно.
8. Нужно ли сообщать клиентам об инциденте?
По 152-ФЗ — да, если утекли персональные данные. По этике — всегда да, иначе потеряете доверие.
9. Есть ли автоматические средства защиты?
Да, например, инструменты для анализа цепочки поставок, SCA-сканеры. Но они не панацея.
10. Сколько стоит аудит безопасности CI/CD?
Зависит от масштаба. Можете написать — посчитаем индивидуально. Первичная консультация бесплатно, кстати.
✦─────────
Нужна помощь?
Если вы CISO банка, руководитель по безопасности ОКИИ или просто ответственный админ, который не хочет кормить хакеров — давайте разбираться вместе.
Оставьте заявку на бесплатную консультацию на сайте: https://securedefence.ru/
Пришлём:
- чек-лист по безопасности GitHub Actions
- дорожную карту внедрения защитных мер
- коммерческое предложение под ваш бюджет
══════
Больше материалов: Центр знаний SecureDefence.