我们的 AI 引擎 Neutron 在加州大学伯克利分校的 CyberGym 基准测试中取得了 97.1% 的成绩。 了解更多

深度解析

深度解析

WFX Phantom:GitHub Actions 凭据窃取活动

一场使用被盗 GitHub 令牌的活动将 wfx 窃取器推入数千个代码仓库,自 11 月 10 日起收集 CI 机密和云凭据。

TL;DR

  • 一场使用被盗 GitHub 令牌的活动自 2026-11-10 起将名为 wfx 的 shell 窃取器推入代码仓库。Step Security 于 2026-11-12 发布了分析。
  • 攻击者打开探测用 pull request,将窃取器隐藏在 workflow 或 shell 脚本中,并在其执行后删除 workflow 运行。截至 2026-11-12 07:40 UTC,GitHub 搜索显示超过 63,000 个探测用 pull request。
  • 窃取器收集仓库机密、runner 内存和云凭据。它可以在 AWS 和 Azure 中持久化。部分令牌来自 Fly.io 的 GitHub App;尚未证实 Fly.io 自身基础设施遭到入侵。
  • 删除相关提交和文件,轮换 workflow 能访问的每个机密和云角色,并检查持久化。没有分配 CVE,因此截至 2026-11-12 没有 CISA KEV 条目。

我受影响吗?

只要满足以下任一条件,您就受影响:

  • 2026-11-10 或之后,您的代码仓库中出现了一个标题为 docs: dependency audit notes、来自 wfx-docs 分支的 pull request。
  • 2026-11-10 或之后,带有以下探测消息之一的提交进入了某个分支。消息为 ci: refresh build cache、ci: restore、ci: cleanup、chore: add ci helper 和 chore: add dependency audit workflow。
  • 某个 workflow 执行了窃取器。窃取器可以是被注入的步骤、被追加的 shell 脚本、新的 .github/workflows/build-audit-<6 characters>.yml,或挂在 postinstall、build 或 test 上的隐藏 .ci-<6 characters>.sh。
  • 该作业能读取仓库或组织机密、能够请求云令牌的 GITHUB_TOKEN,或自托管 runner 上的凭据。
  • 您安装了某个受影响软件包版本。

探测用 pull request 意味着令牌可以写入该仓库。它不能证明窃取器已经运行。攻击者只在令牌权限和仓库配置允许执行的地方推送窃取器。报告估计这涉及数千个仓库,而不是全部 63,000 个探测。

最强的信号是运行编号缺失。如果 Actions 历史在某个探测提交之后从一个运行编号直接跳到下一个,攻击者很可能运行了窃取器并删除了证据。窃取器还会读取 Runner.Worker 进程内存,因此可以获取 workflow 从未传给作业的机密。

软件包 生态系统 受影响版本 干净版本
digitalelvis/neuronai-studio Packagist 3.14.3 3.14.2 或更早
github.com/onuragtas/docker-env Go v1.0.278 和 v1.0.280 2026-11-11 之前的版本
github.com/vkuznet/gocurl Go v0.0.0-20261111095532-8d5aad0cb0da 2026-11-11 09:55 UTC 之前的提交

两个 Go 模块只在 workflow 文件中包含窃取器,而 Go 构建从不执行 workflow 文件。仍应阻止这些版本,因为扫描或镜像仍可能显示它们。Packagist 版本包含 .ci-*.sh 脚本和 postinstall 钩子;composer install 不会执行它,但在包目录内运行 npm install 会执行。

在本地克隆中检查探测提交、隐藏脚本和脚本标记:

# Probe commits
git log --all --since=2026-11-10 --oneline \
  --grep='ci: refresh build cache' \
  --grep='ci: restore' \
  --grep='ci: cleanup' \
  --grep='chore: add ci helper' \
  --grep='chore: add dependency audit workflow'

# Hidden scripts and workflows
git log --all --since=2026-11-10 --name-only --pretty=format: \
  | grep -E '\.ci-[a-z0-9]{6}\.sh|build-audit-[a-z0-9]{6}\.yml' | sort -u

# Stealer markers
grep -R -E 'WF_CALLBACK|WF_CTOKEN|WF_EXFIL_|WF_TG_BOTS|wfx-loot|WFX-BEGIN' . --exclude-dir=.git

检查软件包版本:

composer show digitalelvis/neuronai-studio 2>/dev/null
go list -m all | grep -E 'onuragtas/docker-env|vkuznet/gocurl'

如何修复?

删除攻击者的更改,然后轮换 workflow 能访问的每个凭据。仅删除提交无法撤销窃取,因为窃取器可能已经发送了机密并植入了云持久化。

  1. 用 git revert 还原恶意提交,或将分支重置到最后一个干净提交。在重写历史之前,保留攻击者的提交和 workflow 日志作为证据。
  2. 删除攻击者的分支,关闭探测用 pull request,并移除其添加的任何协作者或部署密钥。
  3. 轮换作业能读取的每个机密:仓库、组织和环境机密;个人访问令牌;部署密钥;npm、PyPI、Docker 和 Vault 凭据;SSH 密钥;以及 AI 提供商密钥。
  4. 撤销被入侵 GitHub 账户的会话和令牌。如果令牌属于某个 GitHub App,请移除该应用的安装,仅在账户清理后再重新连接。
  5. 删除“恶意代码做什么?”中描述的 AWS 和 Azure 持久化。
  6. 从干净提交重建恶意提交与还原之间发布的每个软件包或版本。
  7. 限制谁可以向默认分支推送,要求审查,并将 GITHUB_TOKEN 权限默认设为只读。

如果无法立即轮换某个凭据,请缩小其范围或将其禁用。长期 AWS 访问密钥或攻击者创建的 Azure 联合身份凭据会在密码更改后继续存在。

恶意代码做什么?

窃取器是一个约 1,500 行的单一 shell 脚本,每个功能都有开关。它分四个阶段运行:收集、提升权限、持久化和外传。

收集。 它通过 ${{ toJSON(secrets) }} 和 ${{ toJSON(vars) }} 读取每个仓库机密和变量,以及作业的 GITHUB_TOKEN 和请求云令牌的能力。它转储 Runner.Worker 进程的内存,其中以解密形式保存作业可用的每个机密。它收集进程环境以及 AWS、Azure、GCP、Docker、Kubernetes、npm、PyPI、SSH 和 Vault 的凭据文件。它还获取 AI 提供商和编码代理的 API 密钥,以及在自托管 runner 上获取云实例元数据凭据。

提升权限。 它使用作业的 OpenID Connect 令牌打开 AWS、GCP 和 Azure 会话。它链式进入仓库中指定的其他 AWS 角色,冒充 GCP 服务账户,并重放缓存的 Azure CLI 刷新令牌以访问 Microsoft Graph 和 Key Vault。

持久化。 它可以将被假定 AWS 角色的信任策略改写为 "Principal": {"AWS": "*"},并为 IAM 用户调用 iam:CreateAccessKey,从而给攻击者长期密钥。它可以将名为 wfx-ci-<n> 的 Azure 联合身份凭据添加到 Azure AD 应用。被入侵仓库中的 workflow 随后无需存储的机密即可登录 Azure。第三个功能可以将必需检查标记为通过;报告称其检查的 101 个副本中没有一个开启该开关。

外传。 它将数据打包成一个 gzip 归档。报告发现三种投递配置。第一种向 194.150.166[.]244:8080/exfil 或 18.143.168[.]96:8080/exfil 发送 HTTP POST。第二种增加第三台服务器 153.75.82[.]159:8080/exfil 和 Telegram 备份。第三种最早见于 2026-11-12 05:30 UTC 左右,仅通过五个机器人令牌向聊天 -1004340163187 发送 Telegram。07:00 至 07:40 UTC 之间采样的每个副本都使用仅 Telegram 配置,因此基于 11 月 10 日地址的 IP 阻止列表不再覆盖最新副本。

窃取器隐藏在四个位置:

  • 添加到现有 workflow 的两个步骤。第一个将作业的内部令牌交给下一步,第二个以 Restore cache x9xx 之类的名称运行窃取器。
  • 追加到 workflow 已运行的现有 shell 脚本中,例如 nextcloud/docker 中的 update.sh 或 PancrePal-xiaoyibao/MinerU-xyb 中的 tests/retry_env.sh。
  • 在一次性 chore/<8 characters> 分支上创建新的 workflow .github/workflows/build-audit-<6 characters>.yml。
  • 隐藏的 .ci-<6 characters>.sh 加上 package.json 钩子。报告发现该钩子在 postinstall 中出现 11 次,在 build 中出现两次,在 test 中出现一次。

workflow 启动后,攻击者推送还原提交(ci: restore,有时是 ci: cleanup),并删除 workflow 运行及其日志。在 drunomics/nuxtjs-drupal-ce 中,窃取器于 2026-11-11 09:17:23 UTC 提交,12 秒后出现还原;Actions 历史从运行 #1963 直接跳到 #1965,运行 #1964 被删除。

当删除失败时,运行日志会显示窃取器的行为。在 nextcloud/docker 中,存活的运行输出了:

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、一次找到三个文件的内存扫描,以及一个 8,971 字节的归档。它们还显示该次运行中 GCP、Azure 和 OIDC 路径没有获得令牌。归档上传成功了,因为脚本在上传失败时会打印重试消息,而日志中没有。该次运行从机器本身收集了数据,而不仅是 GitHub 托管的 runner。

如何被利用?

  1. 攻击者持有对仓库有写权限的被盗令牌。令牌可能属于开发者、机器人账户或 GitHub App。
  2. 攻击者从 wfx-docs 打开探测用 pull request。该 pull request 添加一个静态文件 docs/wfx-audit.md,并告诉攻击者令牌可以写入。
  3. 攻击者将窃取器放入四个隐藏位置之一。该提交看起来像维护工作,作者通常是能绕过分支保护的机器人账户。
  4. workflow 在该提交上运行。窃取器收集机密、请求云令牌并发送归档。攻击者还原提交并删除运行,通常在一分钟内完成。
  5. 攻击者在仓库清理后使用被盗云凭据或植入的持久化。报告称,即使令牌被撤销,同一攻击者也可能通过持久化返回。

报告将来自 19 个账户的 173 个探测用 pull request 归因于为 Fly.io GitHub App(fly-io,应用 ID 34557)签发的令牌。该应用对内容、actions 和 pull request 有写权限,足以完成每个步骤。

报告称尚未确定这些令牌是如何获得的,也没有证据表明 Fly.io 自身基础设施遭到入侵。活动的大部分使用其他凭据,例如个人访问令牌。

攻击如何展开?

被盗令牌打开一个探测用 pull request,攻击者推送一个在正常 workflow 内运行的窃取器。运行会发送机密和云令牌,随后攻击者还原提交并删除运行。

flowchart LR
    A["被盗 GitHub 令牌"] --> B["探测用 pull request<br/>docs: dependency audit notes"]
    B --> C["推送窃取器<br/>workflow、shell 脚本或 npm 钩子"]
    C --> D["GitHub Actions 运行<br/>读取机密和 Runner.Worker 内存"]
    D --> E["云 OIDC<br/>AWS、GCP、Azure"]
    D --> F["外传归档<br/>HTTP 服务器或 Telegram"]
    E --> G["持久化<br/>AWS 信任策略、Azure 凭据"]
    F --> H["攻击者还原提交<br/>并删除运行"]

攻击者能做什么?

拥有写令牌的攻击者可以读取 workflow 能访问的每个机密,包括 workflow 从未引用的机密,因为窃取器会转储 runner 进程内存。它可以使用作业的云登录令牌在 AWS、GCP 和 Azure 中担任角色,并植入在令牌轮换后仍存在的持久化。

对仓库的影响取决于 workflow 能做什么。仅对自己代码有读权限的仓库损失小于拥有云部署角色或 npm 发布令牌的仓库。持有生产凭据的自托管 runner 是最坏情况。报告引用的 nextcloud/docker 存活运行显示窃取器读取了 runner 机器本身。

仅凭探测用 pull request 不会给攻击者这些能力。窃取器必须运行,并且令牌必须具有 workflow 使用的权限。报告没有公布受害者数量或已确认的数据窃取清单。

如果我的代码仓库受到影响,应该怎么办?

如果 workflow 运行了窃取器,请将仓库、runner 和作业能访问的每个凭据视为已泄露。删除软件包或提交只是第一步。

  1. 隔离自托管 runner,保留其磁盘、进程列表和日志。在从干净镜像重建之前不要复用它。
  2. 保留 workflow 运行日志和攻击者的提交。缺失的运行编号和提交历史是事件审查的证据。
  3. 从干净机器轮换作业能读取的每个机密和令牌。先从云角色和长期密钥开始,因为窃取器优先针对它们。
  4. 删除允许 "AWS": "*" 的 AWS 角色信任策略,删除您未创建的 IAM 访问密钥,并移除名为 wfx-ci-<n> 的 Azure 联合身份凭据。
  5. 检查使用被盗令牌创建的新仓库、协作者、部署密钥、webhook 和 workflow 文件。
  6. 重建窃取器存在期间发布的任何软件包,并从干净提交以新版本号重新发布。

如果您只安装了 digitalelvis/neuronai-studio 3.14.3 且 postinstall 钩子未运行,请删除该软件包并固定到 3.14.2 或更早版本。如果钩子已运行,请遵循相同的事件处理步骤。

如何检查是否已遭入侵?

从“我受影响吗?”中的 GitHub 搜索和本地检查开始。探测用 pull request、提交消息和隐藏脚本是公开证据。

# On GitHub: probe pull requests in your org
https://github.com/search?q=%22docs%3A+dependency+audit+notes%22+is%3Apr&type=pullrequests

在探测提交之后的 Actions 选项卡中查找缺失的运行编号。消失的运行是窃取器执行且攻击者清理的最明确迹象。

在代码中搜索收集服务器和脚本标记:

grep -R -E '194\.150\.166\.244|18\.143\.168\.96|153\.75\.82\.159|WF_CALLBACK|WF_CTOKEN|WF_TG_BOTS' . --exclude-dir=.git

检查云持久化:

# AWS: a role that trusts every account is the planted back door
aws iam get-role --role-name <role-name> --query 'Role.AssumeRolePolicyDocument'
aws iam list-access-keys --user-name <user-name>

# Azure: federated credentials named wfx-ci-<n>
az ad app federated-credential list --id <application-id>

报告列出以下指标:

  • 收集服务器:194.150.166[.]244:8080/exfil、18.143.168[.]96:8080/exfil 和 153.75.82[.]159:8080/exfil。
  • 回调令牌:b42d70a7aab9140ca19e8249 和 15f44heehnax8jdf41yx,作为 t= 发送。
  • Telegram:api.telegram.org 的 sendDocument,聊天 -1004340163187,机器人 ID 8708988906、9196217635、9180059428、8902935164 和 8876515636。
  • 探测用 pull request:标题 docs: dependency audit notes,分支 wfx-docs,文件 docs/wfx-audit.md,git blob ee6e5addfe591923fc385553cb66cf0d25634a6b。
  • 分支和文件:chore/[a-z0-9]{8}、ci-check-[0-9]{4}、.ci-[a-z0-9]{6}.sh 和 .github/workflows/build-audit-[a-z0-9]{6}.yml。
  • 脚本标记:名为 Pass runtime 和 Restore cache <4 characters> 或 Run audit 的步骤;变量 WF_CALLBACK、WF_CTOKEN、WF_EXFIL_* 和 WF_TG_BOTS;暂存文件夹 .wfx-$$;日志注释 memscan_files、archive_bytes 和 deliver_retry。

报告没有公布受害者数量,被删除的运行可能只留下缺失的编号作为证据。

时间线

  • 2026-11-10:活动开始。最早的副本向 194.150.166[.]244 发送数据。
  • 2026-11-11 09:17:窃取器被提交到 drunomics/nuxtjs-drupal-ce;12 秒后出现还原,运行 #1964 被删除。
  • 2026-11-11 10:02 至 10:36:一波探测用 pull request 使用为 Fly.io GitHub App 签发的令牌。
  • 2026-11-12 01:10:样本中出现 Telegram 备份通道。
  • 2026-11-12 05:30:新副本切换到仅 Telegram 投递。
  • 2026-11-12 07:40:GitHub 搜索返回超过 63,000 个探测用 pull request。
  • 2026-11-12 10:47:Step Security 发布分析。

活动的最初 35 小时,从首次 HTTP 外传到切换到 Telegram 以及 63,000 个探测的里程碑。
从 2026-11-10 到 2026-11-12 报告的活动时间线。来源:Step Security,2026 年 11 月 12 日检查

常见问题

探测用 pull request 意味着我的仓库被入侵了吗?

不是。探测只表明被盗令牌可以写入仓库。请查找载荷提交、隐藏脚本和缺失的 workflow 运行。如果窃取器运行了,请轮换机密。

GitHub 或 Fly.io 遭到入侵了吗?

报告称没有证据表明 Fly.io 自身基础设施遭到入侵。Fly.io GitHub App 向连接账户的用户签发令牌,攻击者使用了其中一些令牌。令牌是如何获得的尚未确定。GitHub 平台本身并未被入侵:该活动滥用有效的 GitHub 令牌和正常的 workflow 功能,这也是没有分配 CVE 的原因。

这次活动有 CVE 吗?

没有。该活动滥用有效的 GitHub 令牌和正常的 workflow 功能,因此没有分配 CVE。受影响的软件包版本由各注册表列出,而不是 CVE。

该活动在 CISA KEV 中吗?

不在。没有分配 CVE,而 CISA KEV 列出 CVE。截至 2026-11-12 没有 KEV 条目。

哪些软件包包含窃取器?

Packagist 上的 digitalelvis/neuronai-studio 3.14.3、github.com/onuragtas/docker-env v1.0.278 和 v1.0.280,以及 github.com/vkuznet/gocurl 的伪版本 v0.0.0-20261111095532-8d5aad0cb0da。两个 Go 模块仅在 workflow 文件中包含它。

攻击者删除了运行。我还能知道它是否执行过吗?

缺失的运行编号和探测提交是证据。提交之后,运行在编号运行之间消失就是模式。即使无法恢复日志,也要轮换凭据。

参考文献

  • Step Security,“WFX Phantom: The GitHub Actions Secret Stealer That Deletes Its Tracks”,2026-11-12 发布:https://www.stepsecurity.io/blog/wfx-phantom-the-github-actions-secret-stealer(2026 年 11 月 12 日检查)
  • Packagist,digitalelvis/neuronai-studio 的 3.14.2 至 3.14.4 版本:https://packagist.org/packages/digitalelvis/neuronai-studio(2026 年 11 月 12 日检查)
  • Go 模块代理,github.com/onuragtas/docker-env v1.0.278 和 v1.0.280:https://pkg.go.dev/github.com/onuragtas/docker-env(2026 年 11 月 12 日检查)
  • Go 模块代理,github.com/vkuznet/gocurl 伪版本:https://pkg.go.dev/github.com/vkuznet/gocurl(2026 年 11 月 12 日检查)
  • GitHub,报告引用的探测用 pull request:https://github.com/EbukaLovesTech/2gether/pull/1(2026 年 11 月 12 日检查)