
攻击者利用被盗GitHub令牌通过Actions窃取密钥并删除痕迹
来源标题:WFX Phantom: The GitHub Actions Secret Stealer That Deletes Its Tracks
攻击者自2026年11月10日起用被盗GitHub令牌向可写仓库推送名为wfx的CI密钥窃取脚本,经GitHub Actions执行后窃取密钥、运行器内存和云凭证,并在约一分钟内回滚提交、删除工作流运行记录。截至11月12日,GitHub搜索显示其探测性拉取请求超过62,000个,约90%仍处于打开状态。
以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。
威胁情报
WFX Phantom:删除自身痕迹的GitHub Actions密钥窃取器
一个由被入侵的GitHub账号组成的协同僵尸网络发起了一场大规模攻击:先提交看似无害的pull request,然后直接将恶意shell脚本推送到默认分支。该脚本被称为wfx,通过GitHub Actions执行以窃取密钥,随后攻击者迅速回滚改动,几乎不留痕迹。
Owais Shaikh
查看LinkedIn
2026年11月12日
分享到X
分享到X
分享到LinkedIn
分享到Facebook
订阅我们的RSS feed
目录
正在加载导航……
从2026年11月10日开始,一名攻击者利用窃取的GitHub令牌,将CI密钥窃取器推送到这些令牌拥有写权限的每一个仓库。该窃取器自称wfx。它会收集所有GitHub Actions密钥、runner的内存以及云凭证,并在你轮换凭证后仍试图留在你的AWS和Azure账户中。随后,攻击者会回滚提交并删除工作流运行记录,通常在一分钟内完成。
规模很大。截至11月12日,GitHub搜索返回了超过62,000个攻击者的探测性pull request,其中约90%仍处于打开状态,而且每隔几分钟仍有新的出现。
我们从GitHub上提取了攻击者的80多个提交来研究payload,包括对nextcloud/docker、webdriverio/webdriverio、juejin-cn/repo-data和ipfs-shipyard/js-ipld-fixtures的提交。本文介绍我们的发现,包括两个会改变检测方式的细节:
查找标题为docs: dependency audit notes、来自wfx-docs分支的pull request,或查找11月10日及之后标题为ci: refresh build cache、chore: add ci helper或chore: add dependency audit workflow的提交。然后查看该提交之后Actions标签页中缺失的运行编号。运行记录缺失意味着窃取器很可能已运行,且攻击者删除了证据。将该工作流能触及的每一个密钥和云角色都视为已失窃。
攻击者并未利用GitHub Actions中的漏洞。它拥有真实开发者账号和机器人账号的有效令牌,并像开发者一样使用它们:提交pull request和推送提交。对于令牌拥有写权限的每个仓库,它执行同样的三个步骤。
攻击者从名为wfx-docs的分支提交一个标题为docs: dependency audit notes、正文为Routine dependency audit notes的pull request。它添加一个文件docs/wfx-audit.md,内容为两行文字。在我们检查的每个pull request中,该文件字节完全相同。
该pull request本身不执行任何操作。它告诉攻击者该令牌可以写入该仓库。如果你看到一个这样的pull request,那么打开它的账号已被入侵,即使没有发生其他事情。
接下来,攻击者以以下形式之一推送窃取器——一个约1,500行的单一shell脚本:
第一种形式最为常见:GitHub提交搜索返回了超过21,000个在11月10日至12日之间带有ci: refresh build cache消息的提交。
工作流一开始运行,攻击者就推送一个回滚(ci: restore,有时后跟ci: cleanup),并删除工作流运行及其日志。
在webdriverio/webdriverio中,两个chore: add ci helper提交是以该项目机器人账号推送的,该账号被允许绕过分支保护。一名贡献者在20分钟内发现了它们并提交了回滚。在drunomics和WebdriverIO中,攻击者使用的令牌都属于机器人账号。拥有写权限、尤其是拥有绕过权限的自动化账号,对这个攻击者来说与维护者同样有价值。
攻击者并不总能成功删除运行记录。在nextcloud/docker(7,400颗星)中,追加到update.sh的窃取器在该提交三秒后于仓库的update.sh工作流(运行#2675)中执行,且该运行从未被删除。一名Nextcloud开发者在27分钟后回滚了该提交。由于该运行记录得以保留,其公开日志显示了窃取器对自身的报告:
updating 32.0.15 [32] fpm-alpine memscan_files 3 gcp_gcloud_auth_empty gcp_live token=0 projects=0 gcloud=0 selfhosted_collected az_graph_token_fail oidc_discovery arn=none gcp=none azure=none archive_bytes 8971 exfil_no_runtime_vars
该任务在自托管runner上运行,因此窃取器还收集了主机级数据(selfhosted_collected)。它转储了runner进程内存(memscan_files 3)并打包了8,971字节。脚本在每次上传失败后打印deliver_retry,而日志中没有出现该信息,因此首次上传到194.150.166[.]244成功了。随后该任务以绿色状态结束,表面上看不出任何问题。
该脚本是普通且注释详尽的shell,通过WF_*变量设置功能开关。它分四个阶段工作。
它将任务的OIDC令牌换取AWS、GCP和Azure会话,链式进入仓库中指定的更多AWS角色,模拟GCP服务账号,并重放缓存的Azure CLI刷新令牌以获取Microsoft Graph和Key Vault访问权限。
有三个操作旨在在凭证轮换后继续存活:
所有内容都被打包成一个gzip压缩归档。代码支持多种投递渠道,包括三种使用GitHub自身基础设施的方式(Actions缓存、位于<!--WFX-BEGIN-->标记之间的步骤摘要,以及名为wfx-loot-<run id>的artifact),但在我们检查的每一份副本中这些都被关闭了。投递发生了两代:
转向Telegram意味着基于11月10日IP地址构建的阻止列表会漏掉最新的副本。出站允许列表仍能阻止两者,因为无论是裸IP还是api.telegram.org都不应出现在正常的构建中。
这些GitHub搜索显示了该活动的足迹。代码搜索需要你登录GitHub。
公开仓库样本,其中窃取器(携带194.150.166[.]244收集地址)被提交。所有这些都已被回滚;链接指向攻击者的提交。
(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。