Let audience know slides are available. No rush to photograph.
Pac-Man board intro — walk through the emoji power pellets!
Compromise once, deploy everywhere. The multiplier effect is what makes SSC attacks devastating.
🐴 XZ Utils (2024)
What Happened:
A contributor "Jia Tan" spent 2+ years gaining maintainer trust on the xz compression library, then injected a sophisticated backdoor targeting OpenSSH authentication. The backdoor would have compromised virtually every Linux server. Caught by Andres Freund who noticed a 500ms SSH login slowdown.
Key Lesson:
Trust is the ultimate attack vector. Social engineering over years can bypass every technical control. Open source maintainer burnout creates opportunities for patient attackers. Verify contributors, not just code.
⚡ tj-actions/changed-files (2025)
What Happened:
Compromised maintainer bot token, manipulated git tags to point to malicious commits. 23,000+ repos using @latest tags automatically pulled and executed malicious code that stole their CI secrets.
Key Lesson:
Never use floating tags (@latest, @main, @v1) in production. Always pin GitHub Actions to specific commit SHAs. Floating tags = uncontrolled automatic updates = potential automatic compromise.
🎭 S1ngularity - Nx (2025)
What Happened:
Exploited pull_request_target injection to steal npm token, published malicious Nx packages, which were then auto-installed by AI coding tools (Cursor, Cline), exfiltrating secrets from thousands of developer machines and CI environments.
Key Lesson:
pull_request_target gives untrusted code write access - extremely dangerous. AI tools amplify attack scale by auto-installing packages. One vulnerable workflow + package registry access = mass compromise at machine speed.
🪱 Shai-Hulud 2.0 (2025)
What Happened:
Massive npm supply-chain campaign compromising 700+ packages from major projects (Zapier, ENS Domains, PostHog, Postman). Preinstall malware executed during npm install, exfiltrating secrets and tokens across dev machines and CI/CD pipelines. Attack spread rapidly at ~1,000 new repos every 30 minutes, affecting 25,000+ GitHub repositories across ~500 users. Stolen data was cross-published, meaning victim credentials appeared in unaffiliated GitHub accounts.
Key Lesson:
Preinstall hooks can execute before your code even runs - widening attack surface to both dev and CI/CD environments. The scale and automation of modern supply chain attacks is unprecedented. With 27% of cloud environments potentially exposed, even popular, trusted packages can be weaponized. Immediate investigation required for npm-based environments.
Meta-lesson across all four:
Supply chain attacks exploit trust and automation. The things that make development fast (auto-updates, trusted dependencies, CI/CD automation) become attack vectors when compromised. Defense requires: pin versions, minimize trust, verify everything, and monitor the entire dependency chain.
Walk through all 4 briefly. Don't deep-dive — you revisit tj-actions, S1ngularity, and Shai-Hulud later. Transition: "These attacks all targeted CI/CD."
Quick level-set. Most audience knows Actions — just framing.
Jobs = isolation boundary. Steps share runner environment — that matters for security.
Walk through 4 elements. Highlight: checkout@v5 is a trust decision — a third-party action running in your context.
LEGO bricks: building blocks. Key point: actions run IN your job context — they get your secrets, tokens, and runner.
Quick scan of the grid. Actions touch everything. Transition: "So what happens when they get compromised?"
==================== PART 1: WEAPONIZE ====================
Transition slide — pause briefly, advance.
Three reasons: source code access, package publishing, secrets. Pipelines are the nexus of all three.
Ask: "What's wrong here?" Uses @v45 — a mutable tag.
Even pinned to @v45.0.7 it's still a tag, not a SHA. Tags can be moved — exactly what happened in the tj-actions attack.
Compromised bot PAT → tag overwritten → 23k repos auto-pulled malicious code. If you control the tag, you control downstream.
Solution: pin to SHA, enable Dependabot for automated updates, verify integrity. Key takeaway for audience.
Trust framework: tag vs SHA vs fork. SHA pinning is the default. Use OSSF Scorecard for third-party assessment.
Ask the audience: spot the vulnerability. Answer: user input in run command → script injection.
Four attack vectors from one injection: reverse shell, secret theft, code manipulation, infra compromise. Powerful demo moment.
Script injection was the entry point for Shai-Hulud. Secrets stolen, code compromised, infra breached.
All event fields shown are attacker-controlled. Never use directly in run commands.
Fix: assign to env var first, then reference as shell variable. Prevents expression injection.
Same workflow, now safe. The DISCUSSION_BODY env var is set at shell level — no expression expansion in the run line.
Ask: what happens on pull_request_target? Secrets exposed + untrusted code checkout + injection. Triple threat.
Spot three issues: pull_request_target + checkout of PR head + secrets exposed + injection in run. This is a triple threat.
1000s of secrets, 480 users' private repos, 500 repos from one company. Massive real impact.
pull_request_target runs in trusted context with secrets. Checking out PR head = running untrusted code with full access. GitHub's Dec 2025 fix helps but doesn't eliminate risk.
Trivy: most severe outcome of hackerbot-claw campaign. A security scanner used to protect CI/CD was itself compromised via CI/CD. The irony is powerful. Stars dropped from 25k to ~25 when repo was recreated. Aqua Security restored within 24h. Key lesson: even security tools can be compromised — pin to SHA, monitor egress, least privilege.
Caches persist across runs. Attacker poisons cache on a PR, subsequent builds on main pick it up. Never trust cache in release pipelines.
Default GITHUB_TOKEN has write-all permissions. If compromised, attacker gets full repo access. Always set permissions explicitly.
Set permissions: {} at workflow level. Add specific permissions per job. Failed is better than compromised.
Long-lived AWS keys in secrets: if leaked, attacker has permanent access. These never expire unless rotated.
OIDC: short-lived tokens, no stored secrets, cloud provider validates claims. Much safer than long-lived keys.
Five best practices: environments for gating, env-level secrets, restrict OIDC claims, rotate regularly, audit access. Tight claim conditions are critical.
Walk through the diagram: script injection → secrets stolen → action takeover → downstream compromise. Everything we discussed in one kill chain.
Real numbers: 218 orgs, 23k repos, 33k secrets. This is the scale of a single supply chain attack.
Transition slide — pause. "Now that we've seen the attacks, let's harden."
Quick recap of the four attack patterns we covered. Each has a clear mitigation.
GitHub org settings: allow-list actions, enforce SHA pinning, drop default token to read-only. Quick wins.
Cline CLI attack: issue title triggered malicious build via AI agent. 4k downloads in 8 hours. AI agents are privileged actors.
Hours vs seconds. s1ngularity used AI tools for mass secret hunting. Scale changes everything.
Skills are the new code. Agents follow them literally without verifying packages exist. react-codeshift: hallucinated by an LLM, spread through skill files to 237 repos, real download attempts observed.
Same four patterns apply to AI: pin deps, minimal creds, validate input, enforce boundaries. AI just moves faster.
Slides and resources at the QR code. Repo link on screen.
Open the floor. Prepared topics: OIDC details, self-hosted runner hardening, AI agent governance.
Thank the audience. Remind them to scan the QR code.