恶意 OVHcloud CLI 发布归档窃取密钥
2026 年 10 月 19 日,五个 OVHcloud CLI 版本的归档被替换,在 94 分钟内分发窃取密钥的构建。请轮换所有密钥并安装 v0.16.0。
要点速览
- 2026 年 10 月 19 日 12:47 至 14:21 UTC,攻击者通过被劫持的贡献者 GitHub 账户,将五个已发布 OVHcloud CLI 版本 v0.11.0 至 v0.15.0 的下载归档替换为窃取密钥的构建。替换文件可下载时间为 94 分钟。
- 在该窗口从 GitHub 安装或更新 CLI 的用户,如果使用 macOS x86_64 或 arm64、Linux x86_64 或 arm64、Windows x86_64,并通过官方安装脚本、直接下载、未固定哈希的 CI 抓取或 CLI 自带升级命令,就会获得替换构建。Homebrew、go install、Docker 和窗口之前的安装不受影响。
- OVHcloud 创始人 Octave Klaba 表示,二进制会查找用户磁盘中的密钥,受影响用户应更改其中所有密钥。没有替换归档副本得到公开分析,OVHcloud 也未说明下载数量,因此受害者人数未知。
- 如果在窗口内安装或更新,应将该机器可读取的所有密钥视为暴露,从另一台机器轮换,移除二进制,并安装当晚发布的不可变 v0.16.0,通过
gh release verify-asset验证。
我是否受到影响?
以下条件全部成立时,您就受到影响:
- 在 2026 年 10 月 19 日 12:47 至 14:21 UTC 安装或更新 OVHcloud CLI。
- 安装通过下列四条路径之一来自 GitHub。
- 平台属于每个版本中被替换的五个二进制资产之一:macOS x86_64 或 arm64、Linux x86_64 或 arm64、Windows x86_64。每个版本的 Linux i386、Windows i386、Windows arm64 和 WebAssembly 构建均未被修改。
四条受影响下载路径:
- 官方安装脚本
install.sh,它下载最新发布归档而不校验哈希。 - 从这五个版本任一 GitHub 发布页面直接下载,未独立验证。
- 从发布下载 URL 抓取归档、未固定哈希的 CI 作业或构建步骤。
- 从脚本安装或手动安装的 macOS、Linux CLI 自带升级命令。它下载归档并替换二进制,但不校验哈希。
满足以下任一条件则不受影响:
- 通过 Homebrew 安装。窗口期间生效的 cask 固定了每个归档的原始 SHA-256,Homebrew 会拒绝哈希不匹配的下载,因此窗口内的 brew 安装或升级会失败,而不是安装替换文件。tap 的 cask 历史显示,从 9 月 16 日的 v0.15.0 更新到 10 月 19 日的 v0.16.0 更新之间没有变化。一些早期文章将 Homebrew 列为暴露,但固定哈希排除了这条安装路径。
- 使用
go install或从源码构建。v0.15.0 标签仍指向 9 月提交 760fd72,源码树未被修改。 - 拉取 Docker 镜像
ovhcom/ovhcloud-cli。其 latest 标签最后一次推送在 9 月 21 日,早于替换数周,且没有重新构建。 - 在 10 月 19 日 12:47 UTC 前安装,且窗口内没有更新;或者始终只获取 Linux 或 Windows i386、Windows arm64、WebAssembly 构建。
| 版本 | 状态 |
|---|---|
| v0.11.0 至 v0.15.0,2026 年 4 月 14 日至 9 月 16 日发布 | 每个版本六个资产在窗口内被替换:五个平台二进制和 checksums.txt;Linux i386、Windows i386、Windows arm64、WebAssembly 构建未动;替换文件由 OVHcloud 于 14:21 UTC 删除 |
| v0.10.0,2026 年 3 月 2 日发布 | 干净:全部十个原始资产完整,保留原始时间戳 |
| v0.16.0,2026 年 10 月 19 日 17:16 UTC 发布 | 干净、不可变的替代版本,由 ovh-cds 服务账户发布,附发布证明 |
如何修复?
安装经验证的干净版本。Linux x86_64 可使用 GitHub CLI 下载 v0.16.0,在解压前验证证明,再安装已验证二进制:
gh release download v0.16.0 --repo ovh/ovhcloud-cli \
--pattern 'ovhcloud-cli_Linux_x86_64.tar.gz' &&
gh release verify-asset v0.16.0 ovhcloud-cli_Linux_x86_64.tar.gz \
--repo ovh/ovhcloud-cli &&
tar xzf ovhcloud-cli_Linux_x86_64.tar.gz &&
sudo install -m 0755 ovhcloud /usr/local/bin/ovhcloud
其他平台从 v0.16.0 发布页面选择对应归档,在解压或运行前验证。
v0.16.0 是不可变发布,GitHub 不允许任何人修改其文件,并附有发布证明,即文件的签名记录,由 verify 命令检查。今后应在 CI 中固定归档的 SHA-256。受影响下载路径缺少可拒绝替换文件的独立哈希检查;正是一个固定了哈希的下游 CI 作业发现了替换。
恶意代码做了什么?
根据 OVHcloud 自身描述,被替换二进制是密钥窃取器,使用项目发布流水线之外的本地检出构建,没有副本被公开保存或分析。
Klaba 表示,二进制查找用户磁盘中的密钥,质量一般但能工作。OVHcloud 未公布它读取哪些路径、将收集数据发送何处或下载了多少份,并在有人存档前删除了替换文件,因此载荷行为仅来自厂商描述。
报告者在删除前捕获的 go version -m 输出,从构建元数据证明了制作方式:
- 模块行显示
v0.15.1-0.20261013083505-5ead8ef9b43d+dirty,这是 Go 为未打标签提交的构建生成的伪版本占位符,此处提交来自 2026 年 10 月 13 日。正式发布使用标签而非伪版本。OVHcloud 干净的 v0.16.0 构建显示v0.16.0+dirty,可见+dirty后缀也出现在真实构建中。此处具有区分力的标记是0.20261013083505伪版本时间戳,再结合意外提交和构建参数。 - 记录的提交为 5ead8ef,而不是 v0.15.0 标签指向的 760fd72。这是披着 v0.15.0 文件名的 v0.16 时期源码树。
- 存在
-ldflags="-s -w"行。项目发布流水线使用一个 Go 不会记录在构建参数中的版本标志,因此真实发布完全不显示-ldflags行。 vcs.modified=true表示构建时工作树有未提交更改,与在本地检出上修改相符。该标志本身不能说明改了什么,也不能证明构建恶意。
替换如何发生?
替换只需要一个具有写权限的被劫持贡献者账户,以及未启用发布不可变性。该账户通过 GitHub Releases API 替换五个版本各六个资产,最后上传 checksums.txt,使公布的哈希与替换二进制一致。
OpenSourceMalware 根据 GitHub 服务端记录重建、并由报告者问题 #300 佐证的步骤如下:
- 攻击者持有
HassenBF凭据,该合法 OVH 贡献者拥有ovhcloud-cli写权限,其已合入的公开贡献位于 OVHcloud 文档仓库。账户已被攻陷数月:2026 年 3 月 5 日和 6 日,其四个个人仓库被强制推送,修改后的顶端提交添加隐藏加载器,这是服务端记录验证的最早攻击者使用。 - 10 月 19 日 12:47 至约 13:12 UTC,该账户通过普通 GitHub Releases API,删除并重新上传 v0.15.0 的五个平台归档及
checksums.txt,随后对 v0.14.0、v0.13.0、v0.12.0、v0.11.0 重复操作。每轮最后上传checksums.txt,使公布哈希匹配新二进制。 - 没有推送标签,也没有 GitHub Actions 工作流运行。发布工作流只在标签推送时触发,运行列表显示 10 月 16 日至 10 月 19 日 16:00 UTC 的 v0.16.0 标签推送之间完全没有工作流,因此项目构建流水线未被触及;攻击者直接上传到现有发布。
- 每条受影响安装路径都从同一发布 URL 获取新文件,任何环节都没有哈希检查:安装脚本从未校验,升级命令也不校验,未固定哈希的 CI 获取当时存在的任何文件。
- 一个下游 CI 作业的所有者固定了 Linux arm64 归档的 SHA-256,作业在 13:50 UTC 失败。所有者运行
go version -m,发现流水线之外的构建,并因仓库没有私密报告渠道而提交公开问题。OVHcloud 于 14:21 UTC 删除替换资产,并于 17:16 UTC 发布不可变 v0.16.0。
被劫持贡献者账户"] --> B["Releases API 替换五个
已发布版本的各六个资产"] B --> C["最后替换 checksums.txt,
使公布哈希匹配"] C --> D["安装路径获取同一 URL,
全程无哈希校验"] D --> E["窃取密钥的构建
在开发者机器上运行"] C --> F["另一个 CI 作业拒绝下载:
固定的 SHA-256 不匹配"] style D fill:#f8d7da
缺口位于 D:四条受影响路径都没有在运行下载文件前进行独立验证。F 的独立固定 SHA-256 检查失败,在该 CI 作业运行替换文件前就发现了问题。
攻击者能做什么?
以用户身份运行的密钥窃取器可读取并复制该账户能访问的密钥。按照 OVHcloud 更改受影响机器全部密钥的指导,应将以下内容视为暴露:
- OVHcloud 凭据:
~/.ovh.conf和环境变量中的 API 凭据。 - 其他可读密钥:SSH 密钥、
.npmrc、.netrc、.pypirc、.env文件,以及云或 CI 凭据。
这些是应清点和轮换的凭据;被删除载荷未公开分析,无法确认精确目标列表。
攻击所需条件:
- 在 94 分钟窗口内,通过四条受影响路径之一,在五个被替换平台构建之一上安装或更新。
- 无其他条件。除正常安装命令外无需用户交互,无需攻陷 OVHcloud 源码或构建流水线,受害者只需运行本来打算运行的工具。
如果安装过它,该怎么办?
如果运行过替换构建,或无法证明没有运行过,应遵循 OVHcloud 指导并采用 OpenSourceMalware 补充建议:
- 将该机器可读取的每项密钥视为暴露,从另一台机器轮换全部密钥,因为替换二进制可能仍在受影响机器上运行。优先轮换:更新二进制只会替换程序,不会撤销已被复制的密钥。
- 移除二进制,安装当晚发布的不可变 v0.16.0 或更高版本。
- 使用
gh release verify-asset v0.16.0 FILE --repo ovh/ovhcloud-cli验证新归档,并在任何抓取它的 CI 作业或 Dockerfile 中固定 SHA-256。缺少这一控制正是攻击路径得以打开的原因。
如何检查是否已遭入侵?
检查二进制而不运行它。三个标记可区分替换构建。这些区别很重要,因为笼统地将任何 +dirty 构建或带 vcs 行的构建都判为恶意,会误报 OVHcloud 自身的真实发布,它们也显示 v0.16.0+dirty 和 vcs.modified=true:
go version -m ./ovhcloud
- 模块行是伪版本
v0.15.1-0.20261013083505-5ead8ef9b43d+dirty,而不是v0.15.0或v0.16.0这样的发布标签。 - 记录的提交为 5ead8ef,而非标签提交;v0.15.0 应为 760fd72。
- 存在
-ldflags="-s -w"行;真实 OVHcloud 发布构建没有-ldflags行。
对于已有的 ovhcloud version 输出,真实 v0.16.0 显示 v0.16.0。省略发布版本标志时,源码默认值为 undefined;对于其他方面未改动、使用所记录参数的构建,这是预期值,并非对已删除载荷的确认观察。应使用上述静态检查,而不是执行可疑二进制。
对于唯一公布哈希对的归档,可直接比较:
- v0.15.0
ovhcloud-cli_Linux_arm64.tar.gz,替换后:3833daffcaaaab27e4e0e0833240ffcb1df8df648f4ec0bc4a75c176e28933cb - v0.15.0
ovhcloud-cli_Linux_arm64.tar.gz,原始:5c2beb56e6d83a4b426565a1557791ef759316abcc4effb38dd627c90aeb80e0
OpenSourceMalware 公布的账户入侵活动指标与 CLI 二进制不同,因为后者没有副本保留:
- 被劫持 GitHub 账户:
HassenBF(ID 4911006)。 - 加载器:
preinstall.js,SHA-256 为43df42fd79f6f81e530892b54634598770d6e496796a863c596e616bcb3fec70。 - 生命周期脚本:
package.json中的"preinstall": "node preinstall.js"条目。 - Solana 死信箱钱包:
BjVeAjPrSKFiingBn4vZvghsGj9KCE8AJVtbc9S8o8SC。 - 节流文件:macOS 主目录中的
~/init.json。
时间线
所有时间均为 UTC,来自 OpenSourceMalware 公布的 GitHub 服务端记录、报告者问题 #300 及发布记录。
- 2026-03-05 和 03-06:
HassenBF账户四个个人仓库被强制推送,修改顶端提交并加入隐藏加载器,这是服务端验证的最早攻击者使用。 - 2026-05-25 至 09-07:该账户继续在 OVHcloud 文档仓库合入普通拉取请求。
- 2026-10-13 08:35:提交 5ead8ef 合入 main;恶意二进制后来基于该源码树、加上未提交变更(
vcs.modified=true)构建。 - 2026-10-19 12:47:首批替换资产进入 v0.15.0。
- 12:57:v0.15.0 的
checksums.txt被替换,使公布哈希匹配。 - 13:01 至 13:12:v0.14.0、v0.13.0,以及同轮中的 v0.12.0、v0.11.0 被替换,替换完成。
- 13:50:固定 SHA-256 检查失败的 CI 所有者打开问题 #300。
- 14:21:OVHcloud 删除五个版本中所有替换资产。
- 16:00:v0.16.0 标签推送启动构建和发布工作流。
- 17:16:ovh-cds 服务账户发布不可变 v0.16.0,附发布证明;Klaba 在 X 发布警报。
- 2026-10-20:OVHcloud 表示初步检查未发现其他仓库受影响,并承诺发布复盘。
- 2026-10-26:本文发布。
常见问题
Homebrew 受影响吗?
不受影响。窗口内生效的 cask 固定每个归档的原始 SHA-256,Homebrew 拒绝哈希不匹配的下载,因此窗口内安装或升级会明确失败,而非安装替换文件。早期文章将 Homebrew 列为暴露路径,但 tap 提交历史显示 v0.15.0 cask 与 v0.16.0 cask 之间没有变化。
源码或构建流水线被攻陷了吗?
没有。v0.15.0 标签仍指向 9 月提交,源码树从未修改,替换期间也没有 GitHub Actions 工作流运行:10 月 16 日至 v0.16.0 标签推送之间运行列表为空。攻击者使用贡献者账户令牌,通过 Releases API 向现有发布上传文件。能够替换资产,是因为没有开启发布不可变性。
发布不可变性设置为什么重要?
除非启用 GitHub 不可变发布功能,否则任何具有仓库写权限的人都能删除并重新上传发布附件,且下载 URL 不变。OVHcloud 此前未启用;该功能自 2025 年 10 月正式可用,锁定发布文件和标签,但只保护开启之后创建的发布。现在已经启用,因此 v0.16.0 无法以同样方式修改。
如何知道 v0.16.0 是干净的?
它由 OVHcloud 的 ovh-cds 服务账户而非个人账户发布,标为不可变,文件无法被替换,并附有发布证明。按照 GitHub 公布的验证流程,您可自行使用 gh release verify-asset v0.16.0 FILE --repo ovh/ovhcloud-cli 检查。
真的有人被攻陷了吗?
未知。OVHcloud 未说明 94 分钟窗口内替换归档下载了多少份,也没有副本保留,因此没有受害人数或载荷分析。对于窗口内使用受影响路径的人,安全做法是不论是否有证据都轮换密钥。
这是 GlassWorm 活动吗?
账户关联真实存在,但归因尚未证实。被劫持账户自己的仓库在 3 月被强制推送加载器,通过其 Solana 死信箱钱包可关联到 GlassWorm 的 ForceMemo 活动;但 10 月是否由同一行为者使用账户,按 OpenSourceMalware 的表述,只是他们的推断。10 月二进制没有任何内容被关联到 GlassWorm,因为没有副本保留。
在下一次替换前找到未固定的下载
先参考 Ostorlab 源代码扫描指南审查仓库风险。对于 OVHcloud,检查 CI 配置和 Dockerfile 中未固定的发布下载,并通过 go version -m 检查保留二进制中的 v0.15.1-0.20261013083505-5ead8ef9b43d+dirty 伪版本和 -ldflags="-s -w" 行。这些人工检查不假定扫描器已有针对此事件的专用特征。
参考资料
- The Hacker News,《黑客劫持五个 OVHcloud CLI 版本分发窃取密钥的恶意软件》,2026 年 10 月 20 日,包含暴露路径、Homebrew tap 验证、检测标记及对
+dirty构建的纠正说明:https://thehackernews.com/2026/10/hackers-hijack-five-ovhcloud-cli.html (2026 年 10 月 26 日核查) - OpenSourceMalware,《Glassworm 入侵 OVH Clouds CLI 工具》,2026 年 10 月 19 日,包含服务端时间线、弱点表、活动指标和归因局限:https://opensourcemalware.com/blog/glassworm-hacks-ovh-clouds-cli-tool (2026 年 10 月 26 日核查)
- 问题 #300,报告者捕获的替换情况、
go version -m输出及哈希对,2026 年 10 月 19 日 13:50 UTC 创建:https://github.com/ovh/ovhcloud-cli/issues/300 (2026 年 10 月 26 日核查) - 干净、不可变的 v0.16.0,2026 年 10 月 19 日 17:16 UTC 发布,包含资产列表和更新日志:https://github.com/ovh/ovhcloud-cli/releases/tag/v0.16.0 (2026 年 10 月 26 日核查)
- v0.15.0 安装脚本,从发布 URL 下载且不校验哈希:https://github.com/ovh/ovhcloud-cli/blob/v0.15.0/install.sh (2026 年 10 月 26 日核查)
- v0.15.0 CLI 自升级代码,下载发布资产并替换二进制,不校验哈希:https://github.com/ovh/ovhcloud-cli/blob/v0.15.0/internal/upgrade/install.go (2026 年 10 月 26 日核查)
- GitHub 不可变发布文档及 2025 年 10 月 28 日正式可用更新日志:https://docs.github.com/en/code-security/concepts/supply-chain-security/immutable-releases 和 https://github.blog/changelog/2025-10-28-immutable-releases-are-now-generally-available/ (2026 年 10 月 26 日核查)
- GitHub 发布资产验证流程:https://docs.github.com/en/code-security/how-tos/secure-your-supply-chain/secure-your-dependencies/verify-release-integrity (2026 年 10 月 26 日核查)
- 窗口内生效的 Homebrew tap cask 提交,固定原始 SHA-256:https://github.com/ovh/homebrew-tap/commit/b70a654 (通过 The Hacker News 引用,2026 年 10 月 26 日)
- ovh/ovhcloud-cli 仓库,管理 OVHcloud 服务的官方 CLI,截至 2026 年 10 月 26 日有 218 个 star:https://github.com/ovh/ovhcloud-cli 和 https://api.github.com/repos/ovh/ovhcloud-cli (2026 年 10 月 26 日核查)
- 干净 v0.16.0 发布资产下载量,截至 2026 年 10 月 26 日的一周为 2,053:https://api.github.com/repos/ovh/ovhcloud-cli/releases/tags/v0.16.0 (2026 年 10 月 26 日核查)
- Klaba 于 10 月 19 日在 X 的声明,由 OpenSourceMalware 和 next.ink 引用:https://next.ink/262824/le-client-en-ligne-de-commande-dovhcloud-compromis-par-un-pirate-sur-github/ (通过 The Hacker News 引用,2026 年 10 月 26 日)