什么是有毒管道执行 (PPE)?
中毒管道执行(PPE)是 OWASP CI/CD 安全风险之一,它是一种滥用源代码管理(SCM)系统访问权限的攻击载体,目的是导致 CI 管道执行恶意命令。虽然 PPE 攻击者无法访问构建环境,但他们可以访问 SCM,从而将恶意代码注入构建管道配置,操纵构建过程。
CICD-SEC-4:有毒管道处决事件的解释
中毒管道执行(PPE)被列为 OWASP 十大 CI/CD 安全风险中的 CICD-SEC-4,是一种针对持续集成和持续部署(CI/CD)系统的复杂攻击策略。
客户可以有多种选择:
在 PPE 策略中,攻击者在CI/CD 管道的 CI 部分中执行恶意代码,绕过了直接访问 CI/CD 系统的需要。该方法涉及对源代码管理 (SCM) 存储库进行权限操作。通过更改 CI 配置文件或 CI 管道作业所依赖的其他文件,攻击者可以注入恶意命令,从而有效地毒化 CI 管道,使未经授权的代码得以执行。
成功的 PPE 攻击可实现一系列广泛的操作,所有操作都是在管道身份的背景下执行的。恶意操作可能包括访问 CI 作业可用的机密、访问作业节点有权限访问的外部资产、将看似合法的代码和工件运送到管道中,以及访问作业节点网络或环境中的其他主机和资产。
鉴于其影响重大、可检测性低以及存在多种利用技术,PPE 攻击构成了广泛的威胁。对于安全小组、工程师和红方人员来说,了解 PPE 及其对策对 CI/CD security
定义管道执行
在持续集成(CI)的背景下,管道执行流指的是由管道构建的存储库中托管的 CI 配置文件所定义的操作顺序。该文件概述了执行作业的顺序,并详细说明了影响流程的构建环境设置和条件。触发后,管道作业会从选定的源代码(如提交/分支)中提取代码,并针对该代码执行 CI 配置文件中指定的命令。
管道内的命令由 CI 配置文件直接调用,或由 CI 配置文件引用的独立文件中的脚本、代码测试或线程间接调用。CI 配置文件通常具有一致的名称和格式,例如 Jenkinsfile (Jenkins)、.gitlab-ci.yml (GitLab)、.circleci/config.yml (CircleCI) 以及位于 .github/workflows 下的 GitHub Actions YAML 文件。
攻击者可以直接或间接利用管道执行命令的能力,在 CI 中执行恶意代码。
如何利用 CICD-SEC-4
个人防护设备攻击要取得成功,必须满足几个严重性标准:
- 攻击者必须获得 SCM 资源库的权限。这可以通过用户凭证、访问令牌、SSH 密钥、OAuth 令牌或其他方法实现。在某些情况下,匿名访问公共存储库可能就足够了。
- 对相关资源库的更改必须触发 CI 管道,而无需额外的批准或审查。这可以是直接推送到远程分支,也可以是通过远程分支或分叉的拉取请求提出修改建议。
- 攻击者获得的权限必须允许触发导致执行管道的事件。
- 攻击者可以更改的文件必须定义管道执行(直接或间接)的命令。
- 管道节点必须能访问非公开资源,如机密、其他节点或计算资源。
执行未审核代码的管道,例如由拉取请求或提交到任意存储库分支触发的管道,更容易受到 PPE 的影响。一旦攻击者可以在 CI 管道中执行恶意代码,他们就可以在管道身份的上下文中进行恶意操作。
中毒管道执行死刑的三种类型
中毒管道执行有三种不同的表现形式:直接 PPE(D-PPE)、间接 PPE(I-PPE)和公共 PPE(3PE)。
直接个人防护设备
在直接 PPE 场景中,攻击者会修改他们可以访问的版本库中的 CI 配置文件,方法是将更改直接推送到版本库中未受保护的远程分支,或者从分支或分叉提交包含更改的拉取请求。根据修改后的 CI 配置文件中的命令定义,管道执行由推送或拉动请求事件触发,一旦触发构建管道,就会在构建节点中执行恶意命令。

图 1:直接中毒管道执行攻击流程
图 1 所示的 D-PPE 攻击示例分为以下几个步骤:
- 攻击者在版本库中启动一个新的远程分支,用有害指令更改管道配置文件,以检索存储在 GitHub 组织内的 AWS 凭据,并将其传输到攻击者控制的外部服务器。
- 代码推送会激活一个管道,从代码存储库中提取代码(包括恶意管道配置文件)。
- 管道根据配置文件运行,现在已被攻击者篡改。恶意指令命令将 AWS 凭据作为存储库机密加载到内存中。
- 按照攻击者的指示,管道执行将 AWS 凭据传输到攻击者控制的服务器的任务。
- 攻击者窃取凭证后,就有能力渗透到生产环境中。
间接个人防护设备
当对手无法访问单片机存储库时,就会出现间接 PPE:
- 如果管道被配置为从同一版本库中单独的受保护分支中提取 CI 配置文件,则会出现这种情况。
- 如果 CI 配置文件存储在与源代码不同的单独存储库中,而用户没有直接编辑它的选项。
- 如果 CI 构建是在 CI 系统本身中定义的,而不是存储在源代码中的文件中,那么就会出现这种情况。
在这些情况下,攻击者仍然可以通过向管道配置文件引用的文件(如管道配置文件中引用的脚本、代码测试或 CI 中使用的自动工具(如衬砌器和安全扫描仪)中注入恶意代码来毒化管道。例如
- make 工具会执行 Makefile 文件中定义的命令。
- 从管道配置文件中引用的脚本,这些脚本与源代码本身存储在同一个存储库中(例如,python myscript.py - 其中的 myscript.py 将被攻击者操纵)。
- 代码测试:在构建过程中,在应用代码上运行的测试框架依赖于专用文件,这些文件与源代码存储在同一个存储库中。攻击者如果能操纵负责测试的代码,就能在构建内部运行恶意命令。
- 自动工具CI 中使用的分拣机和安全扫描仪通常依赖于存储库中的配置文件,该文件通常从配置文件内部定义的位置加载并运行外部代码。
发起间接 PPE 攻击的攻击者不是通过直接 PPE 来毒化管道,而是将恶意代码注入配置文件引用的文件中。恶意代码最终会在管道节点上执行,并运行文件中声明的命令。

图 2:间接中毒管道执行攻击流程
在这个 I-PPE 攻击示例中,事件链的展开过程如下:
- 攻击者在版本库中创建拉取请求,并在 Makefile 文件中添加恶意命令。
- 由于管道被配置为在针对存储库的任何 PR 时触发,因此 Jenkins 管道被触发,从存储库中获取代码,包括恶意 Makefile。
- 管道根据主分支中存储的配置文件运行。它进入构建阶段,并将 AWS 凭据加载到环境变量中(如原始 Jenkinsfile 中所定义)。然后,它会运行 make build 命令,执行添加到 Makefile 中的恶意命令。
- 执行 Makefile 中定义的恶意构建函数,将 AWS 凭据发送到攻击者控制的服务器。
- 然后,攻击者就可以使用窃取的凭据访问 AWS 生产环境。
公共个人防护设备
公共 PPE 是一种由匿名攻击者在互联网上实施的 PPE 攻击。公共软件源通常允许任何用户做出贡献,通常是通过创建拉取请求。如果公共存储库的 CI 管道运行匿名用户建议的未经审查的代码,就很容易受到公共 PPE 攻击。如果易受攻击的公共存储库的管道与私人存储库在同一个 CI 实例上运行,公共 PPE 还可能暴露内部资产,如私人项目的机密。
CI/CD 管道安全执行的重要性
通过成功的 PPE 攻击在 CI 中执行未经审查的恶意代码,可为攻击者提供与构建作业相同级别的访问权限和能力:
- 访问 CI 作业可用的机密,如作为环境变量注入的机密或存储在 CI 中的其他机密。由于负责构建代码和部署工件,CI/CD 系统通常包含数十个高价值凭据和令牌,例如到云提供商、到工件注册中心以及到 SCM 本身的凭据和令牌
- 访问作业节点有权限访问的外部资产,如存储在节点文件系统中的文件,或通过底层主机访问云环境的凭证。
- 能够以构建流程生成的合法代码为幌子,在管道的更远处发送代码和工件。
- 能够访问任务节点网络/环境中的其他主机和资产。
但是,组织可以通过安全的流水线执行来保障其软件产品和基础设施的安全,确保所有编译、测试和部署的代码都是合法的、未被篡改的。
与毒化管道执行相关的风险
个人防护设备的影响可能非常严重性,从未经授权的数据访问、软件完整性受损、系统中断,到数据泄露甚至系统全面接管。这些风险对企业及其客户都构成了重大威胁,凸显了个人防护设备的重要性。

图 3:中毒的 CI 管道的下游影响
在图 3 所示的八步供应链入侵操作中,攻击者获得了对 CI 管道的访问权限,并毒害了 SaaS 应用程序的组件。攻击者通过中毒组件在应用程序中构建后门功能,并将中毒插件发送到下游客户端。由于下游组织很可能认为中毒包是合法的,因此将其内置到云或本地部署的基础设施中。
从一个中毒的 CI 管道开始,攻击者就会造成指数级的附带损害,为无数组织创建后门访问权限。SolarWinds 的攻击就是这种情况。
阅读更多 CI/CD 管道攻击剖析
防止管道执行中毒
预防和减轻 PPE 攻击载体涉及跨越 SCM 和 CI 系统的多种措施:
- 确保运行未审查代码的管道在隔离节点上执行,而不是暴露在机密和敏感环境中。
- 评估在外部贡献者的公共软件源上触发管道的需求。在可能的情况下,避免运行源于分叉的管道,并考虑添加控制措施,例如要求管道执行必须经过人工批准。
- 对于敏感管道,例如那些暴露于机密的管道,应确保在 CI 系统中配置为触发管道的每个分支在 SCM 中都有关联的分支保护规则。
- 为防止篡改 CI 配置文件在管道中运行恶意代码,必须在管道运行前审查每个 CI 配置文件。另外,CI 配置文件可以在远程分支中管理,与包含管道中正在构建的代码的分支分开。远程分支应配置为受保护。
- 删除授予不需要 SCM 资源库权限的用户的权限。
- 每个管道只能访问其实现目标所需的凭证。凭证应具有最低要求的权限。