Исследователь по безопасности Кирилл Бойченко из компании Socket обнаружил активную атаку на цепочку поставок программного обеспечения, затрагивающую сразу две экосистемы — Ruby и Go. За атакой стоит GitHub-аккаунт под названием BufferZoneCorp. Злоумышленники распространяли вредоносные пакеты, замаскированные под популярные библиотеки: activesupport-logger, devise-jwt, go-retryablehttp, grpc-client и config-loader. На момент публикации Ruby-пакеты уже удалены с RubyGems, Go-модули заблокированы.

Всего выявлено 16 вредоносных пакетов: 7 Ruby gems и 9 Go-модулей. Среди Ruby-пакетов числятся knot-activesupport-logger, knot-devise-jwt-helper, knot-rack-session-store, knot-rails-assets-pipeline, knot-rspec-formatter-json, а также два так называемых «спящих» пакета — knot-date-utils-rb и knot-simple-formatter. В Go-экосистеме атакующие разместили , go-weather-sdk, go-retryablehttp, go-stdlib-ext, grpc-client, net-helper, config-loader, и два «спящих» модуля — log-core и go-envconfig.
Термин «спящий пакет» здесь не метафора. Такие пакеты не содержат немедленно активного вредоносного кода при первом анализе — они выглядят безобидно, но закладываются в инфраструктуру заранее, ожидая обновления или иного триггера для активации. Это существенно затрудняет обнаружение через стандартные статические проверки.
Ruby-часть атаки нацелена на максимально быстрое хищение данных прямо в момент установки пакета. Как только разработчик или CI-runner выполняет установку, вредоносный код без каких-либо дополнительных действий собирает переменные окружения, SSH-ключи, секреты AWS, файлы.npmrc и.netrc, конфигурацию GitHub CLI и учётные данные RubyGems. Всё это немедленно уходит на подконтрольный атакующим эндпоинт — простой и трудно блокируемый способ эксфильтрации данных.
Go-модули работают иначе и преследуют более долгосрочные цели. Вредоносная логика распределена по нескольким пакетам кластера, а не сконцентрирована в одном месте. Запуск происходит через функцию init(), которая в Go выполняется автоматически при импорте пакета. Первым делом код проверяет наличие переменных GITHUB_ENV и GITHUB_PATH, характерных для среды GitHub Actions.
Если переменные найдены, пакет выставляет HTTP_PROXY и HTTPS_PROXY, после чего записывает в кэш-директорию поддельный исполняемый файл Go и добавляет эту директорию в PATH рабочего процесса так, чтобы фейковая обёртка вызывалась раньше настоящего бинарника. Хитрость в том, что обёртка не ломает сборку — она перехватывает управление, делает своё дело и передаёт выполнение легитимному Go-инструменту. CI-задача завершается успешно, никаких ошибок нет, а атака остаётся незамеченной.
Помимо манипуляций с GitHub Actions, Go-пакеты реализуют механизм персистентности: в файл ~/.ssh/authorized_keys записывается жёстко прошитый SSH-публичный ключ атакующего. После этого злоумышленник получает постоянный удалённый доступ к скомпрометированному хосту — независимо от того, будет ли пакет впоследствии удалён.
Основные цели атаки — разработчики, CI-раннеры и любые build-окружения, где эти пакеты могли быть установлены. Особую опасность это несёт для командных и корпоративных сред, где компрометация одного CI-раннера открывает доступ к секретам всей организации.
Тем, кто устанавливал перечисленные пакеты, необходимо действовать незамедлительно. Первый шаг — полное удаление пакетов со всех систем. Затем нужно проверить файл ~/.ssh/authorized_keys на предмет посторонних ключей и убрать всё, что туда не добавлялось вручную. Все потенциально скомпрометированные учётные данные — AWS, SSH, GitHub, RubyGems — подлежат немедленной ротации. Сетевые логи стоит просмотреть на предмет исходящих HTTPS-соединений с . Наконец, нужно проверить системные файлы на признаки несанкционированного доступа к чувствительным данным.
Эта атака наглядно показывает, насколько уязвимыми оказываются современные сборочные конвейеры к пакетам с говорящими именами из знакомых неймспейсов. Префикс knot- или принадлежность к аккаунту BufferZoneCorp — детали, которые легко пропустить при беглом взгляде на зависимости. Механизм с подменой Go-бинарника через PATH — отдельно изящный технический приём, поскольку он не требует привилегий и не вызывает ошибок в логах сборки.

Изображение носит иллюстративный характер
Всего выявлено 16 вредоносных пакетов: 7 Ruby gems и 9 Go-модулей. Среди Ruby-пакетов числятся knot-activesupport-logger, knot-devise-jwt-helper, knot-rack-session-store, knot-rails-assets-pipeline, knot-rspec-formatter-json, а также два так называемых «спящих» пакета — knot-date-utils-rb и knot-simple-formatter. В Go-экосистеме атакующие разместили , go-weather-sdk, go-retryablehttp, go-stdlib-ext, grpc-client, net-helper, config-loader, и два «спящих» модуля — log-core и go-envconfig.
Термин «спящий пакет» здесь не метафора. Такие пакеты не содержат немедленно активного вредоносного кода при первом анализе — они выглядят безобидно, но закладываются в инфраструктуру заранее, ожидая обновления или иного триггера для активации. Это существенно затрудняет обнаружение через стандартные статические проверки.
Ruby-часть атаки нацелена на максимально быстрое хищение данных прямо в момент установки пакета. Как только разработчик или CI-runner выполняет установку, вредоносный код без каких-либо дополнительных действий собирает переменные окружения, SSH-ключи, секреты AWS, файлы.npmrc и.netrc, конфигурацию GitHub CLI и учётные данные RubyGems. Всё это немедленно уходит на подконтрольный атакующим эндпоинт — простой и трудно блокируемый способ эксфильтрации данных.
Go-модули работают иначе и преследуют более долгосрочные цели. Вредоносная логика распределена по нескольким пакетам кластера, а не сконцентрирована в одном месте. Запуск происходит через функцию init(), которая в Go выполняется автоматически при импорте пакета. Первым делом код проверяет наличие переменных GITHUB_ENV и GITHUB_PATH, характерных для среды GitHub Actions.
Если переменные найдены, пакет выставляет HTTP_PROXY и HTTPS_PROXY, после чего записывает в кэш-директорию поддельный исполняемый файл Go и добавляет эту директорию в PATH рабочего процесса так, чтобы фейковая обёртка вызывалась раньше настоящего бинарника. Хитрость в том, что обёртка не ломает сборку — она перехватывает управление, делает своё дело и передаёт выполнение легитимному Go-инструменту. CI-задача завершается успешно, никаких ошибок нет, а атака остаётся незамеченной.
Помимо манипуляций с GitHub Actions, Go-пакеты реализуют механизм персистентности: в файл ~/.ssh/authorized_keys записывается жёстко прошитый SSH-публичный ключ атакующего. После этого злоумышленник получает постоянный удалённый доступ к скомпрометированному хосту — независимо от того, будет ли пакет впоследствии удалён.
Основные цели атаки — разработчики, CI-раннеры и любые build-окружения, где эти пакеты могли быть установлены. Особую опасность это несёт для командных и корпоративных сред, где компрометация одного CI-раннера открывает доступ к секретам всей организации.
Тем, кто устанавливал перечисленные пакеты, необходимо действовать незамедлительно. Первый шаг — полное удаление пакетов со всех систем. Затем нужно проверить файл ~/.ssh/authorized_keys на предмет посторонних ключей и убрать всё, что туда не добавлялось вручную. Все потенциально скомпрометированные учётные данные — AWS, SSH, GitHub, RubyGems — подлежат немедленной ротации. Сетевые логи стоит просмотреть на предмет исходящих HTTPS-соединений с . Наконец, нужно проверить системные файлы на признаки несанкционированного доступа к чувствительным данным.
Эта атака наглядно показывает, насколько уязвимыми оказываются современные сборочные конвейеры к пакетам с говорящими именами из знакомых неймспейсов. Префикс knot- или принадлежность к аккаунту BufferZoneCorp — детали, которые легко пропустить при беглом взгляде на зависимости. Механизм с подменой Go-бинарника через PATH — отдельно изящный технический приём, поскольку он не требует привилегий и не вызывает ошибок в логах сборки.