CriticalSupply chainPatch available

Microsoft durabletask 1.4.1 to 1.4.3 on PyPI: import-time cloud credential stealer with a systemd backdoor and geo-targeted wiper

durabletask 1.4.1, 1.4.2 and 1.4.3 stole cloud, Kubernetes and Vault secrets on import on Linux. Check for pgsql-monitor.service, then rotate and rebuild.

Published
Updated
Fixed in
durabletask 1.4.0 (last clean), 1.5.0 and later (published through trusted publishing)
Exploitation
Yes. Three malicious versions were on PyPI for about 1 hour 45 minutes on 19 May 2026

What happened

On 19 May 2026, three malicious releases of durabletask, Microsoft’s Python SDK for the Durable Task framework, were uploaded to PyPI within 35 minutes (StepSecurity):

  • 1.4.1 at 16:19 UTC, with the dropper in __init__.py
  • 1.4.2 at 16:49 UTC, also in task.py
  • 1.4.3 at 16:54 UTC, in five files including entities/__init__.py, extensions/__init__.py and payload/__init__.py

A user reported it in issue #137 at 17:54 UTC and Microsoft yanked the packages at 18:04 UTC. PyPI then quarantined the project. A Microsoft maintainer said on 20 May that 1.4.0 “is safe for use”. Microsoft moved its release pipeline to an internal process and published 1.5.0 on 5 June through trusted publishing (issue #137; PyPI).

No git tags were pushed and no CI ran that day, so StepSecurity concludes the attacker uploaded directly with a stolen PyPI token. The project’s workflow used a stored PYPI_API_TOKEN, not trusted publishing (StepSecurity). Microsoft did not state a root cause in the issue.

Attribution. The OSV record calls TeamPCP the likely actor, based on shared infrastructure with the @antv and guardrails-ai waves (OSV PYSEC-2026-207). StepSecurity ties the backup C2 domain to TeamPCP’s Mini Shai-Hulud campaign. See our TeamPCP advisory.

How the payload works

Details from StepSecurity:

  1. Import, not install. pip install was quiet. On import durabletask on Linux only, about 14 lines in place of the copyright header download https://check.git-service.com/rope.pyz to /tmp/managed.pyz and start it in a new session with output discarded.
  2. Gates. It exits on non-Linux systems, on a LANG starting with ru, and on hosts with two or fewer CPUs.
  3. Collectors. AWS (env, IMDSv2, ~/.aws/credentials, then Secrets Manager and SSM Parameter Store across 19 regions); Azure (service principals, CLI token cache, managed identity, Key Vault secrets); GCP (service-account keys, metadata server, Secret Manager); Kubernetes (kubeconfig, secrets in all namespaces); HashiCorp Vault (all KV secrets); 1Password, Bitwarden, pass and gopass, with brute-force unlock attempts; and 99 file paths, including SSH keys, .env, Terraform state, Docker, npm, PyPI and Cargo tokens, Claude Desktop, Cursor, VS Code, Zed and Continue configs, VPN configs and shell history.
  4. Exfiltration. AES-256-GCM with an RSA-4096 wrapped key, POSTed to https://check.git-service.com/api/public/version. Fallbacks use GitHub commit beacons containing FIRESCALE, and public repositories created with stolen tokens under Russian folklore names such as BABA-YAGA, KOSCHEI and FIREBIRD.
  5. Spread. It runs AWS-RunShellScript through SSM on up to 5 instances per profile, and kubectl exec in up to 5 pods, with t.m-kosche[.]com as a fallback download host.
  6. Persistence or wipe. If it sees Israeli or Iranian timezone or locale markers, it rolls a die; on a 2 it plays audio and runs rm -rf /*. Otherwise it installs a fake systemd user service pgsql-monitor.service that runs pgmonitor.py.

Am I affected?

bash
# Installed version
pip show durabletask 2>/dev/null | grep -E '^Version'

# Pins in requirements and lockfiles
grep -rnE '^durabletask(\[[^]]*\])?==1\.4\.[123]' --include='requirements*.txt' .
grep -nE -A1 'name = "durabletask"' poetry.lock uv.lock 2>/dev/null | grep -E '"1\.4\.[123]"'

# Host artefacts named by StepSecurity and OSV
ls -la /tmp/managed.pyz /usr/bin/pgmonitor.py ~/.local/bin/pgmonitor.py \
  ~/.cache/.sys-update-check ~/.cache/.sys-update-check-k8s 2>/dev/null
systemctl --user status pgsql-monitor.service 2>/dev/null
ps aux | grep -F 'managed.pyz' | grep -v grep

Run the host checks on every Linux machine and node in the same AWS account or Kubernetes cluster as an affected host, because the payload spread through SSM and kubectl exec.

Indicators (StepSecurity; OSV PYSEC-2026-207):

  • Domains and IPs: check.git-service[.]com (160.119.64.3), t.m-kosche[.]com (185.95.159.32)
  • rope.pyz SHA-256: 069ac1dc7f7649b76bc72a11ac700f373804bfd81dab7e561157b703999f44ce
  • Wheel SHA-256: 1.4.1 7d80b3ef74ad7992b93c31966962612e4e2ceb93e7727cdbd1d2a9af47d44ba8, 1.4.2 aeaf583e20347bf850e2fabdcd6f4982996ba023f8c2cd56bbd299cfd56516f5, 1.4.3 877ff2531a63393c4cb9c3c86908b62d9c4fc3db971bc231c48537faae6cb3ec
  • sdist SHA-256: 1.4.1 3de04fe2a76262743ed089efa7115f4508619838e77d60b9a1aab8b20d2cc8bf, 1.4.2 85f54c089d78ebfb101454ec934c767065a342a43c9ee1beac8430cdd3b2086f, 1.4.3 c0b094e46842260936d4b97ce63e4539b99a3eae48b736798c700217c52569dc

In AWS CloudTrail, look for SendCommand with AWS-RunShellScript and bulk GetSecretValue or GetParameter calls from affected roles after 16:19 UTC on 19 May. In Kubernetes audit logs, look for exec into pods you did not expect. In GitHub, look for new public repositories with folklore names created by your tokens.

What to do now

  1. Isolate and rebuild every affected host and container rather than uninstalling, as StepSecurity advises. Stop and remove pgsql-monitor.service and pgmonitor.py only after you have captured them.
  2. Pin a clean version. 1.4.0 or 1.5.0 and later. PyPI lists 1.12.0 as the latest release as of 2 November 2026 (PyPI).
  3. Rotate. AWS keys and every Secrets Manager and SSM value the role could read; Azure service principal secrets and certificates and Key Vault secrets; GCP service-account keys and Secret Manager values; Kubernetes secrets in all namespaces; Vault tokens and KV secrets; GitHub tokens and SSH keys; password manager master passwords if a vault was present; and secrets in .env files and Terraform state.
  4. Check neighbours. Hunt on SSM-managed instances and pods reachable from the affected host.
  5. Block git-service.com and m-kosche.com at egress.
  6. Do not open the Azure/durabletask repository at commit 5f456b8 in an AI coding tool or editor. StepSecurity reports that on 5 June the same contributor account pushed .claude/settings.json, .gemini/settings.json, .cursor/rules/setup.mdc, .vscode/tasks.json and .github/setup.js, which run a 4.6 MB payload when the repository is opened. Anyone who did so after that commit should treat the machine as compromised.

Sources