sysmon.py is a Python persistence implant/backdoor associated with TeamPCP supply-chain compromises, including the March 2026 Trivy, Checkmarx KICS/OpenVSX, and related LiteLLM activity. It is used as a follow-on stage after initial credential-stealing payloads. On Linux hosts, the malware is dropped to paths including ~/.config/systemd/user/sysmon.py, ~/.config/sysmon/sysmon.py, /root/.config/sysmon/sysmon.py, and /host/root/.config/sysmon/sysmon.py, then registered as a systemd user service for automatic restart and persistence. In the Trivy binary compromise, the implant was written when the GITHUB_ACTIONS environment variable was absent, indicating non-CI execution. The implant polls attacker-controlled infrastructure for a payload URL, downloads a follow-on binary to /tmp/pglog, marks it executable, and launches it as a detached process. Reported C2/dead-drop endpoints include tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0[.]io, https://checkmarx.zone/raw, and checkmarkr.zone/raw; some variants ignore URLs containing youtube.com as a kill-switch or dormant state. In Kubernetes-capable environments, related payloads use sysmon.py for persistence beyond the initial container by deploying privileged pods in kube-system, escaping to the host, writing the implant onto cluster nodes, and creating systemd services there. The malware is therefore associated with Linux and Kubernetes persistence, staged payload delivery, and post-compromise backdoor access following broader secret-harvesting operations.
Mallory pivots from this family to the IOCs, detections, and named campaigns that touch your stack, and pages you when something new lands.
1 distinct threat actor attributed by public researchers. Open in Mallory to see the full evidence chain and overlapping campaigns.
The binary checks the GITHUB_ACTIONS environment variable. If absent (meaning it's running on a developer machine or standalone server, not in CI), it writes this Python script to ~/.config/systemd/user/sysmon.py and creates a systemd unit for persistence.
29 distinct techniques documented for this family, organized by ATT&CK tactic.
CrowdStrike’s Engineering team traced the activity to a compromised GitHub Action named aquasecurity/trivy-action... 76 of the scanner’s 77 release tags had been retroactively poisoned via git tag repointing, replacing the legitimate entry point with a multi-stage credential stealer.
it writes this Python script to ~/.config/systemd/user/sysmon.py and creates a systemd unit for persistence.
it writes this Python script to ~/.config/systemd/user/sysmon.py and creates a systemd unit for persistence.
it writes this Python script to ~/.config/systemd/user/sysmon.py
it writes this Python script to ~/.config/systemd/user/sysmon.py and creates a systemd unit for persistence.
it writes this Python script to ~/.config/systemd/user/sysmon.py and creates a systemd unit for persistence.
it writes this Python script to ~/.config/systemd/user/sysmon.py and creates a systemd unit for persistence.
it writes this Python script to ~/.config/systemd/user/sysmon.py and creates a systemd unit for persistence.
a Base64-encoded Python script is decoded and executed... Collected data is encrypted with a randomly generated AES-256-CBC session key, and the session key is wrapped with a hardcoded 4096-bit RSA public key.
The primary exfiltration channel is an HTTPS POST to scan.aquasecurtiy[.]org — a typosquatted domain mimicking Aqua Security's legitimate aquasecurity.com... The choice of filename mimics PostgreSQL logging
If absent (meaning it's running on a developer machine or standalone server, not in CI), it writes this Python script to ~/.config/systemd/user/sysmon.py and creates a systemd unit for persistence.
After an initial five-minute sleep (likely intended to outlast sandbox analysis timeouts), it enters a polling loop that contacts a command-and-control (C2) server approximately every 50 minutes.
The runner executes whatever code the action contains, with full access to the runner's environment, secrets, and network... looking for GitHub Actions secrets — specifically JSON structures matching "name":{"value":"...","isSecret":true}.
On self-hosted runners, a different Base64-encoded Python script performs broad filesystem credential harvesting. It targets the following: SSH private keys and configs, Cloud credentials, Kubernetes configs and service account tokens, Docker registry credentials, Database credentials... .env files and API keys
On self-hosted runners, a different Base64-encoded Python script performs broad filesystem credential harvesting. It targets the following: SSH private keys and configs Cloud credentials... Kubernetes configs... Docker registry credentials... .env files and API keys... System files (/etc/passwd, /etc/shadow, shell histories)
If the script is run on a Kubernetes container that has sufficient permissions, it will attempt to deploy a highly privileged pod... The script checks to see if the file: /var/run/secrets/kubernetes.io/serviceaccount/token exists and if it does, it uses that token to query /api/v1/nodes.
the Python filesystem harvester ... reads SSH keys, cloud credentials, Kubernetes configs, Docker credentials, .env files, terraform state, shell history, database configs, TLS private keys, and cryptocurrency wallets, walking multiple directories up to 6 levels deep
The persistence script polls https://checkmarx.zone/raw every 50 minutes for additional payloads... while True ... time.sleep(3000)
The binary also exhibits the same credential-stealing behavior as the compromised action. It scans for credentials, bundles them, encrypts them, and then exfiltrates the data via a post request.
The primary exfiltration channel is an HTTPS POST to scan.aquasecurtiy[.]org... If the endpoint returns a non-2XX response and INPUT_GITHUB_PAT is available, the script falls back to exfiltrating through GitHub itself. It will create a public repository named tpcp-docs under the victim's GitHub account, create a timestamped release, and upload the encrypted bundle as a release asset.
21 indicators attributed across vendor reports, sandbox runs, and researcher write-ups. Full values are available in Mallory.
IPs, domains, and DNS infrastructure linked to this family.
File hashes (MD5, SHA-1, SHA-256) from samples and reports.
Other indicator types observed in public reporting.
4 sources tracked across advisories, community write-ups, and news. New activity surfaces here as Mallory finds it.
A Linux persistence backdoor dropped by the malicious LiteLLM package payload. It is installed as a disguised system telemetry service, beacons to checkmarkr.zone/raw for commands, and provides persistent access on Linux hosts and Kubernetes nodes.
A persistent C2 backdoor dropped by the malicious litellm package. It is installed as a systemd user service, polls https://checkmarx.zone/raw roughly every 50 minutes, downloads arbitrary binaries, makes them executable, and runs them detached. In Kubernetes environments, it is also written onto host nodes for persistence and lateral compromise.
A Python backdoor dropped by the malicious setup.sh script. It persists on Linux hosts or Kubernetes nodes, polls attacker-controlled infrastructure for additional payload URLs, downloads and executes follow-on payloads, and is used to maintain access after the initial CI/container execution ends.
A Python backdoor payload referenced as highly similar or identical to the CanisterWorm second-stage implant, using an ICP canister dead-drop to retrieve a URL for a downloadable binary payload.
Match every observed IP, domain, and hash against your live telemetry.
Named campaigns wielding this family, with evidence pinned to each claim.
CVEs this family uses for access and lateral movement.
YARA, Sigma, Snort, and vendor rules, auto-deployed to your SIEM.
Every documented technique, ranked by evidence weight.
Reddit, Mastodon, and CTI community discussion around this family.