Date Observed: May 19, 2026
Ecosystem: PyPI (Python)
Targets: CI/CD runners, cloud workloads, Kubernetes clusters, developer environments consuming durabletask
Attack Type: Supply chain compromise via stolen PyPI publishing token; import-time malicious loader with multi-stage payload
Impact: Multi-cloud credential theft (AWS, Azure, GCP, Kubernetes, HashiCorp Vault, password managers); lateral movement via AWS SSM and kubectl exec; geotargeted disk wiper; persistent systemd backdoor
Key Takeaways
- Three malicious versions of Microsoft’s durabletask Python SDK: 1.4.1, 1.4.2, and 1.4.3, were published to PyPI on May 19, 2026, within a 35-minute window using a stolen PyPI API token.
- The dropper fires at import time. No execution beyond import durabletask is required for the payload to run.
- The second-stage payload, rope.pyz, harvests credentials from AWS, Azure, GCP, Kubernetes, HashiCorp Vault, and over 90 filesystem paths including SSH keys, .env files, and Terraform state.
- The worm propagates laterally to up to five additional hosts per infection via AWS SSM SendCommand and Kubernetes kubectl exec.
- The attack is linked to TeamPCP’s Mini Shai-Hulud campaign; the same group behind recent compromises of TanStack, guardrails-ai, and the @antv npm ecosystem. Attribution is assessed with high confidence based on C2 infrastructure overlap (t.m-kosche[.]com is established TeamPCP infrastructure) and consistent campaign fingerprints including Russian folklore exfiltration repo names and the Russian locale kill switch.
Three versions of Microsoft’s official durabletask Python SDK were published to PyPI on May 19, 2026, containing a dropper that executes at import time. The attacker did not breach Microsoft’s GitHub repository. They obtained the PyPI publishing token, stored as a GitHub Actions secret, through a previously compromised GitHub account and uploaded the malicious packages directly via twine, bypassing the repository’s CI/CD pipeline entirely. No matching tags, releases, or workflow runs exist in the GitHub repository for versions 1.4.1, 1.4.2, or 1.4.3. The repository did not use PyPI’s OIDC Trusted Publishing mechanism, so possession of the token was sufficient to publish any version from any machine.
durabletask is the official Python SDK for Microsoft’s Durable Task Framework, used for workflow orchestration in Azure-connected environments. It receives over 400,000 downloads per month. The infected versions were active on PyPI until Microsoft confirmed the compromise, and PyPI quarantined all three.
Scope and Impact of the Attack
The three infected versions were published within a 35-minute window. Each version escalated the injection surface:
| Version | Infected Files |
| 1.4.1 | durabletask/__init__.py |
| 1.4.2 | + durabletask/task.py |
| 1.4.3 | +durabletask/entities/__init__.py, durabletask/extensions/__init__.py, durabletask/payload/__init__.py |
By version 1.4.3, the dropper exists in five files. Any import path into the package triggers the payload. The package primarily targets Linux systems, which makes CI/CD runners, cloud workloads, and Kubernetes nodes the most likely execution targets.
The second-stage payload (rope.pyz) targets credentials across every major cloud provider, password managers (1Password, Bitwarden, pass, gopass), over 90 hardcoded filesystem paths, and AI developer tooling configurations including Claude Desktop, Cursor MCP, and VS Code. Stolen data is encrypted with AES-256-GCM; the session key is wrapped with an attacker-controlled RSA-4096 public key, and the bundle is exfiltrated via three fallback channels. Lateral movement propagates the worm to up to five additional EC2 instances, or Kubernetes pods per infected host.
How the Attack Works
Stage 1: Import-Time Dropper
The attacker cloned the legitimate 1.4.0 source, injected the malicious loader into init.py, and uploaded with twine. The process is fully detached via start_new_session=True. All outputs are redirected to /dev/null. The bare except: pass suppresses all errors. A developer running import durabletask sees nothing.
Stage 2: Payload Execution: rope.pyz
rope.pyz is a 28 KB Python zipapp (a ZIP archive executable directly by Python) downloaded from check.git-service[.]com, a domain registered May 16, 2026; three days before the attack. The server only serves the payload to requests using Python-urllib/3.11 as the User-Agent, blocking most automated scanners and manual curl attempts, according to Upwind.
Before executing, rope.pyz runs three gate checks. Specifically, it exits if any of the following is true:
- Platform: exit immediately if not Linux.
- Locale: exits if $LANG starts with ru. Russian-locale systems are excluded.
- CPU count: exits if os.cpu_count() <= 2, filtering out lightweight sandbox VMs.
If all gates pass, it self-installs the cryptography package via pip (–break-system-packages) and begins concurrent credential collection across seven collector modules via ThreadPoolExecutor.
Stage 3: Credential Harvesting
The payload runs seven collector modules in parallel targeting: AWS Secrets Manager and SSM Parameter Store across 19 regions; Azure Key Vault across all subscriptions; GCP Secret Manager; Kubernetes secrets across all namespaces and contexts; HashiCorp Vault KV mounts; password managers (1Password, Bitwarden, pass, gopass) with brute-force unlock attempts; and 90+ filesystem paths covering SSH keys, cloud configs, Docker credentials, .env files, Terraform state, VPN configs, and shell histories.
Stage 4: Exfiltration with Three-Tier Fallback
All collected data is gzip-compressed, encrypted with AES-256-GCM, and the session key is wrapped with an RSA-4096 public key. Exfiltration proceeds in order:
- Primary C2: HTTPS POST to check.git-service[.]com/api/public/version
- FIRESCALE dead-drop: If C2 is unreachable, the payload searches GitHub’s commit search API for the string FIRESCALE. Matching commits contain a base64-encoded URL with an RSA-SHA256 signature verified against the same hardcoded public key. Only the attacker can publish valid new C2 addresses this way.
- GitHub token exfiltration: If both fail, the payload uses any stolen GitHub tokens to create public repositories with Russian folklore-themed names (BABA-YAGA, KOSCHEI, FIREBIRD, RUSALKA, LESHY, MOROZKO) and uploads the encrypted bundle as results.json.
Stage 5: Lateral Movement and Persistence
The worm propagates via AWS SSM SendCommand (up to five EC2 instances per host) and kubectl exec (up to five Kubernetes pods). Marker files at ~/.cache/.sys-update-check and ~/.cache/.sys-update-check-k8s prevent re-infection.
When check.git-service[.]com/v1/models returns HTTP 200, the response activates roulette.py. On non-targeted systems it installs a pgsql-monitor.service systemd backdoor disguised as a PostgreSQL monitor. On systems with Israeli or Iranian locale markers (Jerusalem, Tel_Aviv, Tehran, he_IL, fa_IR), a 1-in-6 roll triggers the disk wiper: the payload downloads RunForCover.mp3 from C2, sets system volume to maximum, plays the audio, and executes rm -rf /*.
Why This Matters to DevOps and DevSecOps Teams
This attack exploited a gap that most build pipelines have long-lived publishing tokens stored as GitHub Secrets with no binding to a specific workflow or environment. The repository published to PyPI via a PYPI_API_TOKEN secret used by three separate workflows. Because the project did not use PyPI’s OIDC Trusted Publishing, a single stolen token was sufficient to publish any version from any machine outside the CI/CD pipeline. The malicious packages produced no CI/CD activity, no commits, and no tags; nothing an internal audit of the repository would catch.
The 400,000+ monthly downloads mean this package is present in CI/CD pipelines, Docker images, and cloud workload dependencies across a significant number of organizations. Because the dropper fires at import time, the payload executes the first time a CI job runs import durabletask; before any code in the build does anything with the package. A build runner that imports this package in a dependency installation step is already compromised before the build logic begins.
The FIRESCALE dead-drop is operationally significant. Domain seizure or sinkholing; the standard response to C2 infrastructure; does not stop this campaign. The attacker can resume operations with a single public GitHub commit signed with their private key.
How InvisiRisk Protects Against This Attack
Build Proxy / Network Interception: InvisiRisk’s BAF operates as a network proxy inside the CI/CD build environment. When the dropper executes at import time and attempts to connect to check.git-service[.]com to download rope.pyz, BAF intercepts the outbound connection and blocks it before the payload reaches the runner. The C2 callback never completes.

Figure: InvisiRisk BAF’s Outbound Untrusted Host Policy blocking the malicious host.
Stability Buffer: InvisiRisk’s BAF Stability Buffer blocks newly published package versions from entering protected builds during a configurable time window. Because versions 1.4.1, 1.4.2, and 1.4.3 were published within 35 minutes of each other, a 48-hour Stability Buffer would have blocked all three before they could enter a protected pipeline.
Unauthorized API Action Enforcement: InvisiRisk’s BAF enforces policy on outbound API calls during builds. The tertiary exfiltration path; using stolen GitHub tokens to create public repositories and upload encrypted credential bundles; involves PUT/POST operations to the GitHub API outside the expected build scope. BAF blocks these operations, preventing exfiltration via the GitHub token fallback channel.
For teams running durabletask in CI/CD pipelines, InvisiRisk’s Build Application Firewall closes the gap that token-based PyPI publishing leaves open. This attack follows the same pattern as previous Mini Shai-Hulud campaign compromises and the earlier TeamPCP campaign analysis: the infrastructure and techniques are the same, but the target package changed.
Why Build-Time Defenses Matter
The durabletask compromise shows how a stolen publishing token can bypass repository-level controls and push malicious code directly into a trusted package ecosystem. For teams running Python packages in CI/CD pipelines, cloud workloads, and orchestrated environments, this is a build-time and runtime supply chain problem, not just a repository security problem. Until package publishing is bound to specific workflows through mechanisms like Trusted Publishing, a stolen token remains enough to distribute malware at scale through otherwise legitimate software.


