一个自动化攻击活动正在用偷来的 GitHub 令牌,往仓库里塞工作流,把仓库能读到的密钥全部打印进 GitHub Actions 日志,再从日志里下载走,最后删掉运行记录。云安全公司 Wiz 的分析显示,这类攻击在私有仓库和 GitHub Enterprise 环境中已经得手。

打开网易新闻 查看精彩图片

问题出在一道本该兜底的门槛上。GitHub 有一项保护机制,会暂停可疑的工作流运行,等有写权限的协作者批准后才继续。Wiz 检查过的公开仓库运行,全部被这道机制拦在了审批环节,载荷一次都没跑起来。但这项保护目前只覆盖 github.com 上的公开仓库,私有仓库和 GitHub Enterprise Server 不在其列。

Wiz 从 9 月 15 日开始追踪这个活动,按攻击工具请求 GitHub API 时使用的用户代理 gheist,把它命名为 Gheist。截至目前,Wiz 已从超过 850 个被攻陷账号中恢复出 13500 多个被植入的分支,波及范围从个人项目一直到企业组织的私有仓库。至于其中有多少组织的密钥真正暴露,分析没有给出数字。

攻击的落点很具体:每个工作流都挂在一个新分支上,分支名是 ci-sync- 加六位随机字符。工具为令牌能触及的每个仓库建一个这样的分支,提交一个文件 .github/workflows/sync-check.yml,提交信息写着 ci: sync config。这次提交没有签名,但带着令牌所有者的名字。推送动作一发生,工作流就启动了。

工作流会打印任务的运行环境,以及任务能读到的每一个密钥,全部用 base32 编码。GitHub 本来会对日志里的密钥做遮蔽,但其文档说明,值一旦被编码或以其他方式改动,遮蔽就不再有保证。Wiz 的说法是,base32 之后的文本已经和原始密钥对不上,于是被完整打印出来。工作流还设了一个空的权限块,这只限制了攻击者根本不需要的 GITHUB_TOKEN,拦不住任务读取密钥。

工具的一个版本还会为仓库里的每个部署环境各跑一个任务,环境限定的密钥同样会被打印。按 Wiz 的说法,这些往往是生产凭证。随后工具通过 GitHub 的 API 把日志下载走,几秒内删掉运行记录。密钥是从 GitHub 自身流出的,没有攻击者服务器,也没有可拦截的出站连接。

在企业组织里,工具在几分钟内轮换使用多个用户和自动化账号的令牌,先克隆私有仓库,再植入工作流。数小时后,同一组织的云凭证被用来枚举 AWS Secrets Manager。这些凭证从哪来、被谁使用,分析没有交代。

截至 10 月 24 日,活动仍在运行并且还在变化。涉及账号中约三分之二已无法在 GitHub 上找到,Wiz 已向 GitHub 报告,并认为这些账号的消失很可能是一种响应。

按 GitHub 文档,任何对仓库有写权限的人都能读取其中配置的全部密钥。一个带此权限的失窃令牌,就足够了。Wiz 能追溯到的令牌来源包括信息窃取器日志,以及早前供应链攻击中泄露的令牌,例如 3 月 Trivy 漏洞扫描器的入侵事件。

同一批账号还被别的攻击者使用。10 月 6 日,391 个账号把约 1461 个仓库改名,伪装成破解软件传播恶意程序,这次改名抹掉了 610 个被植入的分支。部分受害者似乎还感染了被研究者关联到朝鲜的 PolinRider 恶意软件,10 月 19 日和 20 日,其中三个受害者在 vite.config.js 里加了加载器,重新推送了 Gheist 的提交。5 月向数千个 GitHub 仓库推送窃密工作流的 Megalodon 活动,也命中过其中 57 个账号。

大约 10 月 19 日,工具的一个版本开始推送第二个任务,专门针对自托管运行器,也就是组织自己运行、用来处理 GitHub Actions 任务的机器。它似乎只瞄准注册了这类运行器的仓库。该任务会在运行器机器上搜索凭证,包括 31 类令牌:私钥、进程环境、AWS 实例角色凭证、运行器凭证文件,以及 Kubernetes 服务账号令牌。Wiz 称这个任务有缺陷,分析中没有报告成功的运行。

排查可以从分支名入手。Wiz 建议检查名为 ci-sync- 加六个字符的分支,特征是唯一改动就是新增 sync-check.yml、提交信息为 ci: sync config。在 GitHub 审计日志里,找 ci-sync-* 分支上运行结束一分钟内即被删除的工作流,以及用户代理 gheist 的任何活动。如果工作流用 OIDC 登录云厂商,可以在云审计日志里找主体包含 ci-sync- 分支的角色假定或令牌交换,即使没有采集 GitHub 日志也能查。在自托管运行器上,则找 .gdmp_pat、.gdmp_patg、.gdmp_sweep 三个文件,以及任务日志里的字符串 gdmp-pivot。

一旦确认命中,处置顺序是:轮换该仓库可能用到的所有仓库级、组织级和环境级密钥;吊销推送提交的令牌,并检查其所有者设备是否中了信息窃取器;如果 ci-sync 分支在自托管运行器上跑过,把那台机器及其云实例角色视为已失陷;不要批准被挂起的 sync-check 运行,也不要执行 ci-sync 分支上的代码。

要让失窃令牌变得没那么好用,还有几件事可做:对存放生产密钥的环境强制要求审核人;把 OIDC 信任策略限制到受保护的分支或环境;用 GitHub Apps 或短期的细粒度令牌替代个人访问令牌;把自托管运行器当作短生命周期机器来跑,不给 Docker socket 访问权,也不开放云元数据访问。按 GitHub 文档,在必需审核人批准之前,任务读不到某个环境的密钥。