Компрометация пакета durabletask на PyPI: атака на цепочку поставок Microsoft Azure Durable Functions

information security

Разработчики, использующие официальный Python-клиент Microsoft Azure Durable Functions, оказались под ударом. В репозитории PyPI обнаружены три вредоносные версии пакета durabletask - 1.4.1, 1.4.2 и 1.4.3. Эти сборки содержат скрытый дроппер, который при импорте библиотеки загружает и выполняет модульный фреймворк для кражи данных и распространения по инфраструктуре. Инцидент напрямую связан с кампанией Mini Shai-Hulud, которую ведёт группировка TeamPCP. Microsoft уже подтвердила компрометацию и удалила проблемные версии из PyPI.

Описание

Пакет durabletask является частью среды выполнения Durable Task, используемой в облачных приложениях Azure. Он загружается более 400 тысяч раз в месяц и применяется в автоматизации, CI/CD и других серверных сценариях. Именно такие среды - Linux-серверы, контейнеры, виртуальные машины - стали главной целью злоумышленников.

Все три версии были загружены на PyPI в течение 35 минут. Ни одна из них не имеет соответствующих тегов или записей в системе непрерывной интеграции Microsoft GitHub. Атакующие получили доступ к учётным данным для публикации пакета, обойдя штатный конвейер сборки. По данным анализа, инцидент стал следствием предыдущей компрометации учётной записи GitHub: злоумышленники извлекли секреты репозитория, включая токен PyPI, и использовали его для прямой загрузки.

Как выяснили специалисты компании в своём анализе, код дроппера встроен непосредственно в исходные файлы пакета. После установки любой из трёх версий при первом импорте durabletask выполняется следующий сценарий. Сначала проверяется, работает ли система под управлением Linux. Если да, дроппер скачивает с сервера управления check.git-service.com файл rope.pyz и сохраняет его в /tmp/managed.pyz. Затем запускает его в отдельном процессе, перенаправляя весь вывод в /dev/null. Ошибки не логируются. Разработчик не видит никаких признаков заражения.

С каждой последующей версией число заражённых файлов увеличивалось: в 1.4.1 - только __init__.py, в 1.4.2 - также task.py, а в 1.4.3 дроппер срабатывает уже из пяти точек входа. Это сделано для того, чтобы payload запускался независимо от того, какой именно модуль импортирует пользователь.

Сам payload rope.pyz - это полноценный фреймворк для кражи учётных данных и бокового перемещения. Перед запуском он проверяет, что система является Linux, количество ядер процессора больше двух (чтобы обойти песочницы), а локаль не начинается с "ru" - характерная черта для группировок из Восточной Европы. Если все проверки пройдены, фреймворк запускает семь сборщиков, работающих параллельно.

Они извлекают данные из всех основных облачных провайдеров: AWS, Azure, GCP. Среди целей - менеджеры паролей (1Password, Bitwarden, pass, gopass), файлы конфигурации Kubernetes, токены HashiCorp Vault, SSH-ключи, Docker-сокет и даже настройки инструментов для разработчиков ИИ (Claude Desktop, Cursor, VS Code). Сборщики также читают файлы .env, историю команд и все переменные окружения.

Все собранные данные сжимаются, шифруются алгоритмом AES-256-GCM, а ключ упаковывается RSA-4096. Расшифровать их может только владелец соответствующего закрытого ключа. Отправка происходит несколькими способами. Первичный - POST-запрос к серверу управления. Если тот недоступен, payload ищет в коммитах GitHub строку "FIRESCALE" и по ней восстанавливает новый адрес, заверенный цифровой подписью. Третий способ - создание публичного репозитория на GitHub с использованием украденных токенов жертвы, куда загружается зашифрованный архив.

Фреймворк также умеет распространяться по инфраструктуре. Внутри AWS он через SSM Agent отправляет команду на выполнение payload на до пяти других EC2-инстансов. В Kubernetes использует kubectl exec для внедрения в до пяти подов. После заражения остаются маркерные файлы, которые предотвращают повторное заражение той же машины.

Особую опасность представляет модуль roulette.py. Если система находится в израильской или иранской локали (проверяется по часовому поясу и языку), модуль с шансом один к шести воспроизводит аудиофайл с сервера управления и выполняет команду rm -rf /*. Если условие не срабатывает, payload устанавливает персистентность в виде фальшивого systemd-сервиса с именем pgsql-monitor.service.

Связь с кампанией TeamPCP подтверждается использованием вторичного домена t.m-kosche.com, который ранее фигурировал в атаках на TanStack, Mistral AI, Guardrails AI и другие проекты. Кроме того, имена репозиториев для эксфильтрации взяты из русского фольклора: Баба-Яга, Кощей, Жар-птица.

Для защиты необходимо немедленно проверить установленные версии durabletask. Если используется любая из трёх проблемных, систему следует изолировать, проверить наличие маркерных файлов в ~/.cache/.sys-update-check и ~/.cache/.sys-update-check-k8s, а также следы сервиса pgsql-monitor.service. Все облачные учётные данные, SSH-ключи и токены, к которым имела доступ заражённая система, подлежат отзыву. На сетевом уровне нужно заблокировать домены check.git-service.com и t.m-kosche.com. В будущем стоит применять верификацию хешей пакетов, использовать OIDC для публикации и ограничивать исходящий трафик из сред выполнения.

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

IPv4

  • 160.119.64.3
  • 83.142.209.194

Domains

  • check.git-service.com
  • git-service.com
  • t.m-kosche.com

URLs

  • http://169.254.169.254/
  • http://169.254.170.2/
  • https://check.git-service.com/api/public/version
  • https://check.git-service.com/audio.mp3
  • https://check.git-service.com/rope.pyz
  • https://check.git-service.com/v1/models
  • https://t.m-kosche.com/rope.pyz
  • https://t.m-kosche.com:443/api/public/otel/v1/traces

SHA256

  • 069ac1dc7f7649b76bc72a11ac700f373804bfd81dab7e561157b703999f44ce
  • 3de04fe2a76262743ed089efa7115f4508619838e77d60b9a1aab8b20d2cc8bf
  • 7c24b4d9a8f448832f3752d7f67dcdbf1b7f0f41e10bf633efa175e627144e8b
  • 7d80b3ef74ad7992b93c31966962612e4e2ceb93e7727cdbd1d2a9af47d44ba8
  • 85f54c089d78ebfb101454ec934c767065a342a43c9ee1beac8430cdd3b2086f
  • 877ff2531a63393c4cb9c3c86908b62d9c4fc3db971bc231c48537faae6cb3ec
  • aeaf583e20347bf850e2fabdcd6f4982996ba023f8c2cd56bbd299cfd56516f5
  • c0b094e46842260936d4b97ce63e4539b99a3eae48b736798c700217c52569dc

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