CICD-Pipelines-Vulnerabilities

Un mergullo profundo CI/CD Pipelines Vulnerabilities (IV) : Protecting against Artifact Poisoning through Software Attestations

In our previous post about CI/CD Pipelines, nós vimos how to hack a CI/CD scenario that presuntamente was protected.

Let’s remember our point in the previous post: we started with some pipeline that was vulnerable to Indirect Poisoned Pipeline Execución (I-PPE) and, to fix it, we decided to split the pipeline into two:

  • O 1o pipeline (Build CI), safe to D-PPE and I-PPE, would checkout the PR code, make the build, and generate an artifact
  • O 2nd pipeline (Test CI), also safe to D-PPE and I-PPE would checkout the Base code (to avoid shell script modification) and execute the original scripts against the artifact. 
  • To synchronize the Test CI pipeline to run AFTER the Build CI pipeline, usamos workflow_run gatillo. 

We named this as Scenario #3.

CICD-Security

Although, as we mentioned in that post, there are other solutions, we decided to implement this “solution” for pedagogical reasons, so we can deep dive into vulnerabilities of CI/CD pipelines.

Afterward, we saw how to hack this scenario by poisoning the artifact. This is what we call Artifact Poisoning, i.e. the ability to modify (hack) the pipeline logic by modifying a pipeline artefacto.

What is the problem with this approach? We saw that the problem arises when any user “creates” a new pipeline. 

If a user opens a PR containing a new pipeline, GitHub will execute that pipeline  (given some conditions, as we saw na publicación).

The user is then able to create a new pipeline with the same name as Build CI !! Yes, it is surprising, but GitHub allows you to create two pipelines with the same name!!

When the user opens a PR with these changes, the “new” pipeline will be executed (uploading a poisoned artifact) and the Deploy CI pipeline will be executed after that, resulting in the “modified” shell script overwriting the “original” shell script located in the pipeline workspace. So, this “solution” does not avoid the I-PPE vulnerability (as we can see below)

CICD-Pipelines

What are the problems? At least, there are a couple of issues:

  1. En primeiro lugar, how to be sure that the build process has not been tampered with? In this scenario, the malicious user has been able to modify the intended build process by using their pipeline to create a poisoned artifact.
  2. En segundo lugar, how can we assess the provenance of an artifact?

These questions throw us into the arms of the Software Attestations domain!!

Software Attestations

An certificación is a piece of datos representando proof of an event. In the real world, we generally call these certificacións.

For example, when a lab tests your blood, data about the test is recorded and certified. The blood test results are verificable rastrexable.

Software_Supply_Chain_Attestations

Closer to our IT domain, you could guess what would be a translation of this process to, for example, a compilation process.

Software_Attestations

Information about the compilation server environment and tools, the materials (the source code), and the products/artifacts (the binary code) would be part of such attestation.

Obviously, the attestation must be generated by an authorized attestor (authenticated and non-repudiable) to provide credibility. 

Likely, some of you might be thinking .. and what is the difference between signatures and attestations?

Code Signatures and Attestations

A un alto nivel, unha sinatura is created using a key pair and an artifact. The key pair consists of a public key and a private key. 

Code-Signatures

The user signs an artifact using the private key, and others can then verify the signature using the public key. The private key must be kept secret, but the public key is distributed widely.

Signatures can be used to prove that the holder of the private key used the private key to sign the artifact

Sinaturas don’t prove 

  • The user’s intención to sign the artifact (they might have been tricked), or 
  • The user’s intention to make any specific claim about the artifact 

con Atestados, rather than signing an artifact directly, users create some kind of documento Que captures their intent behind signing the artifact and any specific claims being made as part of this signature.

In-toto Attestation Framework

The most common framework is the in-toto Attestation Framework

  • define un standard format for attestations that bind subjects, the artifacts being described, to authenticated metadata about the artifact 
  • provides a set of pre-defined predicates for communicating authenticated metadata throughout and across software supply chains

Let’s go into some detail about the attestation format.

An Atestado é un digitally signed document que contén Declaracións.

o Afirmación is the middle layer of the attestation, binding it to a particular tema and unambiguously identifying the types of the Predicado:

  • tema: A cryptographically secure reference to the artifact (usually via a hash), and
  • Predicates: a set of specific reivindicacións about that artifact is referred to as a Statement. These claims can be used to express (and later prove) anything you can think of! They can represent manual approval, artifact provenance, automated test results, an audit trail, or more! 

When this Statement is cryptographically signed, it is then referred to as an Atestado

CI/CD_Security_Scenario_3

In this way, as an example, Alice creates a Statement about an artifact and signs it using her private key, creating an Attestation.

  • Bob can then verify the signature in that Attestation, allowing him to trust the claims dentro. 

Bob can then use those claims decidir whether or not to allow this artifact to be used.

Software_Attestations_Gr

Could Attestations help to solve Artifact Poisoning?

After this intro to Attestations, let’s go back to our problem. How can attestations help us to solve our problem, i.e. to avoid artifact poisoning?

The malicious user was able to create an artifact bypassing the “official” mechanism, i.e. by using his/her pipeline to generate the artifact. 

It would be amazing if we could prove that the artifacts being downloaded have been built with the official pipelines. This is only one example of what we could call “Tampering points”, but there might be many others.

SSCS_Tampering_Points

As you can see in the above picture, the tampering points are multiple. In this way, the consumer pipeline (Test CI in our example) must assess the integrity of the build process así como o integrity of the artifact itself.

In our example, the poisoned artifact has been created by introducing a new (poisoned) pipeline that tampers with the build process. But the malicious user could have been able to:

  • modify the code after being checked out from the SCM to generate a malicious binary
  • substitute the right binary produced by the compilation with any other malicious binary
  • compromise the Artifact Registry and upload a poisoned artifact built in any other way
  • etc.

 As you can see, there may be multiple “tampering” points.

What is important here? Obviously to protect all those “tampering” points. But, in the end, what is most important is that the “consumer” of the artifact can assess the integrity of the artifact, and decide whether to go ahead or not with it.

Podemos assess the integrity of an artifact de dous xeitos. 

One is by assessing the orixe of the artifact.

By generating a Provenance Attestation, we provide useful metadata (properly authenticated and non-repudiated) about the artifact. In the following examples, we will use Xygeni SALT (Software Attestations Layer for Trust), the component for generating, registering, and verifying software attestations.

Tamper_Proof_Builds
      - name: Building ...         run: |           # mvn will compile and create target/MyApp.war           mvn clean package                     - name: Generating provenance         run: |               #!/usr/bin/env bash                 shopt -s expand_aliases               alias salt=$PWD/salt_pro/xygeni_salt/salt                     echo " "               echo "-----------"               echo "Generating Provenance with CLI ..."               salt at slsa \                   --basedir ${GITHUB_WORKSPACE}/target \             --key="${PRIVATE_KEY}" \             --public-key=${GITHUB_WORKSPACE}/Test1_public.pem \               --key-password=${KEY_PASSWD} \               --output-unsigned=${GITHUB_WORKSPACE}/cli_provenance_${PIPELINE}_unsigned.json \                   --pipeline ${PIPELINE} --pretty-print \                   --file ./MyApp.war                     

In the above code, you can see there is a step that builds the war file and a second step that generates the Provenance Attestation. To do it, the pipeline uses the private key and also includes the public key in the attestation. 

Behind the scenes, Xygeni’s sal command stores the attestation in a libro de libros (a.k.a an attestation registry, rexistro in our case but you can use any other). Done that, the consumer pipeline can include Xygeni’s Verification Engine to verify the provenance of the artifact and assess the integrity of the artifact.

 - name: 'Verifying the attestation'         run: |           #!/usr/bin/bash   	    echo " "           echo "-------"           # Calculate sha256sum for the artifact           SHA_SUM=$(sha256sum ./MyApp.war | cut -f1 -d ' ')   	    # Recover the attestation Id from the sha256sum           ATT_ID=$(echo $(salt -q registry search --digest sha256:$SHA_SUM --format json) | jq -r .[-1].gitoidSha256)             echo " "           echo "-------"           # Download the provenance attestation           echo "Downloading the provenance attestation ..."           salt -q reg get --id=$ATT_ID --format=json > ${GITHUB_WORKSPACE}/provenance_kk.signed.json             echo " "           echo "-------"           echo "Verifying provenance ..."           salt verify \               --basedir ${GITHUB_WORKSPACE} \               --attestation=${GITHUB_WORKSPACE}/provenance_kk.signed.json \               --public-key=${GITHUB_WORKSPACE}/Test1_public.pem \               --file ./MyApp.war 

The verifying process assesses:

  • o artifact sha256sum is valid (i.e. there is an attestation about that “subject” ), and
  • o attestation is properly authenticated (it has been generated by using the adequate private key) 

This verification process can assess then that both the artifact and the attestation are valid.

But, as you remember, in our case, the artifact was generated by a “malicious” pipeline (i.e. not the original by a modified pipeline). Then we must go ahead and check another aspect: that the artifact has been generated by the “original” pipeline, not any other one. 

To do that, just include a simple line to check for that condition, for example:

   echo " "           echo "-------"           # Download the provenance attestation           echo "Downloading the provenance attestation ..."           salt -q reg get --id=$ATT_ID --format=json > ${GITHUB_WORKSPACE}/provenance_kk.signed.json           WFR=$(jq -r .payload ${GITHUB_WORKSPACE}/provenance_kk.signed.json |base64 -d | jq -r .predicate.buildDefinition.internalParameters.environment.GITHUB_WORKFLOW_REF)           echo $WFR | grep cicd_top10_3_salt\/.github\/workflows\/build.yml 

This additional check will fail if the artifact had not been generated by our “safe” pipeline.

If the artifact was generated by our original pipeline (cicd_top10_3_salt/.github/workflows/build.yml), the grep command will succeed, otherwise, it will fail, breaking the pipeline and aborting any further steps.

The “builder” pipeline is just one tampering point to check but, as mentioned before, there are some other tampering points we should check.

Por exemplo, a what if the source code has been tampered with after repo checkout and before the build command? In this case, the code to be built is not the same as stored in the SCM. 

To check this tampering point is so easy as to check the hashes of the material at every step.

SHA_ATT_MATERIAL=$(jq -r .payload ${GITHUB_WORKSPACE}/provenance_kk.signed.json | base64 -d | jq -r .predicate.attestations[0].predicate.materials[].digest[]) SHA_STEP_MATERIAL=$(jq -r .payload ${GITHUB_WORKSPACE}/provenance_kk.signed.json | base64 -d | jq -r .predicate.attestations[3].predicate.materials[0].digest[]) 

Conclusións

In summary,  a Software Attestation is an assertion made about a piece of software, i.e. an authenticated statement (metadata) about a software artifact or collection of software artifacts.

Software attestations are a generalization of raw artifact/code signing. The attestation is a signed document (in a given format, typically based on JSON) that associates metadata with an artifact. They represent evidence linking inputs (materials) and outputs (artifacts produced) at each build step.

Attestations provide a verifiable record of the steps done for building the final software artifacts, including input materials for each step and the build commands run.

In conclusion, Software Attestations are a great mechanism to check many different integrity aspects of our build process. 

Finished the series? Don’t worry! Feel free to jump back to ‘Envelenado Pipeline Execution (PPE)‘ or any other post that piques your interest again!

Stay tuned, we will deep dive into software attestations and build security in further blog posts. 

Envelenado Pipeline Execution (PPE)

Un mergullo profundo CI/CD Pipelines Vulnerabilities (I)​

Indirect Poisoned Pipeline Execution (I-PPE)

Un mergullo profundo CI/CD Pipelines Vulnerabilities (II)​

Artifact Poisoning and Code Injection​

Un mergullo profundo CI/CD Pipelines Vulnerabilities (III)​
ferramentas-sca-tools-software-ferramentas-de-análise-de-composición
Priorizar, corrixir e protexer os riscos do software
Obtén a túa conta gratuíta.
Non se precisa tarxeta de crédito.

Asegura o desenvolvemento e a entrega do teu software

con Xygeni Product Suite