CICD-Pipelines-Vulnerabilities

A Deep Dive fi CI/CD Pipelines Vulnerabilities (IV) : Protecting against Artifact Poisoning through Software Attestations

In our previous post about CI/CD Pipelines, ahna rajna how to hack a CI/CD scenario that preżumibbilment kien protett.

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

  • L-1 pipeline (Build CI), safe to D-PPE and I-PPE, would checkout the PR code, make the build, and generate an artifact
  • It-2 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, użajna workflow_run grillu. 

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 artifatt.

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 fil-kariga).

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. L-ewwel, 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. It-tieni nett, how can we assess the provenance of an artifact?

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

Software Attestations

An attestazzjoni hija biċċa ta ' data li jirrappreżenta proof of an event. In the real world, we generally call these ċertifikazzjonijiet.

For example, when a lab tests your blood, data about the test is recorded and certified. The blood test results are verifikabbli u, traċċabbli.

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

F'livell għoli, a firma 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

Firem don’t prove 

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

bil Attestazzjonijiet, rather than signing an artifact directly, users create some kind of dokument Li captures their intent behind signing the artifact and any talbiet speċifiċi being made as part of this signature.

In-toto Attestation Framework

The most common framework is the in-toto Attestation Framework

  • jiddefinixxi 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 Attestazzjoni hija digitally signed document li fih Dikjarazzjonijiet.

il Dikjarazzjoni is the middle layer of the attestation, binding it to a particular suġġett and unambiguously identifying the types of the Predikat:

  • suġġett: L- cryptographically secure reference to the artifact (usually via a hash), and
  • Predicates: a set of specific pretensjonijiet 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 Attestazzjoni

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 ġewwa. 

Bob can then use those claims biex tiddeċiedi 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 kif ukoll il- 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
  • eċċ.

 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.

Nistgħu assess the integrity of an artifact b'żewġ modi. 

One is by assessing the provenjenza 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 melħ command stores the attestation in a ktieb (a.k.a an attestation registry, rekord 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:

  • il artifact sha256sum is valid (i.e. there is an attestation about that “subject” ), and
  • il 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.

Per eżempju, 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[]) 

Konklużjonijiet

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 ‘Avvelenat 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. 

Avvelenat Pipeline Execution (PPE)

A Deep Dive fi CI/CD Pipelines Vulnerabilities (I)​

Indirect Poisoned Pipeline Execution (I-PPE)

A Deep Dive fi CI/CD Pipelines Vulnerabilities (II)​

Artifact Poisoning and Code Injection​

A Deep Dive fi CI/CD Pipelines Vulnerabilities (III)​
sca-tools-software-composition-analysis-tools
Prijoritizza, irranġa, u assigura r-riskji tas-softwer tiegħek
Ikseb il-Kont B'Xejn tiegħek.
Mhix meħtieġa karta ta 'kreditu.

Assigura l-Iżvilupp u l-Kunsinna tas-Softwer tiegħek

bis-Suite tal-Prodotti Xygeni