Компрометация GitHub Action codfish/semantic-release-action: подмена тегов и кража токенов с использованием скрытого канала

security

24 июня 2026 года злоумышленник скомпрометировал репозиторий codfish/semantic-release-action, популярного инструмента для автоматизации релизов в GitHub Actions. Атака была реализована через подмену Git-тегов: злоумышленник принудительно переместил несколько версионных тегов, включая плавающие v2, v3, v4 и v5, на вредоносный коммит. Любой рабочий процесс (workflow), ссылающийся на это действие по одному из перемещённых тегов, после 15:39 UTC 24 июня выполнял код атакующего внутри своего runner'а (среды выполнения действий).

Описание

codfish/semantic-release-action представляет собой обёртку над библиотекой semantic-release, используемую для автоматического определения номеров версий, генерации журналов изменений, создания тегов и публикации пакетов. Это действие применяется с 2019 года, имеет более 100 звёзд на GitHub и используется тысячами проектов. Как правило, такие рабочие процессы содержат GITHUB_TOKEN и нередко NPM_TOKEN с правами публикации - именно тот тип доступа, который представляет наибольший интерес для атакующего.

Механизм атаки основывался на том, что Git-теги по умолчанию не защищены от перемещения. Любой, кто имеет права на запись в репозиторий, может принудительно перенаправить тег на другой коммит. GitHub Actions разрешает ссылку на тег в момент выполнения workflow, поэтому перемещение тега переписывает все будущие запуски, ссылающиеся на него, без какого-либо уведомления автора workflow. Злоумышленник в два этапа перенаправил шестнадцать тегов, включая все мажорные версии (v2-v5). Первый вредоносный коммит захватил пятнадцать тегов, второй - тег v2. Оба коммита содержат идентичную полезную нагрузку - файл index.js одинакового хеша.

Второй важный элемент атаки - замена конфигурации action.yml. Изначально действие работало как Docker-based: оно собирало контейнер из Dockerfile и выполняло entrypoint.js. Вредоносный коммит заменил action.yml на composite action (составное действие). В новом файле были добавлены два шага: первый использует легитимное стороннее действие oven-sh/setup-bun для установки среды выполнения Bun, второй запускает файл index.js с помощью команды bun run. Оба шага имеют условие if: always(), что гарантирует их выполнение независимо от успеха или неудачи предыдущего шага. Исходные файлы Dockerfile, entrypoint.js и entrypoint.spec.js остались в репозитории - они просто не вызываются, что создаёт иллюзию неизменности содержимого.

Полезная нагрузка index.js представляет собой 512 КБ обфусцированного JavaScript, закодированного как массив строк с шестнадцатеричными именами переменных - типичный вывод коммерческого обфускатора. Внутри скрыты несколько механизмов. Во-первых, система мертвого ящика (dead-drop), использующая публичный API поиска коммитов GitHub. Проведённый анализ показывает, что полезная нагрузка ищет коммиты, содержащие строку RevokeAndItGoesKaboom, за которой следует зашифрованный AES-256-CBC GitHub-токен (Fine-Grained PAT). После расшифровки с помощью жёстко заданного ключа token используется для эксфильтрации данных и выполнения команд. Второй маркер - thebeautifulsnadsoftime (с опечаткой вместо TheBeautifulSandsOfTime) - позволяет доставлять подписанные RSA команды, которые выполняются через eval(). Таким образом, злоумышленник получает удалённое выполнение кода на любом инфицированном runner'е, не используя традиционную C2-инфраструктуру и не вызывая подозрительного сетевого трафика, так как весь обмен идёт через api.github.com.

Полезная нагрузка включает несколько модулей. Она проверяет локаль среды: если она начинается с ru, выполнение останавливается (killswitch). Затем payload извлекает GitHub OIDC-токены и персональные токены доступа (PAT), используя шаблоны известных форматов. Собранные данные шифруются AES-128-GCM и эксфильтруются через создание нового репозитория GitHub. Также payload содержит механизм заражения AI-ассистентов разработки: он внедряет в репозиторий файлы конфигурации для Claude, Copilot, Cline, Aider и других инструментов, чтобы при следующем открытии репозитория на машине разработчика выполнялся вредоносный код. Кроме того, payload сканирует ~/.ssh/known_hosts и ~/.ssh/config и распространяется на обнаруженные SSH-хосты, копируя себя через scp и выполняя bun run на удалённых машинах.

Этот инцидент имеет прямое отношение к утечке инструментария Miasma, произошедшей 10 июня 2026 года. Тот же самый маркер TheBeautifulSandsOfTime уже был замечен в кампаниях против пакетов npm под управлением @redhat-cloud-services и других скомпрометированных репозиториях. После публикации такого credential-stealing toolkit его используют разные операторы без необходимости писать собственные инструменты. Компрометация codfish/semantic-release-action - часть этой же волны.

Последствия атаки крайне серьёзны: любой проект, использующий скомпрометированные теги, рисковал потерей токенов доступа, включая токены публикации в npm, PyPI и RubyGems. Злоумышленник мог не только украсть данные, но и опубликовать вредоносные версии пакетов от имени легитимных мейнтейнеров, подписывая их через Sigstore для обхода политик безопасности. Распространение через SSH и заражение AI-ассистентов создаёт долгосрочную угрозу для разработчиков, которые клонируют заражённые репозитории.

Защита от подобных атак включает несколько мер. Фиксация действий по полному SHA коммита (а не по подвижному тегу) гарантирует, что workflow выполняет именно тот код, который был одобрен при последнем аудите. Решения класса StepSecurity Harden-Runner обеспечивают мониторинг процессов, блокировку аномальных обращений к памяти и эксфильтрации на уровне runner'а. Политика Compromised Actions Policy блокирует запуск скомпрометированных действий до выполнения. Обнаружение подставных коммитов (Imposter Commit Detection) позволяет выявить ситуации, когда коммит не является частью ни одной легитимной ветки или тега репозитория. Внедрение этих инструментов значительно снижает вероятность успешной атаки на цепочку поставок через подмену тегов GitHub Actions.

Индикаторы компрометации

Malicious commits

  • 5792aba0e2180b9b80b77644370a6889d5817456 (tags v2.2.1, v3, v3.0.0 through v3.5.0, v4, v4.0.0, v4.0.1, v5, v5.0.0)
  • bcb6b1d409144318e8fad2171d6fe06d02299d1a (tag v2)

Payload hash

  • index.js (both malicious commits): sha256 9f93d77d32833a515bc406c46da477142bb1ac2babeecb6aa42f98669a6db015

Other indicators

  • Dead drop marker string: thebeautifulsnadsoftime
  • Bun runtime pulled in via oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6

Affected tags

  • codfish/semantic-release-action@v2
  • codfish/semantic-release-action@v2.2.1
  • codfish/semantic-release-action@v3 through v3.5.0
  • codfish/semantic-release-action@v4, v4.0.0, v4.0.1
  • codfish/semantic-release-action@v5, v5.0.0

Confirmed clean

  • codfish/semantic-release-action@v1.0.0 through v1.10.0
  • codfish/semantic-release-action@v2.0.0

Комментарии: 0