文章总结: 本文以Snowflake的GitHubActions命令注入漏洞为例,揭示了CI/CD流水线中直接拼接用户输入的安全风险。攻击者通过构造恶意Issue标题触发命令注入,窃取JiraAPIToken。修复方案强调使用环境变量传递用户输入,避免直接展开。建议开发者在DevSecOps实践中将用户输入视为不可信数据,采用安全编码规范。 综合评分: 89 文章分类: 漏洞分析,实战经验,安全建设,安全工具
安全警告!你的GitHub Actions可能正在泄露核心凭证(附修复方案)
原创
Hankzheng Hankzheng
技术修道场
2026年8月20日 07:59 广东
在小说阅读器读本章
去阅读
大家好,今天咱们来聊一个很多开发和运维兄弟在日常工作中极易踩坑,却能造成毁灭性打击的漏洞——CI/CD 自动化流水线上的配置安全。
最近,安全大厂 Wiz 披露了他们对数据仓库巨头 Snowflake 进行授权安全测试时发现的一个骚操作。Wiz 的安全研究员仅仅通过在 Snowflake 的开源项目里提交了一个精心构造的 GitHub Issue,就成功触发了命令注入,甚至直接拿到了 Snowflake 内部 Jira 系统的核心 API Token。
今天我就带大家深挖一下这个漏洞的技术原理。无论你是做开发、网络运维还是网络安全防护,这个案子绝对值得你加入“防踩坑指南”。
💣 漏洞起因:一条未过滤的“信任链”
这次出事的,是 Snowflake 公开的 .NET 驱动仓库(snowflakedb/snowflake-connector-net)。问题的根源出在他们用来做 CI/CD 自动化的 .github/workflows/jira_issue.yml 脚本上。
这个 Workflow 设计的初衷很好:当有人在仓库里新建一个公共 Issue 时,它会自动运行,并拿着内部的 JIRA_BASE_URL、JIRA_USER_EMAIL 和 JIRA_API_TOKEN 去跟 Jira 做联动。
但坏就坏在“直接拼接”上。
该脚本直接将不受信任的用户输入(也就是攻击者可以随意控制的 Issue 标题和正文),硬编码插到了 run: 模块的 Shell 执行语句中。大家可以想象一下,如果我在 Issue 的标题里写了一段 bash 闭合代码,系统在没有任何清洗的情况下直接丢给 Shell 去跑,这不就是经典的命令注入吗?
🤯 逻辑绕过:幽灵属性的妙用
你可能会问:大厂的 CI/CD 难道没有做条件限制吗?
确实做了,但写歪了。脚本里有一段逻辑判断,本来是想过滤掉机器人的操作,代码去检查了 github.event.pull_request.user.login,看它是不是等于 whitesource-for-github-com[bot]。
这里就出现了一个极其离谱的实现难点突破:这个 Workflow 是由新建 Issue 触发的,根本不是 Pull Request!
GitHub 官方机制补充: 在 GitHub Actions 中,如果你尝试引用(Dereference)一个当前上下文中根本不存在的属性(比如在 Issue 事件里去读取 PR 的信息),它不会报错,而是会默默地解析为一个空字符串(Empty String)。
因此,空字符串显然不等于 whitesource...[bot],这导致普通的、甚至恶意的 Issue 一路绿灯,直接越过了这层形同虚设的“安检”,进入到了核心的 Job 阶段。
🕵️♂️ 注入与提权:Wiz 的 Red Agent 是如何得手的?
Wiz 并不是靠人工慢慢测的,他们用了一套叫 Red Agent 的自动化测试系统。整个渗透路径非常值得我们学习:
-
初探报错:
Red Agent 丢过去的第一个 Payload,因为转义问题导致了 Shell 语法错误(Syntax Error)。但它没有放弃,而是根据回显自动调整了策略。
-
带外注入(OOB):
调整后的 Payload 成功闭合了上下文。Red Agent 并没有选择在控制台硬刚,而是非常聪明地打了一个带外回调(Out-of-band Callback)。
-
窃取凭据:
随着回调请求发到 Wiz 的服务器,一同被带出来的,正是 CI/CD 环境中挂载的
JIRA_API_TOKEN!
这个 Token 的权限有多大?
据 Wiz 披露,这个属于 [email protected] 的凭证,拥有对 snowflakecomputing.atlassian.net 下多个敏感看板的读取权限,其中涵盖了:
- 内部工程研发进度
- 安全合规审计
- 漏洞赏金(Bug Bounty)追踪
试想一下,如果黑客拿到这份“内部漏洞明细表”,这绝对是教科书级别的软件供应链信息泄露。
🛠️ 怎么防?标准修复方案看这里
事情在 6月23日 通过 HackerOne 报给 Snowflake 后,他们反应非常神速,当天就在 PR #1402 中合并了修复。
敲黑板了!这里的修复思路是整个漏洞最核心的价值点:
在 GitHub Actions 中处理用户输入,绝对不能使用原生表达式直接展开! 官方在去年 7 月份就发过警告,正确的做法是:通过中间环境变量传递,并使用 jq 等工具解析。
✅ 安全的做法演示:
# 不要直接在 run 里写 ${{ github.event.issue.title }}
env:
ISSUE_TITLE: ${{ github.event.issue.title }}
run: |
# 将变量安全地传递给工具,而不是让Shell解析它
echo $ISSUE_TITLE | jq ...
🤖 题外话:AI到底背不背锅?
这个案子还有一个非常有意思的“大瓜”。Wiz 在报告中暗示,这个致命漏洞是 GitHub Copilot Autofix 自动修复功能写出来的。AI 差点成了背锅侠。
但如果我们顺着 Git 的历史扒下去,真相其实是这样的:
那个带有高危注入风险的 jira_issue.yml 重构代码,实际上是一位人类老哥(sfc-gh-hpathak)在 2025年8月25日 的一次提交(Commit 094038e)中亲手写的。
之所以会扯上 Copilot,是因为在 6月18日 的一次合并(Squash merge 4a1b8ce)中,人类的代码和 Copilot 协助修改的另一处代码被揉在了一起。Git 历史证明了 Copilot 参与了那个 PR,但脆弱的那几行代码,还真是人类自己挖的坑。
💡 写在最后
好在根据 Snowflake 的日志审计,在漏洞暴露的短短 5 天内,除了 Wiz 之外并没有被真实的黑客利用,算是有惊无险。截至目前,这个事件甚至没有被分配 CVE 编号和 CVSS 评分。
这给我们敲响了警钟:在 DevSecOps 时代,流水线本身就是系统最脆弱的护城河。 各位师傅在配置这些脚本时,千万记得把用户输入当做“剧毒物质”来处理。
技术无止境,安全靠细节。今天分享的这个实战案例,希望能给大家带来一些启发。咱们下期再见!👋
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:技术修道场 Hankzheng Hankzheng《安全警告!你的GitHub Actions可能正在泄露核心凭证(附修复方案)》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论