持续集成和持续部署(CI/CD) pipeline在促进软件开发流线型方面发挥着关键作用。然而,由于这些 pipeline变得越来越重要,保护它们免受漏洞攻击的必要性也变得更加明显。这项深入调查重点是解决 OWASP Top-10 中确定的突出风险 CI/CD 安全风险:中毒 Pipeline 执行(PPE)。
什么是中毒 Pipeline 执行(PPE)
根据 OWASP Top-10 CI/CD 安全风险,”中毒 Pipeline 执行 (PPE)风险是指攻击者能够访问源代码控制系统,但无法访问构建环境。 通过在构建过程中注入恶意代码/命令来操纵构建过程 pipeline 配置, 本质上 “毒害” pipeline 并在构建过程中运行恶意代码”
简而言之,中毒 Pipeline 执行(PPE)发生在 攻击者可以修改 pipeline 逻辑.
那里有两个 变种:
- 直接个人防护装备 (聚苯醚): 在 D-PPE 场景中, 攻击者修改 CI 配置文件 在他们可以访问的存储库中,要么将更改直接推送到存储库上未受保护的远程分支,要么从分支或分叉提交包含更改的 PR。 自 CI pipeline 执行由修改后的 CI 配置文件中的命令定义,攻击者的恶意命令最终在构建完成后在构建节点中运行 pipeline 被触发。
- 间接个人防护装备 (个人防护装备): 在某些情况下,如果对手能够接触到 SCM 存储库(例如,如果 pipeline 配置为从同一存储库中单独的受保护分支中提取 CI 配置文件)。 在这种情况下,与其毒害 pipeline 攻击者将恶意代码注入到 pipeline (例如:从 pipeline 配置文件)
在这两种情况下 GitHub 将执行修改后的 pipeline 无需事先审查或批准.
及早发现个人防护装备
我们如何检测这种类型的漏洞?
让我们看看这个例子 pipeline :
name: PR CI
on:
pull_request:
branches: [ main ]
env:
MY_SECRET: ${{ secrets.MY_SECRET }}
jobs:
pr_build_test_and_merge:
runs-on: ubuntu-latest
steps:
# checkout PR code
- name: Checkout repository
uses: actions/checkout@v4
# Simulation of a compilation
- name: Building ...
run: |
echo $MY_SECRET
mkdir ./bin
touch ./bin/mybin.exe
# Simulation of running tests
- name: Running tests ...
id : run_tests
run: |
echo Running tests..
chmod +x runtests.sh
./runtests.sh "${{ github.event.pull_request.user.login }}" "${{ github.workflow }}"
echo Tests executed.
虚拟 shell 脚本 (runtests.sh) 的内容如下:
#!/usr/bin/bash
echo "Executing Tests script [from user $1 at $2]" >> runtests.out
exit 0
此 pipeline 非常简单:它的目的是为审稿人提供一些初步的提示,以便 Pull Request (PR)接受流程:
- 它将触发于 拉取请求 (即每当创建 PR 时)
- 它检查 PR 代码(即贡献的代码)
- 它将使构建
- 它将对贡献的代码运行测试(例如通过执行 shell 脚本)
如果代码无法编译或无法通过测试,则步骤 #3(进行构建)和 #4(运行测试)将失败。因此,这些步骤是接受 PR 的必要条件,但不是充分条件。如果成功,repo 管理员将继续审查贡献的代码,并在此基础上接受/拒绝/评论 PR。
Xygeni 扫描仪
西吉尼 提供 CLI(“Xygeni 扫描仪”),可以嵌入到 pipeline 或在命令行中运行。Xygeni 扫描仪将处理 pipeline来检查漏洞,如果提供了 GitHub PAT,它将连接到 GitHub 以发现 org/repo 级别的漏洞。
Xygeni 库存
当我们在这个仓库上执行 Xygeni Scanner 时,它发现了一组有用的资产( Xygeni 库存)。库存中将填充多种不同类型的 CI/CD 资产,如:
- 此 SCM 系统 存储库存储的位置
- 此 SCM 插件 安装/使用
- 此 代码库 本身
- 此 SCM 工作机构 仓库所属
- 此 CI/CD Pipeline和工作
- 此 CI/CD 系统 运行 pipelines
- IaC 资源中心 定义到仓库中
- 外置 依赖
- 等等..
在我们的示例中,我们可以通过某些特定资产类型过滤库存(SCM- 以及 CICD 相关资产),因此我们可以看到:
- SCM 系统是 GitHub Cloud
- Repo 存储在 GitHub Cloud 中,属于特定的 GitHub 组织
- 那里有两个 pipeline由 GitHub 提供支持(CI/CD 系统)的
- 所有的 pipeline 包含一个特定步骤
通过选择以上 pipeline 我们可以看到一些漏洞:
- At pipeline 水平,它容易受到 直接 以及 间接 PPE。
我们可以看到中毒者的详细信息 Pipeline 执行漏洞
Xygeni 检测到它 易受 D-PPE 伤害 因为它是在 Pull Request 事件,并且没有额外的安全控制,因此任何 repo 用户都可以修改 pipeline 并且这些修改将在无需任何审查或批准的情况下执行。
同样,Xygeni 还检测到它 易受 I-PPE 伤害 因为从 pipeline:任何 repo 用户都可以修改 shell 脚本,并且这些修改将在无需任何审核或批准的情况下执行。
您想了解更多吗?
利用 PPE
为了利用 PPE,让我们考虑以下场景: 两种 repo 用户:
- An 内部用户 (负责该 repo 的内部开发人员),具有 repo 的写入权限
- An 外部用户 (负责该 repo 但对 repo 具有读取权限的外包开发人员),即不允许分支 repo 并被迫在 fork 上工作。
假设双方都是恶意攻击者(或被恶意行为者冒充)。存储库包含一些秘密,双方都希望 窃取回购秘密 并将其发送到黑客控制的服务器。为此,他们将利用中毒 Pipeline 执行漏洞 pipeline.
在两种情况下(外部和内部用户),他们都会打开 Pull Request 做同样的修改:
- 此 pipeline 并修改了 shell 脚本 至 阅读秘密 来自环境和 将其发送到黑客控制的服务器
修改可能如下:
两位用户都将创建 Pull Request 经过修改. 创建 PR 后, GitHub 将执行这两项修改 (无需事先审查或批准),结果如下:
对于写和读用户来说也一样, 在两种情况下,D-PPE 和 I-PPE 均被执行,不同之处在于 读取用户无法访问机密。 (!!!!)
原因是, 对于来自 fork 的 PR,GitHub 不允许访问 repo 机密。 虽然读取用户无法读取机密,但他/她仍然可以运行任何其他程序。一个典型的攻击示例是创建下载加密矿工的 PR,因此 GitHub 运行器在执行中毒时将执行加密矿工 pipeline.
当然,这不是一个安全的环境!! 回购管理员可以做什么来避免这种情况?
经过一番谷歌搜索后,仓库管理员决定修改 pipeline 被触发 拉取请求目标 事件。为什么?因为 pipelinepull_request_target 触发的 s 不允许执行 pipeline 修改即,无论用户如何修改,“原始” pipeline 将被执行。
按照我们的例子,攻击将与之前相同。那么在这之后会发生什么 pipeline 修改?
正如所料, D-PPE 未执行 但因为 I-PPE 仍然存在, 读取用户现在可以访问 repo 机密!
读取用户现在可以访问机密的原因是什么?尽管 pipeline 无法修改,但是仍然可以修改shell脚本。 当 pipeline 在 pull_request_target 上触发,它将以特权模式执行 so 它也将是 shell 脚本,导致 shell 脚本可以访问 repo 机密!!
预防措施
GitHub 提供了一些措施来防止恶意 PR。
分支保护规则
使用 GitHub,您可以为选定的分支定义分支保护规则。
对于受保护的分支,您可以指定一个策略 需要一个 pull request 合并前 (以及附加条件,如所需数量的批准、代码所有者的审核等)
需要特别考虑的几个情况是:
- “允许指定参与者绕过所需 pull requests“。
- “不允许绕过上述设置=
虽然大多数条件都增加了政策的严格性,但这些条件放松了政策,这可能为恶意活动打开大门,例如,凭证被“特权”行为者窃取的情况。
限制 GITHUB_TOKEN 权限(最低权限)
将 GitHub 令牌权限限制为所需的权限;这样,即使攻击者成功入侵了你的 pipeline,他们就无能为力了。
通过使用避免字符串插值 pipeline 环境变量
每当你在你的 pipeline,请注意,它们应默认被视为“不受信任”的数据(其内容由最终用户控制)。请参阅 不受信任的操作和工作流程安全 以及 学习 Github Actions。
您应该始终使用环境变量在脚本内插入输入变量,而不是使用字符串插值。
工作流程运行和批准要求
对于 国家 repos,GitHub 允许指定 如何与“外部”公关人员合作.
GitHub 组织设置(“组织 >> 设置 >> 操作 >> 常规”)指定如何管理外部 PR:
默认情况下,GitHub 会要求首次贡献者获得 PR 批准,这使得恶意请求攻击更加复杂。即便如此,攻击者仍可能通过贡献一些无辜的内容来获得项目维护者的信任 pull request 在真正的攻击发生之前。
从这个意义上说 第三种选择(需要所有外部合作者的批准)增加了更高级别的控制。
对于 私立 repos,GitHub 还在组织和 Repo 级别提供了有用的控制。
“运行工作流程 Pull Requests”(默认情况下未选中)允许用户从 fork PR 运行工作流程(使用具有只读权限且无权访问机密的 GITHUB_TOKEN)。通过同时选择此选项和最后一个选项(“需要批准 fork PR 工作流程“”,就可以达到和私有仓库类似的策略(如上图)。
正如我们在一位读者的 PPE 攻击中看到的那样, 允许从 fork 运行工作流 pull requests 不安全!!
其余选项(“从 fork 向工作流发送写入令牌 pull requests“和”将机密和变量从 for 发送到工作流 pull requests“) 降低安全级别 应用于分叉 PR。
您可以在组织级别或存储库级别定义此分叉策略。如果在组织级别禁用该策略,则无法在存储库级别启用它。但是,如果在组织级别启用该策略,则可以在存储库级别禁用它。
概括
我们希望你已经看到了拥有一些 pipeline 易受中毒 Pipeline 执行。 commit 脆弱的 pipeline,而且编写一个安全的方案非常困难。
因此,使用 Xygeni 扫描仪来了解此类漏洞非常有价值。
除非您知道漏洞的存在,否则您无法解决漏洞!
但是…还有一个问题尚未解决… 如何避免I-PPE?
这将是我们下一篇文章的主题 🙂... 间接中毒 Pipeline 执行(I-PPE) !!





