Gheist: Попытка масштабной эксплуатации украденных токенов GitHub столкнулась с препятствиями

Источник: wiz.io•

Gheist: Попытка масштабной эксплуатации украденных токенов GitHub столкнулась с препятствиями

Автоматизированная кампания внедряет рабочие процессы для дампа секретов во все репозитории, к которым имеют доступ украденные токены GitHub. Она не использует C2-сервер, активно развивается и была существенно нарушена недавними улучшениями безопасности GitHub.

С 15 сентября 2026 года Wiz Research отслеживает Gheist — новую кампанию, осуществляющую массовую эксплуатацию скомпрометированных токенов GitHub. Атака фиксирует рабочий процесс с именем sync-check в каждом репозитории, к которому у каждого токена есть доступ. Этот рабочий процесс пытается вывести секреты репозитория в собственный лог. Затем инструмент скачивает лог через API GitHub и удаляет запуск секундами позже. Запросы API инструмента содержат user agent gheist, что и дало название кампании.

Мы обнаружили более 13 500 внедренных веток из более чем 850 скомпрометированных аккаунтов, начиная от личных проектов и заканчивая частными репозиториями корпоративных организаций.

Недавний механизм контроля со стороны GitHub блокирует эксплуатацию в публичных репозиториях, но мы наблюдали успешную эксплуатацию и последующую активность в частных репозиториях и средах GitHub Enterprise.

По состоянию на 24 октября:

  • атака продолжается и развивается

атака продолжается и развивается

  • примерно ⅔ затронутых аккаунтов больше недоступны на GitHub. Это, вероятно, свидетельствует о мерах реагирования, так как мы сообщили об этой активности в GitHub.

примерно ⅔ затронутых аккаунтов больше недоступны на GitHub. Это, вероятно, свидетельствует о мерах реагирования, так как мы сообщили об этой активности в GitHub.

Как работает атака

Для каждого репозитория инструмент создает ветку с именем ci-sync- и шестью случайными символами, после чего фиксирует .github/workflows/sync-check.yml с сообщением ci: sync config. Коммит является неподписанным и создается от имени владельца токена. Событие push запускает рабочий процесс:

Эффективность обеспечивают три детали:

  • GitHub маскирует значения секретов в логах только тогда, когда они появляются в неизменном виде. Секрет, закодированный в base32, не совпадает, поэтому он выводится полностью.

GitHub маскирует значения секретов в логах только тогда, когда они появляются в неизменном виде. Секрет, закодированный в base32, не совпадает, поэтому он выводится полностью.

  • Хотя permissions: {} ограничивает GITHUB_TOKEN задания, который не нужен злоумышленнику, он не ограничивает контекст secrets.

Хотя permissions: {} ограничивает GITHUB_TOKEN задания, который не нужен злоумышленнику, он не ограничивает контекст secrets.

  • Когда в репозитории настроены среды развертывания (deployment environments), инструмент перечисляет их в матрице заданий с помощью environment: ${{ matrix.environment }}, поэтому секреты, привязанные к средам (часто это производственные учетные данные), также выводятся.

Когда в репозитории настроены среды развертывания (deployment environments), инструмент перечисляет их в матрице заданий с помощью environment: ${{ matrix.environment }}, поэтому секреты, привязанные к средам (часто это производственные учетные данные), также выводятся.

Поскольку секреты уходят через собственный API GitHub, у злоумышленника нет ни собственного домена, ни исходящего соединения для блокировки.

В корпоративных организациях мы наблюдали, как инструмент в течение нескольких минут перебирал токены нескольких пользователей и автоматизированных аккаунтов, клонировал частные репозитории перед внедрением рабочего процесса, а часы спустя использовал облачные учетные данные из той же организации для перечисления AWS Secrets Manager.

Как эволюционировала полезная нагрузка

Параллельно наблюдались два варианта этой атаки. Их можно различить по именам веток.

Инструмент A использует шестнадцатеричный суффикс и простую полезную нагрузку.

Инструмент B использует суффикс в base36, добавляет timeout-minutes: 6 и матрицу сред всего через несколько часов после своего первого запуска 28 сентября, а также модифицирует выбранную полезную нагрузку для каждого репозитория.

19 октября Инструмент B добавил echo X | base32 -w 120 между дампами, что выводит LAFA==== в качестве разделителя, вероятно, для парсера логов злоумышленника. Спустя часы он зафиксировал второе задание, sync-host, с параметром runs-on: self-hosted, по-видимому, только для репозиториев с зарегистрированным самостоятельным хостером (self-hosted runner). Он записывает три вспомогательных файла (.gdmp_pat, .gdmp_patg, .gdmp_sweep) и логирует каждый этап под заголовком ===== gdmp-pivot: <stage> =====:

  • Детали хоста и crontab.

Детали хоста и crontab.

  • Рекурсивный поиск в домашнем каталоге, рабочем пространстве и системных путях 31 формата токенов (включая ключи для ИИ-провайдеров, таких как Anthropic, Hugging Face, Groq и NVIDIA), приватных ключей, файлов .env, состояния Terraform и истории оболочки (shell history).

Рекурсивный поиск в домашнем каталоге, рабочем пространстве и системных путях 31 формата токенов (включая ключи для ИИ-провайдеров, таких как Anthropic, Hugging Face, Groq и NVIDIA), приватных ключей, файлов .env, состояния Terraform и истории оболочки (shell history).

  • Окружение каждого процесса из /proc/*/environ.

Окружение каждого процесса из /proc/*/environ.

  • Через сокет Docker — контейнер с примонтированной корневой файловой системой хоста для повторения поиска на хосте.

Через сокет Docker — контейнер с примонтированной корневой файловой системой хоста для повторения поиска на хосте.

  • Учетные данные роли экземпляра AWS, а также метаданные GCP и Azure.

Учетные данные роли экземпляра AWS, а также метаданные GCP и Azure.

  • Файлы учетных данных раннеров GitHub Actions и GitLab.

Файлы учетных данных раннеров GitHub Actions и GitLab.

  • Токен учетной записи службы Kubernetes.

Токен учетной записи службы Kubernetes.

Тем не менее, это задание содержит ошибки: оно ищет не тот файл ключа раннера, его регулярное выражение для URL-учетных данных повреждено, и оно не собирает токены GCP или Azure.

Задержка утверждения в GitHub и частные репозитории

С 28 июля 2026 года GitHub удерживает потенциально вредоносные запуски рабочих процессов в публичных репозиториях до тех пор, пока их не утвердит соавтор. Каждый проверенный нами публичный запуск Gheist, во всех вариантах, включая задание для самостоятельного хостера, находится в статусе action_required. Механизм контроля сработал, и вредоносные полезные нагрузки никогда не запускались.

К сожалению, эта задержка не распространяется на частные репозитории или GitHub Enterprise Server. Там журналы аудита GitHub показывают завершение запусков в ветках ci-sync-*, после чего они удаляются user agent gheist. CloudTrail показывает, что собственные рабочие процессы организаций выполняются в этих ветках и принимают роли AWS, что доказывает выполнение Actions без утверждения.

Для защитников этот последний сигнал позволяет обнаружить Gheist даже без логов GitHub, как показано ниже.

Другая активность на тех же аккаунтах

Эта кампания использует скомпрометированный доступ к GitHub. Одни и те же аккаунты используются другими злоумышленниками и кампаниями, что демонстрирует, как скомпрометированные учетные данные могут циркулировать по всей экосистеме угроз. Мы наблюдали пересечения со следующими кампаниями:

  • Репозитории-приманки (распространение вредоносного ПО). 6 октября 391 аккаунт переименовал около 1 461 репозитория в приманки с взломанным ПО, такие как fl-studio-crack и solana-sniper-lite. Переименование уничтожило 610 веток ci-sync в качестве побочного эффекта. 166 из этих аккаунтов никогда не появляются в активности Gheist, поэтому пул больше, чем след Gheist.

Репозитории-приманки (распространение вредоносного ПО). 6 октября 391 аккаунт переименовал около 1 461 репозитория в приманки с взломанным ПО, такие как fl-studio-crack и solana-sniper-lite. Переименование уничтожило 610 веток ci-sync в качестве побочного эффекта. 166 из этих аккаунтов никогда не появляются в активности Gheist, поэтому пул больше, чем след Gheist.

  • Бесплатные вычислительные мощности (криптомайнинг). 18 октября 25 аккаунтов Gheist создали по репозиторию «Windows RDP Runner» в течение девяти минут, пытаясь злоупотребить GitHub Actions ради бесплатных вычислительных циклов.

Бесплатные вычислительные мощности (криптомайнинг). 18 октября 25 учетных записей Gheist создали репозитории «Windows RDP Runner» в течение девяти минут, пытаясь использовать GitHub Actions для получения бесплатных вычислительных ресурсов.

  • PolinRider. Некоторые жертвы, судя по всему, заражены PolinRider — вредоносным ПО, связанным с КНДР, которое прячет JavaScript в конфигурационных файлах сборки и принудительно отправляет (force-push) коммиты задним числом. 19 и 20 октября три жертвы повторно отправили свои коммиты Gheist с загрузчиком, добавленным в конец файла vite.config.js после сотен пробелов, а в двух случаях — с файлом api.js, который package.json запускает перед каждой разработкой и сборкой.

. Некоторые жертвы, судя по всему, заражены PolinRider — вредоносным ПО, связанным с КНДР, которое прячет JavaScript в конфигурационных файлах сборки и принудительно отправляет (force-push) коммиты задним числом. 19 и 20 октября три жертвы повторно отправили свои коммиты Gheist с загрузчиком, добавленным в конец файла vite.config.js после сотен пробелов, а в двух случаях — с файлом api.js, который package.json запускает перед каждой разработкой и сборкой.

  • Megalodon. 57 учетных записей из первой фазы Gheist также стали жертвами Megalodon в мае.

Megalodon. 57 учетных записей из первой фазы Gheist также стали жертвами Megalodon в мае.

Источники учетных данных, которые нам удалось отследить, соответствуют этой картине: логи стирателей информации (infostealer) и токены, скомпрометированные в результате более ранних атак на цепочку поставок, таких как взлом Trivy в марте.

Примечание: Gheist отличается от GhostAction, который использует другие файлы рабочих процессов и эксфильтрирует секреты на адрес 193.32.204[.]199.

Какие действия следует предпринять командам безопасности?

Поиск

  • Ищите ветки с именем ci-sync-, за которым следуют шесть случайных символов, где единственным изменением является новый файл .github/workflows/sync-check.yml с сообщением коммита ci: sync config.

Ищите ветки с именем ci-sync-, за которым следуют шесть случайных символов, где единственным изменением является новый файл .github/workflows/sync-check.yml с сообщением коммита ci: sync config.

  • В журналах аудита GitHub ищите запуски рабочих процессов в ветках ci-sync-*, которые были удалены в течение минуты после завершения, а также любые действия от агента пользователя (user agent) gheist.

В журналах аудита GitHub ищите запуски рабочих процессов в ветках ci-sync-*, которые были удалены в течение минуты после завершения, а также любые действия от агента пользователя (user agent) gheist.

  • Если ваши рабочие процессы проходят аутентификацию в облаке с помощью OIDC, проверьте журналы аудита облака на наличие предположений ролей или обменов токенами, в субъекте (subject) которых указана ветка ci-sync-. Это указывает на то, что GitHub Actions выполнялись в ветке злоумышленника, даже если вы не собираете логи GitHub.

Если ваши рабочие процессы проходят аутентификацию в облаке с помощью OIDC, проверьте журналы аудита облака на наличие предположений ролей или обменов токенами, в субъекте (subject) которых указана ветка ci-sync-. Это указывает на то, что GitHub Actions выполнялись в ветке злоумышленника, даже если вы не собираете логи GitHub.

  • На размещенных пользователем исполнителях (self-hosted runners) ищите файлы .gdmp_pat, .gdmp_patg и .gdmp_sweep, а также строку gdmp-pivot в журналах заданий.

На размещенных пользователем исполнителях (self-hosted runners) ищите файлы .gdmp_pat, .gdmp_patg и .gdmp_sweep, а также строку gdmp-pivot в журналах заданий.

Реагирование

  • Ротируйте каждый секрет репозитория, организации и среды, доступный для затронутого репозитория. Удаление запуска или ветки не отменяет факт утечки.

Ротируйте каждый секрет репозитория, организации и среды, доступный для затронутого репозитория. Удаление запуска или ветки не отменяет факт утечки.

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

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

  • Если ветка ci-sync запускалась на хосте self-hosted runner, считайте этот хост и его роль экземпляра скомпрометированными.

Если ветка ci-sync запускалась на хосте self-hosted runner, считайте этот хост и его роль экземпляра скомпрометированными.

  • Не одобряйте приостановленные запуски sync-check и не запускайте код из веток ci-sync.

Не одобряйте приостановленные запуски sync-check и не запускайте код из веток ci-sync.

Усиление защиты. Требуйте проверяющих для сред с производственными секретами, ограничивайте политики доверия OIDC защищенными ветками или средами, используйте GitHub Apps или недолговечные токенизированные доступы с мелкозернистыми правами вместо персональных токенов доступа, а также запускайте self-hosted runners как эфемерные хосты без сокета Docker или неограниченного доступа к метаданным.

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

  • Агент пользователя (user agent) gheist

Агент пользователя (user agent) gheist

  • Ветка ci-sync-, за которой следуют шесть символов, соответствующая регулярному выражению ^ci-sync-[0-9a-z]{6}$

Ветка ci-sync-, за которой следуют шесть символов, соответствующая регулярному выражению ^ci-sync-[0-9a-z]{6}$

  • Сообщение коммита ci: sync config, добавляющее только файл .github/workflows/sync-check.yml

Сообщение коммита ci: sync config, добавляющее только файл .github/workflows/sync-check.yml

  • Маркеры toJSON(secrets) }}' | base32 -w 120 и ===== gdmp-pivot:; файлы .gdmp_pat, .gdmp_patg, .gdmp_sweep

Маркеры toJSON(secrets) }}' | base32 -w 120 и ===== gdmp-pivot:; файлы .gdmp_pat, .gdmp_patg, .gdmp_sweep

  • SHA-хэши блогов рабочих процессов (Workflow blob SHAs): f3da2b8b357dcda02fd455d314be2a95007516a6 (простой), d09535857a741bbb976bdc084a9a021713133291 (тайм-аут), 1dc5af9cce950aad1eb1546ecce8565b117540c4 (разделитель), c665b778b2d822453293003e7ca0bbb2cb08e39d (задание на self-hosted). Варианты матрицы сред различаются в зависимости от репозитория.

SHA-хэши блогов рабочих процессов (Workflow blob SHAs): f3da2b8b357dcda02fd455d314be2a95007516a6 (простой), d09535857a741bbb976bdc084a9a021713133291 (тайм-аут), 1dc5af9cce950aad1eb1546ecce8565b117540c4 (разделитель), c665b778b2d822453293003e7ca0bbb2cb08e39d (задание на self-hosted). Варианты матрицы сред различаются в зависимости от репозитория.

Чем может помочь Wiz?

Рекомендация Threat Center включает запрос для Event Explorer, охватывающий внедренные коммиты, активность Gheist в журналах аудита GitHub и предположения облачных ролей из веток злоумышленников. Wiz Defend обнаруживает эту активность с помощью правил, включая «New Workflow Completed And Deleted In Short Time Span», «Workflow Pushed With Secret Dump» и «Commit Created With Known Malicious Commit Message».

О чём эта статья

Ещё в разделе «Кибербезопасность»

Все →

Ещё от Wiz