CICD-Pipeline弱点

深入了解 CI/CD Pipeline漏洞(四):通过软件证明防止工件中毒

在我们之前的帖子中 CI/CD Pipelines, 我们看到 如何破解 CI/CD 情景 想必 受到保护。

让我们回想一下上一篇文章中的观点: 我们从一些开始 pipeline 容易受到 间接中毒 Pipeline 执行 (I-PPE),为了解决这个问题,我们决定拆分 pipeline 分为两部分:

  • 1st pipeline (构建 CI),对 D-PPE 和 I-PPE 来说是安全的,将检出 PR 代码,进行构建,并生成工件
  • 2nd pipeline (测试 CI),对于 D-PPE 和 I-PPE 来说也是安全的,它会检出基础代码(以避免修改 shell 脚本)并针对工件执行原始脚本。 
  • 同步测试 CI pipeline 在 Build CI 之后运行 pipeline, 我们用了 工作流运行 触发。 

我们将其命名为场景#3。

CICD-安全

虽然,正如我们在那篇文章中提到的,还有其他的解决方案,但我们决定出于教学原因实施这个“解决方案”,这样我们就可以深入研究 CI/CD pipelines.

之后,我们了解了如何破解这种情况 毒害神器.这就是我们所说的 神器中毒即修改(破解)的能力 pipeline 通过修改逻辑 pipeline 神器。

这种方法有什么问题?我们看到,当任何用户“创建”新的 pipeline. 

如果用户打开包含新内容的 PR pipeline,GitHub 将执行该 pipeline  (给定一些条件,正如我们所见 在文中).

然后用户可以创建一个新的 pipeline 与 Build CI 同名!是的,这很令人惊讶,但 GitHub 允许您创建两个 pipeline同名!!

当用户打开包含这些更改的 PR 时, 新的” pipeline 将被执行(上传中毒工件) 和部署 CI pipeline 之后将被执行,导致“修改后的”shell 脚本覆盖位于 pipeline 工作区。因此,这个“解决方案”无法避免 I-PPE 漏洞(如下所示)

CICD-Pipelines

有什么问题?至少有几个问题:

  1. 首先, 如何确保构建过程没有被篡改? 在这种情况下,恶意用户能够利用他们的 pipeline 制造一件有毒的神器。
  2. 其次, 我们如何评估文物的出处?

这些问题让我们陷入 软件证明 领域!!

软件证明

An 证明 是一块 data 代表 事件证明。在现实世界中,我们通常称这些 认证.

例如,当实验室测试你的血液时,测试数据会被记录和认证。血液测试结果是 可验证 以及 可追溯的.

软件供应链证明

更接近我们的 IT 领域,您可以猜测这个过程可以被翻译成例如编译过程。

软件证明

有关编译服务器环境和工具、材料(源代码)以及产品/工件(二进制代码)的信息将成为此类证明的一部分。

显然,证明必须由授权证明者(经过认证且不可否认)生成,以提供可信度。 

可能你们中的一些人可能会想..什么是 签名和证明之间的区别?

代码签名和证明

从高层次来看, 签名 使用密钥对和工件创建。密钥对由公钥和私钥组成。 

代码签名

用户使用私钥对工件进行签名,然后其他人可以使用公钥验证签名。私钥必须保密,但公钥却广泛传播。

可以使用签名 证明私钥持有者使用私钥签署了该工件

签名 无法证明 

  • 用户的 意向 签署文物(他们可能被骗了),或者 
  • 用户意图做出任何 关于文物的具体声明 

通过 证明书,而不是直接签署工件,用户创建某种 文件 捕捉他们的意图 签署文物和任何 具体主张 作为此签名的一部分。

In-toto认证框架

最常见的框架是 in-toto 认证框架

  • 定义 standard 证明格式 将主题(被描述的工件)与经过验证的关于工件的元数据绑定在一起 
  • 提供了一组 预定义谓词 用于在整个软件供应链中传递经过验证的元数据

让我们详细了解一下证明格式。

An 证明书 是一个意念波· 数字签名的文档 包含 声明。

个人陈述 是证明的中间层,将其绑定到特定的 咨询内容 并明确识别 谓语:

  • 咨询内容:以 对工件的加密安全引用 (通常通过哈希)
  • 谓词:一组特定的 索赔 关于该工件的信息称为声明。这些声明可用于表达(并随后证明)您能想到的任何内容!它们可以表示手动批准、工件出处、自动测试结果、审计跟踪等! 

当本声明经过加密签名后,它被称为 证明书

CI/CD_安全_场景_3

以此方式,举例来说,爱丽丝创建了一份关于某个工件的声明,并使用她的私钥对其进行签名,从而创建了一份证明。

  • 然后 Bob 就可以 验证签名 在该证明中,允许他 相信这些说法 内。 

然后 Bob 就可以使用这些声明 决定 是否允许使用该工件。

软件证明

证明能帮助解决工件中毒问题吗?

在介绍完证明之后,让我们回到我们的问题。 证明如何帮助我们解决问题,即避免工件中毒?

恶意用户能够绕过“官方”机制创建一个工件,即通过使用他/她的 pipeline 生成工件。 

如果我们能够证明所下载的工件是使用官方 pipelines. 这只是我们所说的“篡改点”的一个例子,但可能还有很多其他的例子。

SSCS篡改点

如上图所示,篡改点有多个。这样,消费者 pipeline (在我们的示例中测试 CI)必须评估 构建过程的完整性 以及 文物本身的完整性.

在我们的例子中,中毒工件是通过引入新的(中毒)创建的 pipeline 篡改构建过程。但恶意用户可以做到以下事情:

  • 从中签出后修改代码 SCM 生成恶意二进制文件
  • 用任何其他恶意二进制文件替换编译生成的正确二进制文件
  • 破坏 Artifact Registry 并上传以其他方式构建的受污染工件
  • 等等

 可以看到,可能有多个“篡改”点。

这里最重要的是什么?显然是为了保护所有这些“篡改”点。但归根结底,最重要的是 该工件的“消费者”可以评估该工件的完整性,并决定是否继续使用它。

我们可以 评估文物的完整性 有两种方式。 

一是通过评估 出处 的神器。

通过生成 出处证明,我们提供了有关该工件的有用元数据(经过适当认证且不可否认)。 在以下示例中,我们将使用 Xygeni 盐 (信任的软件证明层),用于生成、注册和验证软件证明的组件。

防篡改构建
      - 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      
             

在上面的代码中,你可以看到有一个步骤是构建 war 文件,第二个步骤是生成 出处证明. 要做到这一点, pipeline 使用私钥,并且在证明中包含公钥。 

在幕后,Xygeni 的 命令将证明存储在 总账 (又称认证注册表, 记录 在我们的例子中,但您可以使用任何其他的)。 这样做了,消费者 pipeline 可以包括Xygeni的 验证引擎 验证文物的来源并评估文物的完整性。

 - 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

验证过程评估:

  • 工件 sha256sum 有效 (即有关于该“主题”的证明),并且
  • 证明已正确验证 (它是使用适当的私钥生成的) 

这个验证过程可以评估工件和证明是否有效。

但正如你记得的那样,在我们的案例中,该工件是由“恶意” pipeline (即不是原版,而是经过修改的 pipeline)然后我们必须继续检查另一个方面: 该工件是由“原始”生成的 pipeline,而不是任何其他的。 

为此,只需添加一行简单的代码来检查该条件,例如:

   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

如果该工件不是由我们的“安全”生成的,则此附加检查将失败 pipeline.

如果神器是由我们最初的 pipeline (cicd_top10_3_salt/.github/workflows/build.yml),grep 命令将会成功,否则将会失败,从而破坏 pipeline 并中止任何进一步的步骤。

“建造者” pipeline 只是一个需要检查的篡改点,但是,如前所述,我们还应该检查一些其他篡改点。

例如, 如果在 repo checkout 之后和 build 命令之前源代码被篡改了怎么办? 在这种情况下,要构建的代码与存储在 SCM. 

检查这个篡改点非常简单,只需在每一步检查材料的哈希值即可。

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[])

结论

综上所述, 软件认证 是对某个软件做出的断言,即关于软件工件或软件工件集合的经过验证的声明(元数据)。

软件证明是原始工件/代码签名的泛化。证明是一份签名文档(采用给定格式,通常基于 JSON),将元数据与工件关联起来。它们代表在每个构建步骤中将输入(材料)与输出(生成的工件)联系起来的证据。

证明提供了构建最终软件工件所完成步骤的可验证记录,包括每个步骤的输入材料和运行的构建命令。

总之,软件证明是检查我们构建过程的许多不同完整性方面的绝佳机制。 

看完了这个系列?别担心!欢迎随时返回“中毒 Pipeline 执行(PPE)' 或任何再次引起您兴趣的其他帖子!

敬请关注,我们将深入探讨软件证明和 build security 在进一步的博客文章中。 

中毒 Pipeline 执行(PPE)

深入了解 CI/CD Pipeline弱点(一)​

间接中毒 Pipeline 执行(I-PPE)

深入了解 CI/CD Pipeline弱点(二)​

工件中毒和代码注入

深入了解 CI/CD Pipeline脆弱性(三)​
sca-tools-软件-成分分析工具
确定软件风险的优先级、进行补救并加以保护
7-day免费试用
无需信用卡

保护您的软件开发和交付

使用 Xygeni 产品套件