作者:来自 Elastic Mika Ayenson, PhD
我们如何构建一个开源、可直接使用的 CI 模板,该模板通过信号提取和 LLM 推理来检测 GitHub Actions、GitLab CI 以及 Azure DevOps 流水线中的 CI/CD 滥用行为。
前言
在 2025 和 2026 年,我们观察到一个在整个行业反复出现的模式。攻击者不再直接针对生产服务器,而是转而攻击用于部署这些服务器的自动化系统。一旦开发者凭证被入侵、工作流文件被篡改,CI/CD 环境中的所有 secret 就可能开始流向攻击者控制的端点。我们在涉及大型开源项目、Fortune 500 公司以及关键基础设施工具的事件中都看到了这一攻击方式的真实发生。
攻击链看起来非常简单,却极具欺骗性:
开发者凭证被盗 → 工作流文件被修改 → CI/CD secrets 被窃取 → 横向移动至云环境与生产系统
今天我们正式开源 cicd-abuse-detector,这是一个可直接使用的 CI 模板,它通过基于正则表达式的信号提取以及 LLM 分析来检测 CI/CD 流水线中的可疑变更。它可以在 GitHub Actions、GitLab CI 和 Azure DevOps 中运行,并且基于公开安全研究中记录的真实攻击技术设计。
关键要点
CI/CD 环境是高价值攻击目标,因为一旦某个 workflow 被入侵,就可能同时泄露云凭证、package registry token、代码签名密钥、部署密钥以及 OIDC token
该工具从 diff 中提取 50+ 条基于正则表达式与元数据的信号,然后将这些信号连同完整 diff 一起交给 Claude 进行结构化威胁分析。无需 Python,仅依赖 bash 和 Claude Code CLI
检测模式已经在 Nord Stream 和 Gato-X 等攻击性工具,以及 ArtiPACKED 和 HackerBot-Claw 等真实事件中进行了验证
该项目包含 19 个恶意 diff 和 4 个良性示例,这些示例基于真实事件建模,并提供一个自动化测试套件,用于验证每一条信号
为什么 CI/CD 流水线是高优先级攻击目标
如果你花时间审查 GitHub Actions 或 GitLab CI 配置,你会注意到这些文件中集中了承担了多少“信任”。一个典型的部署 workflow 通常同时拥有 AWS 凭证、npm 发布 token、Docker Hub 密码,以及一个具有写权限的 GitHub token。攻击面并不是某个存在 CVE 的服务器,而是一个 YAML 文件。
大规模凭证窃取
攻击者一旦获取开发者凭证,就可以修改 workflow,从 CI 环境中窃取 secrets。GhostAction campaign 在 2025 年 9 月展示了这种规模化攻击:攻击者入侵 327 个 GitHub 用户、817 个仓库,并通过注入的 workflow 文件窃取 3,325 个 secrets,这些 workflow 会将凭证 POST 到攻击者控制的端点。
Shai-Hulud npm worm 进一步升级了攻击方式。该自传播攻击通过 gh auth token 获取 GitHub Personal Access Token,运行 TruffleHog 进行 secret 侦测,并使用被盗 token 在同一开发者名下的其他 package 中悄悄注入恶意代码。仅第一波攻击就发布了超过 46,000 个恶意包。
高权限触发器滥用
pull_request_target 是 GitHub Actions 中最危险的功能之一。与普通 pull_request 不同,它会在 base repository 的上下文中运行 workflow,并访问 secrets,但同时又可能执行来自不可信 fork 的代码。Pull Request Nightmare 的研究展示了这一点,并影响了 Google、Microsoft 和 NVIDIA 维护的仓库。
在 2026 年 2 月,一个名为 HackerBot-Claw 的自动化攻击活动系统性扫描公共仓库,专门寻找这种错误配置。它使用了五种利用方式,包括被污染的 Go 函数、branch name 命令注入、基于文件名的注入、直接 script 注入,以及针对 Claude-based code reviewer 的 AI prompt injection。在最严重案例中,Aqua Security 的 Trivy 仓库被完全攻破,进一步导致供应链攻击,影响近 7,000 台机器并泄露 33,000 个 secrets。正如Microsoft 安全博客所记录,这些攻击之所以成立,是因为被盗 token 在数周后仍然有效。
其余攻击分类
除了凭证窃取和触发器滥用之外,该威胁模型还覆盖了四类在公开研究中反复出现的攻击:
权限提升( permission escalation ):例如添加 permissions: write-all 或 id-token: write,从而扩大任何一次入侵的影响范围
runner 攻击( runner targeting ):将 job 重定向到自托管 runner(通常可访问内部网络),或指定攻击者控制的 container image
供应链篡改( supply chain manipulation ):包括使用 mutable action 引用(@main 而非 SHA 固定版本)、远程 script 执行(
|)、lockfile registry 替换以及依赖投毒防御规避( defense evasion ):通过修改 commit timestamp 让恶意文件看起来“更旧且可信”。KL4R10N 博客记录了这一技术在与朝鲜相关的攻击活动中的使用
这些攻击分别对应 MITRE ATT&CK 的具体技术:T1552(未安全存储的凭证)、T1195(供应链攻击)、T1070.006(时间戳伪造)、T1059(命令与脚本解释器)。
检测器是如何工作的
我们希望这些模板在运行时不需要 Python、自定义运行环境或复杂依赖。所有逻辑都运行在标准 shell 工具上,并且使用默认的 ubuntu-latest runner。唯一额外安装的工具是通过 npm 安装的 Claude Code CLI,它负责处理认证、重试以及模型路由。
阶段 1:过滤与 diff
当 pull request 被创建(或 push 到受保护分支)时,workflow 会识别所有变更文件,并按三个与 CI/CD 相关的层级进行分类。第一层是核心 CI 文件,例如 workflow 定义、pipeline 配置以及 Makefile。第二层是构建与发布相关的工件,例如 Dockerfile、package manifest、lockfile,以及签名或部署脚本。第三层则覆盖开发环境配置,例如 .vscode/tasks.json 和 .devcontainer 文件。
每个文件都会单独进行 diff,并限制在 10,000 字符以内。之所以按文件拆分而不是全局限制,是因为统一截断可能被绕过。攻击者可以通过在恶意 workflow 修改旁加入大量无害的 Dockerfile 修改,把 payload 推过字符限制。
阶段 2:信号提取
在 LLM 看到任何内容之前,50+ 条正则表达式模式会对每个 diff 进行扫描,以识别已知的危险模式。这些 signals 是“提示性”的,不会直接决定分析结果,但会为 LLM 提供一个预筛选的威胁摘要。下面是一些示例:
| 信号(Signal) | 模式(Pattern) | 检测内容(What it catches) |
|---|---|---|
| secrets_context | ${{.*secrets. | 在 workflow 中直接进行 secret 插值(direct secret interpolation) |
| pull_request_target | pull_request_target | 会让 PR 代码获取 secrets 的危险触发器 |
| checkout_ref | ref:.*github.event.pull_request.head.(sha|ref) | 在特权上下文中检出不可信的 PR 代码 |
| double_base64 | base64.*|.*base64 | 双重编码,用于绕过日志脱敏(Nord Stream 技术) |
| ld_preload | LD_PRELOAD | 通过环境变量注入实现任意代码执行 |
| vscode_auto_task | runOn.*folderOpen | VS Code task,在打开文件夹时自动执行(Contagious Interview) |
该信号列表基于真实的对抗性工具,包括 Nord Stream 和 Gato-X,并在 19 个基于特定真实事件建模的恶意示例 diff 上进行了测试。
该检测器在 GitHub Actions、GitLab CI 和 Azure DevOps 上运行方式完全一致。下面是它在各个平台上的检测触发示例:
阶段 3:LLM 分析
信号摘要、完整 diff、作者画像以及 commit 元数据会被打包,并通过 Claude Code CLI 发送给 Claude。分析 prompt 会引导模型完成以下几个方面的分析:
对 diff 的理解与逐文件风险评估
信号解释(结合上下文判断,单个 signal 不构成结论)
针对回溯时间戳(backdated commits)的时间分析
作者信任度评估(账号年龄、贡献历史、组织成员关系等)
基于 60+ 条信号组合表进行严重性校准
误报识别(例如:curl 下载已知工具不等同于数据外泄)
给出具体、可执行的修复建议(例如:“将 actions/setup-node@main 固定到具体 SHA”,而不是“请谨慎审查”)
最终输出是一个结构化 JSON verdict,包含 severity、confidence、reasoning、evidence 和 recommendations,并通过 JSON Schema 验证 进行校验。
阶段 4:告警与拦截(Alert and gate)
根据 verdict 的严重级别,workflow 会执行不同动作:输出 step summary、创建 issue、发送 Slack 通知,并在达到配置阈值时直接让 PR 检查失败(fail the PR check)。
Slack 和 GitHub Issues 只能解决即时通知问题,但无法提供可查询的历史记录。检测器生成的每一个 verdict(例如 benign、suspicious 或 malicious)都可以选择性地作为结构化文档发送到 Elasticsearch,并写入 logs-cicd.abuse-default data stream。
该 workflow 会将 verdict 连同 CI/CD 元数据(platform、repository、actor、event type、run URL)一起写入一个统一索引,从而覆盖所有三种支持的平台。
这一步使跨平台关联变得真正可行:来自 GitHub Actions 的告警和来自 GitLab CI 的告警,如果属于同一个 actor,会进入同一个 data stream,并且可以通过一条 ES|QL 语句进行统一查询:
FROM logs-cicd.abuse-* WHERE verdict.verdict IN ("malicious", "suspicious") AND @timestamp > NOW() - 7 days EVAL platform = cicd.platform, repo = cicd.repository, actor = cicd.actor, severity = verdict.severity KEEP @timestamp, platform, repo, actor, severity SORT @timestamp DESC该 schema 包含 cicd.platform、cicd.repository、cicd.actor,以及完整的 verdict 对象(verdict、severity、confidence、summary、reasons、evidence),使构建检测规则变得非常直接。
例如,一个在一小时内影响多个仓库的协同攻击活动、一个在多个平台上被反复标记的“惯犯(repeat offender)”,或者一个需要触发 incident response 的 critical 级别告警激增,都可以被轻松关联起来。
与真实攻击进行验证
为了验证覆盖范围,我们将检测信号与真实的攻击工具源码、公开研究以及事件复盘报告进行了对照。
Nord Stream:逐字节 payload 匹配
Nord Stream 是 Synacktiv 开源的 CI/CD secret 提取工具,支持 GitHub、GitLab 和 Azure DevOps。我们提取了 YAML 生成器源码(``),并将其输出模板与我们的示例 diff 进行对比。
GitHub payload 模板使用了 env -0 | awk -v RS='0' '/^secret_/ {print $0}' | base64 -w0 | base64 -w0
。我们的nord-stream-pipeline-exfil.diff 中包含这一行的逐字匹配内容,并且我们的 double_base64、env_null_dump 和 env_secret_grep 信号全部被触发。OIDC Azure 模板使用了 azure/login@v1
并配合id-token: write 权限,随后执行az accountget-access-token | base64 -w0 | base64 -w0。我们的 diff 捕获了这一完整流程,并触发了cloud_auth_action 和 id_token_write。Azure DevOps pipeline 技术( addSpnToEnvironment
用于 SPN 凭证泄露、DownloadSecureFile 用于安全文件窃取、通过 ssh.js修改 SSH task 源代码)全部出现在nord-stream-azure-devops.diff 中,并被平台特定信号识别。
ArtiPACKED:artifact 竞争条件
ArtiPACKED 研究表明,当将整个 checkout 目录作为 artifact 上传时,会泄露包含 GITHUB_TOKEN的.git/config 文件。由于 v4 artifact API 允许在 job 执行过程中下载 artifact,攻击者可以在任务结束前提取并使用该 token。
我们的 artifact-token-leak.diff模型复现了这一攻击模式,使用upload-artifact 并将 path: .设置为(整个工作区)。信号可以捕捉该行为,而 LLM 会进一步评估上传范围是否包含.git 目录。
GITHUB_ENV 注入:LD_PRELOAD 到 RCE
Legit Security 研究 显示,在 Google Firebase 和 Apache 的案例中,将不可信输入写入 $GITHUB_ENV可能允许攻击者设置任意环境变量,例如LD_PRELOAD 和 NODE_OPTIONS,从而在特权 workflow 中实现代码执行。
我们的 github-env-injection.diff复现了这一技术,包含LD_PRELOAD三种 payload:指向恶意共享对象的、带有强制注入的,以及对$GITHUB_PATH的篡改。 github_env_write、ld_preload 和 github_path_write 信号均如预期触发。
Contagious Interview:IDE 配置作为初始入口点
Contagious Interview campaign(归因于 DPRK 的攻击活动)通过 “虚假面试” 针对开发者,分发包含 .vscode/tasks.json文件的仓库,这些文件会在打开文件夹时自动执行。其展示内容被隐藏(reveal: never,echo: false),并使用 curl|node实现静默执行。
我们的 ide-config-poisoning.diff捕获了完整攻击链,包括自动执行触发器(runOn: folderOpen)、隐藏展示层、curl | nodepayload、用于隐藏files.exclude 目录的 .vscode条目,以及包含 base64 编码 URL 和执行链的木马化 postinstall hook。六种 signal 同时命中该攻击。
防御建议
除了部署检测器之外,我们还从这些攻击模式中总结出了一些关键加固措施:
将所有 action 固定到 SHA,而不是 tag 或 branch。SHA 固定引用可以防止类似 tj-actions(CVE-2025-30066)这类标签回滚攻击
将 secrets 限制在单个 step,而不是使用 job 级环境变量。每个 step 只能访问其真正需要的 secrets
尽量使用短生命周期的临时 token,以降低攻击面
避免使用 pull_request_target
,除非绝对必要。如果必须使用,也不要在同一个workflow_run-triggered workflow中 checkout PR head 代码。应使用独立来分离“需要 secrets”和“需要 PR context”的操作为每个 workflow 设置显式权限,因为默认 token 权限过于宽泛。在 workflow 级别设置 permissions: {},并为每个 job 添加细粒度权限
启用 persist-credentials: false
checkout,因为 actions/checkout 默认行为会将GITHUB_TOKEN 保存在 .git 目录中。如果上传 artifact,这个 token 也可能被一并带走
总结
CI/CD 流水线已经成为供应链攻击的主要攻击面。支撑现代软件交付的自动化系统,正是攻击者用来窃取凭证、投毒包以及横向进入云基础设施的关键入口。传统的代码审查很难发现这些模式,因为它们往往非常隐蔽、强依赖平台特性,并且刻意伪装成正常的 DevOps 改动。
通过结合基于正则的信号提取与 LLM 推理,我们可以在 pull request 阶段就识别这些模式,在它们进入生产环境之前进行拦截。该仓库提供了完整的威胁模型、测试套件以及示例 diff,如果你想深入理解细节或将其适配到自己的环境中都可以参考。
要开始使用,请查看 cicd-abuse-detector 仓库获取安装说明、完整威胁模型和示例 diff。我们也欢迎大家分享新的攻击模式和检测思路,可以在社区 Slack 中交流,或在 Discuss 论坛提问。
CI/CD 滥用与 MITRE ATT&CK 映射
我们使用 MITRE ATT&CK 框架来映射攻击者针对 CI/CD 流水线的战术、技术与流程(TTP)。
战术(Tactics)
| 战术 | CI/CD 相关性 |
|---|---|
| Credential Access (TA0006) | 从 CI 环境中窃取 secrets |
| Execution (TA0002) | 在 pipeline runner 中执行命令 |
| Persistence (TA0003) | 定时触发器、cron workflow |
| Defense Evasion (TA0005) | commit 时间戳篡改、日志规避 |
| Initial Access (TA0001) | 开发者凭证泄露、PAT 钓鱼 |
| Lateral Movement (TA0008) | 使用云凭证横向移动 |
技术(Techniques)
| 技术 | CI/CD 应用 |
|---|---|
| T1552: Unsecured Credentials | CI 环境变量、artifact、runner 内存中的 secrets 泄露 |
| T1195.002: Compromise Software Supply Chain | 被投毒的 actions、依赖与 lockfile |
| T1059: Command and Scripting Interpreter | curl 等命令执行 |
| T1070.006: Timestomp | 通过回溯 commit 时间绕过审查 |
| T1098: Account Manipulation | 通过 write-all、id-token: write 进行权限提升 |
| T1078: Valid Accounts | 使用被盗 PAT 修改 workflow |
参考资料
以下资源在上述研究中被引用:
cicd-abuse-detector
Nord Stream
Gato-X
ArtiPACKED
GhostAction campaign
Shai-Hulud npm worm
Pull Request Nightmare
HackerBot-Claw
StepSecurity HackerBot-Claw
Legit Security GitHub env injection
Contagious Interview
Codecov postmortem
Git history lies
Synacktiv CI/CD exploitation
Claude Code
关于 Elastic Security Labs
Elastic Security Labs 是 Elastic Security 的威胁情报研究团队,致力于推动威胁环境的积极变化。团队公开发布关于新兴威胁的研究,并从战略、战术与操作层面对攻击者目标进行分析,同时将这些研究整合进 Elastic Security 的内置检测与响应能力中。
关注 Elastic Security Labs(Twitter @elasticseclabs),或访问 Elastic Security Labs 获取最新研究成果。
原文:CI/CD pipeline abuse: the problem no one is watching — Elastic Security Labs