
On 22 March 2026, Aikido Security published analysis of a new payload from TeamPCP, the group behind the Trivy and CanisterWorm supply-chain attacks. Inside a Kubernetes cluster it deploys a privileged DaemonSet to every node. If the node's timezone or locale points to Iran, it wipes the host and reboots it. Elsewhere it installs a persistent backdoor. Any team whose CI runners or workloads ran the poisoned Trivy, npm or PyPI packages should check its clusters for this.
What happened
Aikido tied the payload to the CanisterWorm campaign through shared infrastructure. Both use the same Internet Computer (ICP) canister for command and control (tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0[.]io), the same backdoor code and the same /tmp/pglog drop path. A shell stager hosted on rotating trycloudflare.com tunnels downloads kubectl if it isn't installed, fetches a Python script and runs it.
The script makes two checks. It looks for Kubernetes through the service-account token path /var/run/secrets/kubernetes.io/serviceaccount or the KUBERNETES_SERVICE_HOST variable. It looks for Iran by reading /etc/timezone or timedatectl for Asia/Tehran or Iran, and checking LANG for fa_IR. Then:
- Kubernetes + Iran: deploys DaemonSet
host-provisioner-iraninkube-system, with a container namedkamikazerunningalpine:latest. It deletes the top-level directories of the host filesystem and forces a reboot. - Kubernetes, not Iran: deploys
host-provisioner-std, which writes the CanisterWorm backdoor to each host and installs it as a systemd service. - No Kubernetes, Iran: runs
rm -rf / --no-preserve-root, trying passwordlesssudoif it isn't root. - No Kubernetes, not Iran: exits.
Both DaemonSets run privileged: true with hostPID and hostNetwork. They mount the host's / at /mnt/host and tolerate every taint, so they also land on control-plane nodes. A later variant no longer needs Kubernetes. It scans the local /24 for unauthenticated Docker APIs on TCP 2375 and starts privileged Alpine containers with the host root bind-mounted. It also spreads over SSH using keys from ~/.ssh and usernames parsed from /var/log/auth.log, with StrictHostKeyChecking=no.
Who is affected
- Clusters where a TeamPCP payload ran with Kubernetes credentials. Likely routes include CI runners that used the compromised Trivy release or GitHub Actions on 19–20 March 2026, and machines that installed CanisterWorm-infected npm packages or the poisoned LiteLLM PyPI releases.
- Any pod whose service account can create DaemonSets in
kube-system. In practice that means cluster-admin or something close to it. - Hosts that expose the Docker Engine API without TLS on port 2375, or that hold SSH keys giving access to other hosts.
- Machines configured with Iran's timezone or Persian locale face destruction. All others face persistent backdoors.
What it means for developers and engineering teams
None of this uses a Kubernetes vulnerability. It relies entirely on legitimate API calls made with credentials that were too broad. A DaemonSet is the most efficient way to run code on every node, and a privileged pod with hostPath: / is effectively root on the host. If a CI job, an operator or a developer kubeconfig can create one, then stealing that credential compromises the whole cluster.
Three common habits make this possible: CI pipelines that deploy with cluster-admin kubeconfigs, workloads that run with the default service account plus a generous RoleBinding, and no admission policy blocking privileged pods outside a small set of infrastructure namespaces. Kubernetes' built-in Pod Security Admission at the restricted or baseline level rejects privileged containers and hostPath mounts. The payload's kube-system target is a reminder to treat that namespace carefully too, for example with a policy engine rule that allows only known DaemonSets.
The locale-based targeting is also a warning about trusting geography signals. Changing a timezone is trivial, so it doesn't protect anyone. It shows the actor is willing to destroy systems, not just steal credentials.
What to do now
- Look for the DaemonSets:
kubectl get ds -A | grep -E 'host-provisioner|kamikaze', and review every DaemonSet inkube-systemthat you don't recognize. - Find privileged hostPath workloads:
kubectl get pods -A -o json | jq -r '.items[] | select(any(.spec.volumes[]?; .hostPath.path=="/")) | .metadata.namespace+"/"+.metadata.name' - Check nodes for
/tmp/pglog,/tmp/.pg_state,/var/lib/pgmon/,/var/lib/svc_internal/runner.py, and systemd unitspgmonitororinternal-monitor. - Block egress to
*.icp0.ioand*.trycloudflare.comfrom nodes and runners unless you have a reason to allow it, and alert on any hits. - Enforce Pod Security Admission: label application namespaces
pod-security.kubernetes.io/enforce=restricted, and gatekube-systemwith an admission policy (Kyverno, Gatekeeper or ValidatingAdmissionPolicy). - Scope CI credentials: deploy jobs get namespace-scoped Roles, never cluster-admin. Prefer short-lived OIDC-federated tokens.
- Close Docker port 2375 everywhere, or require TLS on 2376. Alert on outbound SSH using
StrictHostKeyChecking=no. - If you find the backdoor, rebuild the affected nodes and rotate every credential reachable from the cluster.
Related on TechLogHub
Sources
- Aikido Security: CanisterWorm Gets Teeth: TeamPCP's Kubernetes Wiper Targets Iran
- BleepingComputer: TeamPCP deploys Iran-targeted wiper in Kubernetes attacks
- Cloud Security Alliance: TeamPCP Trivy supply chain attack and Kubernetes wiper research note
- OpenSourceMalware: TeamPCP supply chain campaign, a March 2026 retrospective


