对GitHub Actions工作流进行安全审查,发现可利用的漏洞。当被要求“审查GitHub Actions”、“审计工作流”、“检查CI安全”、“GHA安全”、“工作流安全审查”,或审查.github/workflows/中的pwn请求、表达式注入、凭证窃取和供应链攻击时使用。专注于利用,提供具体的PoC场景。
<!--
攻击模式和真实世界示例来源于StepSecurity(2025)的HackerBot Claw活动分析:
https://www.stepsecurity.io/blog/hackerbot-claw-github-actions-exploitation
-->
GitHub Actions 安全审查
发现GitHub Actions工作流中可利用的漏洞。每个发现必须包含具体的利用场景——如果你无法构建攻击,就不要报告。
本技能编码了真实GitHub Actions漏洞的攻击模式——而非通用的CI/CD理论。
范围
审查提供的工作流(文件、差异或仓库)。根据需要研究代码库,以在报告前追踪完整的攻击路径。
需要审查的文件
.github/workflows/*.yml— 所有工作流定义action.yml/action.yaml— 仓库中的复合操作.github/actions/*/action.yml— 本地可重用操作- 工作流加载的配置文件:
CLAUDE.md、AGENTS.md、Makefile、.github/下的shell脚本
不在范围内
- 其他仓库中的工作流(仅注明依赖关系)
- GitHub App安装权限(如果相关则注明)
威胁模型
仅报告外部攻击者可利用的漏洞——即没有仓库写入权限的人。攻击者可以从fork打开PR、创建issue和发表评论。他们不能推送到分支、触发workflow_dispatch或触发手动工作流。
不要标记需要写入权限才能利用的漏洞:
workflow_dispatch输入注入——需要写入权限才能触发- 受保护分支上仅
push触发的工作流中的表达式注入 - 所有调用者均为内部时的
workflow_call输入注入 - 仅
workflow_dispatch/schedule工作流中的密钥
置信度
仅报告高和中置信度的发现。不要报告理论性问题。
| 置信度 | 标准 | 操作 |
|---|---|---|
| 高 | 追踪了完整攻击路径,确认可利用 | 报告并附带利用场景和修复方案 |
| 中 | 攻击路径部分确认,存在不确定环节 | 报告为需要验证 |
| 低 | 理论性或已在其他地方缓解 | 不报告 |
对于每个高置信度发现,提供全部五个要素:
- 入口点 — 攻击者如何进入?(fork PR、issue评论、分支名称等)
- 载荷 — 攻击者发送什么?(实际代码/YAML/输入)
- 执行机制 — 载荷如何运行?(表达式展开、检出+脚本等)
- 影响 — 攻击者获得什么?(令牌窃取、代码执行、仓库写入权限)
- PoC草图 — 攻击者将遵循的具体步骤
如果你无法构建全部五个要素,则报告为中置信度(需要验证)。
步骤1:分类触发器并加载参考
对于每个工作流,识别触发器并加载相应的参考:
| 触发器/模式 | 加载参考 |
|---|---|
pull_request_target |
references/pwn-request.md |
带命令解析的issue_comment |
references/comment-triggered-commands.md |
run:块中的${{ }} |
references/expression-injection.md |
| PAT/部署密钥/提升的凭证 | references/credential-escalation.md |
| 检出PR代码+配置文件加载 | references/ai-prompt-injection-via-ci.md |
| 第三方操作(尤其是未固定版本) | references/supply-chain.md |
permissions:块或密钥使用 |
references/permissions-and-secrets.md |
| 自托管运行器、缓存/制品使用 | references/runner-infrastructure.md |
| 任何确认的发现 | references/real-world-attacks.md |
选择性加载参考——仅加载与找到的触发器相关的部分。
步骤2:检查漏洞类别
检查1:Pwn请求
工作流是否使用了pull_request_target并且检出了fork代码?
- 查找带有指向PR头的
ref:的actions/checkout - 查找来自fork的本地操作(
./.github/actions/) - 检查是否有
run:步骤执行了来自检出PR的代码
检查2:表达式注入
在外部可触发的工作流中,run:块内是否使用了${{ }}表达式?
- 映射每个
run:步骤中的每个${{ }}表达式 - 确认该值由攻击者控制(PR标题、分支名称、评论正文——而不是数字ID、SHA或仓库名称)
- 确认表达式在
run:块中,而不是if:、with:或作业级别的env:中
检查3:未授权命令执行
由issue_comment触发的工作流是否在未授权的情况下执行命令?
- 是否有
author_association检查? - 任何GitHub用户都能触发该命令吗?
- 命令处理器是否也使用了可注入的表达式?
检查4:凭证升级
提升的凭证(PAT、部署密钥)是否可被不受信任的代码访问?
- 每个密钥的爆炸半径是多少?
- 被攻陷的工作流能否窃取长期令牌?
检查5:配置文件投毒
工作流是否从PR提供的文件加载配置?
- AI代理指令:
CLAUDE.md、AGENTS.md、.cursorrules - 构建配置:
Makefile、shell脚本
检查6:供应链
第三方操作是否安全地固定了版本?
检查7:权限和密钥
工作流权限是否最小化?密钥是否适当限定范围?
检查8:运行器基础设施
自托管运行器、缓存或制品是否安全使用?
安全模式(不要标记)
在报告之前,检查模式是否实际上是安全的:
| 模式 | 为什么安全 |
|---|---|
pull_request_target 且未检出fork代码 |
从不执行攻击者代码 |
run:中的${{ github.event.pull_request.number }} |
仅数字——不可注入 |
${{ github.repository }} / github.repository_owner |
仓库所有者控制此值 |
${{ secrets.* }} |
不是表达式注入向量 |
if:条件中的${{ }} |
由Actions运行时评估,而非shell |
with:输入中的${{ }} |
作为字符串参数传递,不经过shell评估 |
| 固定到完整SHA的操作 | 不可变引用 |
pull_request触发器(非_target) |
在fork上下文中运行,使用只读令牌 |
workflow_dispatch/schedule/push到受保护分支中的任何表达式 |
需要写入权限——超出威胁模型 |
关键区别: ${{ }}在run:块中危险(shell展开),但在作业/步骤级别的if:、with:和env:中安全(Actions运行时评估)。
步骤3:报告前验证
在包含任何发现之前,阅读实际的工作流YAML并追踪完整的攻击路径:
- 阅读完整工作流 — 不要仅依赖grep输出
- 追踪触发器 — 确认事件并检查控制执行的
if:条件 - 追踪表达式/检出 — 确认它在
run:块中或实际引用了fork代码 - 确认攻击者控制 — 验证该值映射到外部攻击者可以设置的内容
- 检查现有缓解措施 — 环境变量包装、author_association检查、受限权限、SHA固定
如果任何环节断裂,标记为中置信度(需要验证)或放弃该发现。
如果所有检查均未产生发现,报告零发现。不要编造问题。
步骤4:报告发现
## GitHub Actions 安全审查
### 发现
#### [GHA-001] [标题] (严重性:严重/高/中)
- **工作流**:`.github/workflows/release.yml:15`
- **触发器**:`pull_request_target`
- **置信度**:高——通过攻击路径追踪确认
- **利用场景**:
1. [逐步攻击]
- **影响**:[攻击者获得什么]
- **修复**:[修复问题的代码]
### 需要验证
[中置信度项目,附需要验证的解释]
### 已审查并确认安全
[已审查并确认安全的工作流]
如果没有发现:"未发现可利用的漏洞。所有工作流已审查并确认安全。"






