The supply chain campaign linked to TeamPCP continues with the compromise of durabletask v1.4.1, v1.4.2, and v1.4.3. DurableTask is the official Microsoft Python client for the Durable Task workflow execution framework.
These packages have been quarantined by PyPi, subsequent to Wiz's analysis.
Root Cause
There is a direct link between the previously reported @antv wave and this compromised package.
A compromised user account was identified based on their publication of the repositories implicated in that wave. That same account can be observed targeting the microsoft/durabletask-python between 15:08 UTC and 15:16 UTC:
We can observe the attacker copied the latest commit message from main, and the behavior matches past examples of leveraging compromised credentials to dump secrets from GitHub. Workflows in the repository indicate that the PyPi token was present within the GitHub secrets.
So we can conclude that the attacker compromised a GitHub account via a previous attack, dumped GitHub secrets from a repository to which the user had access, and from there had access to the PyPi token to publish directly.
Payload
The payload is a slight evolution of the one previously deployed in the compromise of the guardrails-ai package on May 11th.
| Attribute | transformers.pyz v2 | durabletask v1.4.1 | durabletask v1.4.2 | durabletask v1.4.3 |
|---|---|---|---|---|
| Status | Malware payload | Compromised | Compromised | Compromised |
| C2 Domain | 83.142.209.194 | check.git-service.com | check.git-service.com | check.git-service.com |
| Backup C2 | - | t.m-kosche.com | t.m-kosche.com | t.m-kosche.com |
| Payload | transformers.pyz | rope.pyz | rope.pyz | rope.pyz |
| Injection Points | __init__.py | __init__.py | task.py | entities/__init__.py, extensions/__init__.py, payload/__init__.py |
| Target OS | Linux | Linux | Linux | Linux |
| Credential Stealing | AWS, Azure, GCP, K8s, Vault, Filesystem, Passwords | Yes (inherited) | Yes (inherited) | Yes (inherited) |
| AWS SSM Propagation | No | Yes | Yes | Yes |
| K8s Lateral Movement | No | Yes | Yes | Yes |
| Password Manager Bruteforce | No | Yes (Bitwarden, 1Password, GPG) | Yes (Bitwarden, 1Password, GPG) | Yes (Bitwarden, 1Password, GPG) |
| History Scraping | No | Yes (.bash_history, .zsh_history) | Yes | Yes |
| Propagation Limit | - | 5 targets/host | 5 targets/host | 5 targets/host |
| Infection Marker | - | ~/.cache/.sys-update-check | ~/.cache/.sys-update-check | ~/.cache/.sys-update-check |
| RSA Key | Key A | Key B (new) | Key B (new) | Key B (new) |
| SSL Verification | Disabled | Enabled | Enabled | Enabled |
What steps should security teams take?
Wiz customers should refer to the Wiz Threat Intelligence Center Advisory on this incident.
Immediately identify exposure: Search lockfiles and CI logs for durabletask versions 1.4.1, 1.4.2, or 1.4.3. Look for
/tmp/managed.pyzor/tmp/rope-*.pyzon Linux systems.Check for persistence: Search for the infection marker
~/.cache/.sys-update-check(AWS/general) and~/.cache/.sys-update-check-k8s(Kubernetes) on affected systems. Presence confirms payload execution. Check for running python3/tmp/managed.pyzprocesses.Rotate all credentials: If exposure is suspected, rotate AWS credentials (especially IAM User credentials), Azure credentials, GCP service accounts, Kubernetes service accounts, Vault tokens, and any passwords stored in Bitwarden, 1Password, or pass/gopass. Assume shell history (.bash_history, .zsh_history) was exfiltrated.
Audit AWS SSM and Kubernetes: Check CloudTrail for SSM:SendCommand and SSM:DescribeInstanceInformation calls from compromised instances. Review Kubernetes audit logs for unexpected kubectl exec activity. The worm propagates to up to 5 targets per infected host.
Check password manager sessions: Review Bitwarden (bw) and 1Password (op) CLI usage. The payload attempts brute-force unlock using harvested passwords from environment variables and shell history.
Block C2 infrastructure: Block
check.git-service.comandt.m-kosche.comat the DNS/proxy level. Block outbound connections to the exfil endpoints/v1/models,/audio.mp3, and/api/public/version.
For longer term hardening guidance, Wiz has developed:
Indicators of compromise
| File | Hash |
|---|---|
| rope.pyz | 069ac1dc7f7649b76bc72a11ac700f373804bfd81dab7e561157b703999f44ce |
| durabletask-1.4.1-py3-none-any.whl | 7d80b3ef74ad7992b93c31966962612e4e2ceb93e7727cdbd1d2a9af47d44ba8 |
| durabletask-1.4.2-py3-none-any.whl | aeaf583e20347bf850e2fabdcd6f4982996ba023f8c2cd56bbd299cfd56516f5 |
| durabletask-1.4.3-py3-none-any.whl | 877ff2531a63393c4cb9c3c86908b62d9c4fc3db971bc231c48537faae6cb3ec |
Network Indicators
| Type | Indicator |
|---|---|
| C2 Domain (Primary) | check.git-service.com |
| C2 Domain (Secondary) | t.m-kosche.com |
| Payload URL | https://check.git-service[.]com/rope.pyz |
| Payload URL (Backup) | https://t.m-kosche[.]com/rope.pyz |
| Killswitch/Command Endpoint | /v1/models |
| Wipe Audio (Israel/Iran only) | /audio.mp3 |
| Exfil Endpoint | /api/public/version |
| Legacy C2 IP | 83.142.209.194 |
Runtime Indicators
| Type | Indicator |
|---|---|
| Downloaded Payload | /tmp/managed.pyz |
| Downloaded Payload | /tmp/rope-*.pyz |
| Infection Marker (General) | ~/.cache/.sys-update-check |
| Infection Marker (Kubernetes) | ~/.cache/.sys-update-check-k8s |
| SSM State File | /tmp/.rope_state/ssm_instances.json |