Poisoned pipeline execution (PPE), an OWASP CI/CD Security Risk, is an attack vector that abuses access permissions to a source code management (SCM) system with the intent of causing a CI pipeline to execute malicious commands. While PPE attackers don’t have access to the build environment, they have gained access to the SCM, which enables them to inject malicious code into the build pipeline configuration to manipulate the build process.
Poisoned pipeline execution (PPE), listed as CICD-SEC-4 on the OWASP Top 10 CI/CD Security Risks, represents a sophisticated attack strategy targeting continuous integration and continuous deployment (CI/CD) systems.
Customers may have a variety of options available to them:
In the PPE strategy, attackers execute malicious code within the CI portion of the CI/CD pipeline, bypassing the need for direct access to the CI/CD system. The method involves manipulating permissions against a source code management (SCM) repository. By altering CI configuration files or other files on which the CI pipeline job depends, attackers inject malicious commands, effectively poisoning the CI pipeline and enabling unauthorized code execution.
Successful PPE attacks can enable a broad range of operations, all executed within the context of the pipeline's identity. Malicious operations can include accessing secrets available to the CI job, gaining access to external assets the job node has permissions to, shipping seemingly legitimate code and artifacts down the pipeline, and accessing additional hosts and assets in the job node's network or environment.
Given its critical impact, low detectability, and the existence of multiple exploitation techniques, the PPE attack poses a widespread threat. For security teams, engineers, and red teamers, understanding PPE and its countermeasures is critical to CI/CD security
In the context of continuous integration (CI), pipeline execution flow refers to the sequence of operations defined by the CI configuration file hosted in the repository the pipeline builds. This file outlines the order of executed jobs, as well as detailing build environment settings and conditions that affect the flow. When triggered, the pipeline job pulls the code from the chosen source (e.g., commit/branch) and executes the commands specified in the CI configuration file against that code.
Commands within the pipeline are invoked either directly by the CI configuration file or indirectly by a script, code test, or linter residing in a separate file referenced from the CI configuration file. CI configuration files typically have consistent names and formats, such as Jenkinsfile (Jenkins), .gitlab-ci.yml (GitLab), .circleci/config.yml (CircleCI), and the GitHub Actions YAML files located under .github/workflows.
Attackers can exploit the ability to manipulate commands executed by the pipeline, either directly or indirectly, can be exploited by attackers to execute malicious code in the CI.
For a PPE attack to be successful, several criteria must be met:
Pipelines that execute unreviewed code, such as those triggered off pull requests or commits to arbitrary repository branches, are more susceptible to PPE. Once an attacker can execute malicious code within the CI pipeline, they can conduct malicious operations within the context of the pipeline's identity.
Poisoned pipeline execution manifests in three distinct forms: direct PPE (D-PPE), indirect PPE (I-PPE), and public PPE (3PE).
Direct PPE
In a direct PPE scenario, attackers modify the CI configuration file in a repository they have access to, either by pushing the change directly to an unprotected remote branch on the repo or by submitting a pull request with the change from a branch or fork. The pipeline execution is triggered by the push or pull request events, as defined by commands in the modified CI configuration file, which results in the execution of the malicious commands in the build node once the build pipeline is triggered.
The D-PPE attack example illustrated in figure 1 transpires in the following succession of steps:
Indirect PPE
Indirect PPE occurs when the possibility of D-PPE isn’t available to an adversary with access to an SCM repository:
In these scenarios, the attacker can still poison the pipeline by injecting malicious code into files referenced by the pipeline configuration file, such as scripts referenced from within the pipeline configuration file, code tests, or automatic tools like linters and security scanners used in the CI. For example:
Rather than poisoning the pipeline via direct PPE, the attacker launching an indirect PPE attack injects malicious code into files referenced by the configuration file. The malicious code is ultimately executed on the pipeline node and runs the commands declared in the files.
In this I-PPE attack example, the chain of events unfolds as follows:
Public PPE
Public PPE is a type of PPE attack executed by anonymous attackers on the internet. Public repositories often allow any user to contribute, usually by creating pull requests. If the CI pipeline of a public repository runs unreviewed code suggested by anonymous users, it’s susceptible to a public PPE attack. The public PPE can also expose internal assets, such as secrets of private projects, in cases where the pipeline of the vulnerable public repository runs on the same CI instance as private ones.
Executing malicious unreviewed code in the CI via a successful PPE attack provides attackers with the same level of access and ability as the build job:
But organizations can safeguard their software products and infrastructure with a secure pipeline execution that ensures all code compiled, tested, and deployed is legitimate and untampered.
The implications of PPE can be severe, ranging from unauthorized data access, compromised software integrity, system disruptions, to data breaches or even a total system takeover. These risks pose significant threats to both the business and its clients, underscoring the gravity of PPE.
In the eight-step supply chain compromise operation seen in figure 3, the attacker gains access to the CI pipeline and poisons components of the SaaS application. Via the poisoned component, the attacker builds backdoor functionality into the application and sends the poisoned plugins to downstream clients. Since the downstream organizations likely perceive the poisoned package as legitimate, they build it into their cloud or on-premises infrastructure.
From one poisoned CI pipeline, the attacker achieves exponential collateral damage, having created backdoor access to countless organizations. This was the case with the SolarWinds attack.
READ MORE: Anatomy of a CI/CD Pipeline Attack
Preventing and mitigating the PPE attack vector involves multiple measures spanning across both SCM and CI systems: