乐于分享
好东西不私藏

AI 助手埋下安全坑:Snowflake 公开仓库曝出 GitHub Actions 工作流注入漏洞

AI 助手埋下安全坑:Snowflake 公开仓库曝出 GitHub Actions 工作流注入漏洞

导语:无需仓库权限,仅提交一条精心构造的GitHub Issue,即可在CI流水线执行任意命令,窃取内部Jira凭证。该漏洞由安全厂商Wiz的自动化安全代理Red Agent发现,事件也再次敲响警钟:AI生成代码同样需要严格安全审查。

近日,网络安全厂商Wiz披露了一处存在于Snowflake开源仓库snowflakedb/snowflake‑connector‑netGitHub Actions工作流注入漏洞。攻击者只需要创建一条恶意构造的GitHub Issue,就可以在CI工作流运行环境执行任意shell命令,获取工作流中配置的内部Jira系统凭证。值得注意的是,该漏洞仅影响仓库CI/CD自动化脚本,并未影响Snowflake .NET连接器正式发布版本

漏洞根源:两处致命错误叠加

漏洞文件为仓库内CI脚本.github/workflows/jira_issue.yml。该工作流的设计初衷是:一旦公开仓库有人新建Issue,自动在Snowflake内部Jira创建对应的Bug工单,脚本中直接引用了3个机密密钥:JIRA_BASE_URLJIRA_USER_EMAILJIRA_API_TOKEN,全部暴露在同一个执行步骤内。

问题一:不受信输入直接嵌入Shell脚本

脚本直接将攻击者可控的Issue标题、Issue正文,通过 ${{ github.event.issue.title }} 语法直接展开嵌入到run:的shell代码块中。虽然写了sed做简单转义,但转义逻辑运行在GitHub模板展开完成之后,攻击者利用单引号即可跳出字符串边界,注入任意操作系统命令。

GitHub官方文档明确警告:不要直接把外部可控上下文变量直接写进run块,未校验的外部输入会造成命令注入风险。

问题二:无效的安全判断条件

脚本写了一条防护判断:

if: ((github.event_name == 'issue_comment' && github.event.comment.body == 'recreate jira' && github.event.comment.user.login == 'sfc‑gh‑mkeller') || (github.event_name == 'issues' && github.event.pull_request.user.login != 'whitesource‑for‑github‑com[bot]'))

当触发事件类型为普通issues(新建Issue)时,github.event.pull_request这个对象本身就是null(空)。按照GitHub Actions上下文规则,访问不存在对象的属性会返回空字符串,null != 'whitesource‑for‑github‑com[bot]'表达式结果永远为true。这个安全校验完全失效,任何GitHub普通用户提交Issue都能够触发该高危Job

漏洞利用:自动化安全代理完成完整攻击

Wiz自研自动化安全检测系统Red Agent完成完整漏洞验证。最开始第一版攻击载荷因为注释符号破坏shell语法出现报错,AI代理自动分析报错信息,调整Payload,最终成功实现带外数据回传。

攻击成功后,从GitHub Actions运行器拿到了泄露的Jira API Token,该账号归属qa@snowflake.net,拥有Snowflake Jira实例snowflakecomputing.atlassian.net多个项目的读取权限,覆盖工程研发、安全合规、漏洞赏金追踪板块。Jira内部权限配置、流水线运行日志并未对外公开。

攻击payload原理:利用单引号逃逸echo字符串,内嵌curl请求,把环境变量内的Jira全套凭证base64编码之后回传到攻击者控制的服务器。

时间线:漏洞仅在线暴露5天

  1. 2026‑06‑18
    :PR #1218合并进入主分支,漏洞代码正式合入默认分支。提交记录中包含Copilot Autofix powered by AI共同作者标识。

    历史细节澄清:真正产生漏洞的jira_issue.yml不安全重构代码,来自2025‑08‑25人工提交;Copilot Autofix的改动原本作用于另一个文件jira_close.yml。但最终合并的squash压缩提交带上了Copilot Autofix共同作者签名,因此外界普遍将该漏洞归因为AI自动修复引入。从Git历史看,AI并不是漏洞代码的直接编写者,但参与了本次PR内其他修改。

  2. 2026‑06‑23
    :Wiz通过HackerOne漏洞赏金平台上报漏洞(报告#3819931)。Snowflake当天合并PR #1402完成漏洞修复。修复方案:不再直接把github事件对象嵌入shell脚本;把Issue标题、正文先赋值给环境变量,再通过jq --arg参数安全传入JSON构建逻辑,彻底杜绝shell注入风险。
  3. 2026‑06‑24
    :Snowflake轮换泄露的Jira API Token。审计日志核查确认,在这5天漏洞暴露窗口内,除Wiz授权安全测试行为之外,没有发现其他第三方未授权访问痕迹。审计原始日志未对外公开。
  4. 2026‑08‑17
    :Wiz对外公开发布本次安全研究。截至对外披露时间,该漏洞没有分配CVE编号,无CVSS评分,也未录入CISA已知利用漏洞KEV目录;主分支代码已经完全移除风险代码,同时也没有对应连接器软件版本更新,暂未观测到野外真实恶意利用事件。

Snowflake官方回应表示:感谢Wiz负责任的漏洞上报,内部调查未发现未授权访问证据,团队将持续强化开发安全流程,并和安全社区分享经验。

事件启示:CI/CD工作流安全最佳实践

GitHub官方博客曾专门针对Actions注入漏洞给出防护建议,结合本次事件提炼几点开发实操要点:

✅ 不要把不受信任的外部事件数据直接嵌入run脚本块Issue标题、评论、PR标题、分支名都属于外部不可信输入。优先赋值给env环境变量,在shell内读取环境变量,不要直接用 ${{ }}模板语法把内容直接写进shell代码。

✅ 充分理解GitHub Actions上下文对象行为访问不存在的对象属性不会报错,只会返回空字符串,很容易写出形同虚设的if防护条件,需要注意区分不同event事件下对象字段是否存在。

✅ AI生成代码必须和人工编写代码同等安全审查Copilot、CodeLlama等AI助手生成/自动修复代码,缺少业务上下文,会抹掉过去为了安全特意保留的防护逻辑。AI产出代码同样要经过SAST、CodeQL扫描,不能直接合并。

✅ 最小权限原则配置Workflow权限与密钥CI工作流尽量使用最小权限;密钥凭据仅给到真正需要的step;公开仓库接收外部issue、pr触发的流水线,尽量避免注入高权限内部系统密钥。

✅ 启用CodeQL扫描GitHub Actions工作流开启对YAML工作流文件的污点追踪扫描,可以提前识别这类命令注入风险。

本文素材来源Wiz安全博客、Snowflake GitHub仓库公开提交记录、GitHub官方安全文档。漏洞仅针对CI脚本,普通使用Snowflake .NET Connector用户不受影响。