Pull Requests do not execute the pipelines modifications that they include
ID |
pipeline_reviewed_before_execution |
Severity |
critical |
Family |
CI/ CD Security |
Tags |
cicd-sec-04, cicd-security, infrastructure, reachable, security |
Description
Poisoned Pipeline Execution (PPE) risks refer to the ability of an attacker with access to source control systems - and without access to the build environment, to manipulate the build process by injecting malicious code/commands into the build pipeline configuration, essentially ‘poisoning’ the pipeline and running malicious code as part of the build process.
There are three types of PPE:
-
Direct PPE (D-PPE): The attacker modifies the CI config 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 PR with the change from a branch or a fork
-
Indirect PPE (I-PPE): The attacker cannot modify the pipeline itself, but he/ she may gain control of a script which is executed in the pipeline.
-
Public-PPE (3PE): The CI pipeline of a public repository runs unreviewed code suggested by anonymous users.
To prevent Direct PPE
, each CI configuration file should be reviewed before the pipeline is executed to prevent tampering with the CI configuration file that could lead to malicious code execution in the pipeline.
Security
In a successful PPE attack, attackers execute malicious unreviewed code in the CI. This provides the attacker with the same abilities and level of access as the build job, including:
-
Access to any secret available to the CI job, such as secrets.
-
Access to external assets the job node has permissions to, such as files stored in the node’s file system, or credentials to a cloud environment accessible through the underlying host.
-
Ability to ship code and artifacts further down the pipeline, in the guise of legitimate code built by the build process.
-
Ability to access additional hosts and assets in the network/environment of the job node.
Mitigation / Fix
Ensure that Pull Requests do not execute the pipelines modifications that they include.
GitHub
For GitHub, using the pull_request_target
trigger makes the difference, since it behaves in an almost identical way to the pull_request event with the same set of filters and payload. However, instead of running against the workflow and code from the merge commit, the event runs against the workflow and code from the base of the pull request.
CircleCI
CircleCI by default will not build a pull request (PR) from a forked repository on your project. However, this behaviour can be enabled by visiting Project Settings>Advanced
on your project and setting the Build forked pull requests
option to On.
Enabling this setting along with Pass secrets to builds from forked pull requests
may result insecure against malicious pull requests. Look at this interesting article (Shaking secrets out of CircleCI builds - insecure configuration and the threat of malicious pull requests) to know more about the consequences of using these configurations.
Jenkins
For Jenkins, when using a MultiBranch job that takes the JenkinsFile from the PR branch, then anyone with read access to a repository can fork it, commit some changes to their fork and then create a pull request against the original repository with their changes, leading to a PPE.
In order to protect against a malicious pull request itself modifying the JenkinsFile to remove the protections (PPE), you can do several things:
-
Define the JenkinsFile to be used in a pull request as the JenkinsFile placed in the protected branch (using Remote JenkinsFile Provider plugin).
-
Define the trust policy for pull requests from forks (using for example GitHub Branch Source plugin).
This plugin allows setting a trust level for the pull requests discovered from forks, where the recommended levels are
From users with Admin or Write permission
andNobody
.