Инциденты и уязвимости
Утечка Codecov (2021) — когда «доверенный инструмент» в CI был взломан и утекли секреты
В 2021 году Bash Uploader от Codecov (скрипт curl|bash, запускаемый в CI) был изменён на стороне поставщика и ~2 месяца сливал секреты CI клиентов; поймала это проверка контрольной суммы. Цепочка как карта обороны, плюс исправления: проверять загружаемые артефакты, минимальные привилегии секретов CI, ротация.
Мы читаем реальные публичные инциденты не как повторный прогон новостей, а через призму «как бы вы от этого защитились». Эта статья основана на публичных материалах (официальный post-mortem, CISA, анализ от поставщиков безопасности), ссылки приведены в конце.
- Цель
- Codecov (инструмент покрытия кода) и CI его клиентов
- Раскрытие
- 15 апреля 2021 (изменение с 31 января / обнаружено 1 апреля)
- Класс
- атака на цепочку поставок (изменение доверенного артефакта на стороне поставщика) + кража секретов CI
- Масштаб
- многие клиенты Codecov (до ~29 000). Downstream-удары по HashiCorp, Twilio, Rapid7
- Первопричина
- CI запускал загруженный артефакт без проверки + работа поставщика с ключами + чрезмерно открытые секреты CI
- Настоящее исправление
- проверять загружаемые артефакты, минимальные привилегии секретов CI, ротация, мониторинг исходящего трафика
Что произошло (простыми словами)
Многие команды загружают и запускают внешние инструменты как есть в своём CI, например через curl … | bash. Bash Uploader от Codecov был таким скриптом.
Злоумышленник использовал слабость в системе распространения Codecov, чтобы переписать сам распространяемый скрипт, так что при запуске в CI он отправлял переменные той среды (секреты) на внешний сервер. С точки зрения клиента, он просто вызвал доверенный инструмент как всегда. Поскольку ни строчки вашего кода не изменилось, почти нет сигнала о чём-то неладном.
Примерно через два месяца клиент сравнил контрольную сумму скрипта (SHA256) с официальным значением и нашёл несовпадение — тогда изменение и вскрылось. Среди утёкшего могли быть облачные ключи, deploy-ключи, API-ключи и токены, и отсюда атака выстроилась в цепочку до downstream-утечек в таких компаниях, как HashiCorp и Twilio.
Это пугает именно потому, что «ваш код нетронут»
Атаки на цепочку поставок нацелены не на написанный вами код, а на то, чему вы доверяете и что подтягиваете. Поэтому ревью кода и логи редко их ловят. «Мы этому доверяем» = «мы это не проверяем» становится дырой.
Цепочка — это ещё и карта обороны
Важно, что это была цепочка из четырёх шагов, и на каждом шаге было место для остановки. Читайте это как «где её можно было пресечь», а не как рецепт атаки.
① Запуск доверенного инструмента через curl | bash (содержимое не проверено)
⊘ остановка: закрепление версии + проверка контрольной суммы загружаемого артефакта / вендоринг
② Артефакт подменён «на стороне поставщика»
Ваш код не трогали, поэтому заметить нельзя.
⊘ остановка: сравнение с заведомо корректным хешем; использование закреплённой версии
③ Переменные окружения CI (секреты, ключи, токены) отправлены наружу
⊘ остановка: минимальные привилегии, посекретные секреты CI (не передавать всё окружение)
④ Украденные ключи выстраиваются в цепочку к продакшену и другим сервисам
⊘ остановка: регулярная ротация ключей + мониторинг исходящего (egress) трафика
Хронология (по раскрытым данным)
2021-01-31
Начинается изменение Bash Uploader (далее с перерывами).2021-03–
Секреты неделями утекают из CI клиентов незамеченными.2021-04-01
Клиент находит несовпадение контрольной суммы и сообщает об этом.2021-04-15
Codecov раскрывает информацию публично; советует клиентам ротировать все секреты.2021-04–
Проявляется downstream-влияние в HashiCorp, Twilio, Rapid7. Codecov позже выводит Bash Uploader из эксплуатации.
Первопричина — не одна ошибка
Списать всё на «вина Codecov» — и это повторится. На стороне клиента тоже были проседающие слои.
Как было (на тот момент)
- CI запускал загруженный артефакт без проверки (доверяя
curl | bash) - Секреты CI чрезмерно открыты (каждый шаг видит каждую переменную окружения)
- Секреты редко ротировались (украденные ключи долго остаются действительными)
- Исходящий трафик CI без мониторинга
Как должно быть (профилактика)
- Закрепление версии + проверка контрольной суммы загружаемых артефактов или вендоринг
- Передавать секреты CI с минимальными привилегиями, только тем шагам, которым они нужны
- Ротировать секреты регулярно (короткий срок жизни даже при утечке)
- Мониторить исходящий трафик CI и обнаруживать отправки на неизвестные адреса назначения
«Доверие» идёт в паре с проверкой
Атаки на цепочку поставок нацелены на то, чему вы доверяете и что подтягиваете — библиотеки-зависимости, инструменты CI, сборочные образы. Случай XZ Utils имел ту же форму. Минимизируйте доверие и проверяйте целостность того, что подтягиваете, машинно.
Как предотвратить это в вашей среде
Исправления в порядке приоритета, работающие в любом масштабе. Однажды проведите инвентаризацию «чему доверяет и что запускает ваш CI» — и это станет личным.
Проверяйте загружаемые артефакты (не доверяйте curl|bash вслепую)
Если CI подтягивает внешний скрипт или бинарник, требуйте закреплённую версию (тег/коммит) + проверку контрольной суммы (SHA256). Где возможно, вендорьте его в собственный репозиторий. Эту утечку поймала именно такая проверка.
Ограничьте секреты CI до минимальных привилегий
Не передавайте каждую переменную окружения каждому шагу. Передавайте только нужный секрет нужной задаче/шагу. Используйте короткоживущие токены (например, OIDC), чтобы сократить долгоживущие ключи в покое.
Регулярно ротируйте секреты
В случае кражи короткий срок жизни ограничивает ущерб. Сделайте ротацию рутиной, и ротируйте немедленно при сообщении об инциденте в цепочке поставок.
Мониторьте исходящий трафик CI (egress)
Имейте возможность обнаруживать отправки на неизвестные адреса назначения из CI. Даже если вход не остановлен, держите слой, замечающий эксфильтрацию.
Позиция этого сайта: минимизировать доверие, проверять
Этот сайт проектирует приём зависимостей и сборок так, чтобы он был машинно проверяемым. То, что показывают Codecov и XZ Utils — одно и то же: «мы этому доверяем» склонно становиться «мы это не проверяем», и это дверь для атак на цепочку поставок. Сокращайте доверие и механически подтверждайте целостность того, что подтягиваете.
Источники (публичные материалы)
Факты здесь основаны на следующей публичной информации, с фокусом на уроках защиты — а не на шагах воспроизведения или изменённом коде.
- Codecov, "Post-Mortem / Root Cause Analysis (April 2021)" — about.codecov.io
- Codecov, "Bash Uploader Security Update" — about.codecov.io
- CISA, "Codecov Releases New Detections for Supply Chain Compromise" (2021) — cisa.gov
- Rapid7, "Analysis of the Codecov Supply Chain Compromise" (2021) — rapid7.com
Читать дальше
- История: Бэкдор XZ Utils — атака на само доверие
- Глоссарий: Что такое .env (где собираются секреты)
- Защита: Машинно мониторьте свои зависимости
- Инцидент: Equifax — запущенный известный CVE · Capital One — SSRF
FAQ
QВ чём была первопричина инцидента с Codecov?
Не баг в вашем коде, а изменение доверенного внешнего инструмента, который вы запускаете в CI (Bash Uploader от Codecov), в его источнике (upstream). CI загружал скрипт через curl и запускал его без проверки содержимого, поэтому запускалась изменённая версия, и переменные окружения CI (секреты) уходили наружу.
QПочему это оставалось незамеченным ~2 месяца?
Потому что изменённым был чужой инструмент — ваш собственный репозиторий и код не трогали, и в логах нет громкой аномалии. В итоге это поймали, когда клиент сравнил контрольную сумму скрипта (SHA256) с официальным значением и нашёл несовпадение.
QИмеет ли это значение для небольших проектов?
Да. Подтягивание инструментов в CI через «curl … | bash» распространено даже в одиночку, и если это взломано, ваши секреты CI забирают одним махом. Меры защиты отсюда (закрепление версии + проверка контрольной суммы загружаемых артефактов, минимальные привилегии секретов CI, регулярная ротация) работают в любом масштабе.