Malicious OVHcloud CLI Release Archives Steal Secrets
Swapped archives on five OVHcloud CLI releases delivered a secret-stealing build for 94 minutes on October 19, 2026; rotate every secret and install v0.16.0.
TL;DR
- On October 19, 2026, between 12:47 and 14:21 UTC, an attacker working through a hijacked contributor's GitHub account replaced the download archives of five published OVHcloud CLI releases, v0.11.0 through v0.15.0, with a secret-stealing build. The swapped files were downloadable for 94 minutes.
- Anyone who installed or updated the CLI from GitHub in that window, on macOS x86_64 or arm64, Linux x86_64 or arm64, or Windows x86_64, via the official install script, a direct release download, an unpinned CI fetch, or the CLI's own upgrade command, got the swapped build. Homebrew, go install, Docker and installs made before the window were not affected.
- OVHcloud founder Octave Klaba said the binary looks for secrets stored on the user's disk and that anyone affected should change every secret stored there. No copy of a swapped archive has been analyzed in public, and OVHcloud has not said how many copies were downloaded, so the number of victims is unknown.
- If you installed or updated in the window, treat every secret readable from that machine as exposed, rotate them from a different machine, remove the binary, and install the immutable v0.16.0, published the same evening, verifying it with
gh release verify-asset.
Am I affected?
You are affected if all of these hold:
- You installed or updated the OVHcloud CLI between 12:47 and 14:21 UTC on October 19, 2026.
- The install came from GitHub through one of the four routes listed below.
- Your platform was one of the five swapped binary assets per release: macOS x86_64 or arm64, Linux x86_64 or arm64, or Windows x86_64. The Linux i386, Windows i386, Windows arm64 and WebAssembly builds were left alone on every release.
The four affected download routes:
- The official install script,
install.sh, which downloads the latest release archive without checking its hash. - A direct download from the GitHub release page of any of the five releases, without independent verification.
- A CI job or build step that fetches the archive from the release download URL without a pinned hash.
- The CLI's own upgrade command, from a script or manual install on macOS or Linux. It downloads the archive and replaces the binary without checking its hash.
You are not affected if any of these hold:
- You installed with Homebrew. The cask in force during the window pinned the original SHA-256 of each archive, and Homebrew refuses a download whose hash does not match, so a brew install or upgrade in the window would have failed rather than installed the swapped file. The tap's cask history shows no change between the v0.15.0 update on September 16 and the v0.16.0 update on October 19. Some early write-ups listed Homebrew as exposed; the pinned hashes rule out that install path.
- You used
go installor built from source. The v0.15.0 tag still points at its September commit, 760fd72, and the source tree was never touched. - You pulled the Docker image
ovhcom/ovhcloud-cli. Its latest tag was last pushed on September 21, weeks before the swap, and had not been rebuilt. - You installed before 12:47 UTC on October 19 and did not update during the window, or you only ever fetched the Linux or Windows i386, Windows arm64, or WebAssembly builds.
| Release | Status |
|---|---|
| v0.11.0 through v0.15.0, published April 14 to September 16, 2026 | Six assets per release swapped during the window: five platform binaries plus checksums.txt; Linux i386, Windows i386, Windows arm64 and WebAssembly builds untouched; the swapped files deleted by OVHcloud at 14:21 UTC |
| v0.10.0, published March 2, 2026 | Clean: all ten original assets intact with their original timestamps |
| v0.16.0, published October 19, 2026 at 17:16 UTC | The clean, immutable replacement, published by the ovh-cds service account with a release attestation |
How do I fix it?
Install a verified clean release. For Linux x86_64, use the GitHub CLI to download v0.16.0, verify its attestation before extraction, and install the verified binary:
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
For other platforms, select the matching archive from the v0.16.0 release and verify it before extracting or running it.
The v0.16.0 release is immutable, so GitHub will not let anyone change its files, and it carries a release attestation, a signed record of its files, which the verify command checks. Going forward, pin the SHA-256 of the archive in CI. The affected download paths had no independent hash check to reject the replacement. A downstream CI job with a pinned hash detected the swap.
What does the malicious code do?
The swapped binary is a secret stealer by OVHcloud's own description, built from a local checkout outside the project's release pipeline, and no copy of it has been preserved or analyzed in public.
Klaba said the binary looks for secrets stored on the user's disk and called its quality mediocre but functional. OVHcloud has not published which paths it reads, where it sends what it collects, or how many copies were downloaded, and it deleted the swapped files before anyone archived them, so the payload's behavior is known only from the vendor's description.
What the build metadata proves, from the reporter's go version -m output captured before deletion, is how it was made:
- The module line reads
v0.15.1-0.20261013083505-5ead8ef9b43d+dirty, a pseudo-version, the placeholder Go generates for a build from an untagged commit, here one from October 13, 2026. A genuine release uses its tag rather than a pseudo-version. OVHcloud's clean v0.16.0 build reportsv0.16.0+dirty: the+dirtysuffix also occurs in genuine builds. The distinguishing marker here is the0.20261013083505pseudo-version timestamp, together with the unexpected commit and build flags. - The recorded commit is 5ead8ef, not 760fd72, the commit the v0.15.0 tag points to. It is a v0.16-era tree wearing a v0.15.0 filename.
- A
-ldflags="-s -w"line is present. The project's release pipeline builds with a version flag that Go leaves out of the recorded flags, so a genuine release shows no-ldflagsline at all. vcs.modified=truemeans the working tree had uncommitted changes when it was built, which is consistent with changes applied to a local checkout. This flag alone does not show what changed or prove the build is malicious.
How did the swap happen?
The swap needed one hijacked contributor account with write access and release immutability left off. The account used the GitHub Releases API to replace six assets on each of five releases, uploading checksums.txt last so the published hashes matched the replacement binaries.
The steps, reconstructed from GitHub's server-side records by OpenSourceMalware and corroborated by the reporter's issue #300:
- The attacker held credentials for
HassenBF, a legitimate OVH contributor with write access toovhcloud-cli. Its merged public contributions sit in OVHcloud's documentation repository. The account had been compromised for months: on March 5 and 6, 2026, four of its personal repositories were force-pushed with amended tip commits that added a hidden loader, the earliest server-verified use of the account by the attacker. - Between 12:47 and about 13:12 UTC on October 19, using the ordinary GitHub Releases API, the account deleted and re-uploaded five platform archives and
checksums.txton v0.15.0, then repeated the pass on v0.14.0, v0.13.0, v0.12.0 and v0.11.0, uploadingchecksums.txtlast in each pass so the published hashes matched the new binaries. - No tag was pushed and no GitHub Actions workflow ran. The release workflow only fires on tag pushes, and the run list shows no workflow at all between October 16 and the v0.16.0 tag push at 16:00 UTC on October 19, so the project's build pipeline was never touched; the attacker uploaded straight to the existing releases.
- Every affected install path fetched the new file from the same release URL, with no hash check anywhere: the install script has never checked one, the upgrade command does not check one, and unpinned CI fetched whatever was there.
- A downstream CI job whose owner had pinned the SHA-256 of the Linux arm64 archive failed at 13:50 UTC. Its owner ran
go version -m, found the out-of-pipeline build, and filed a public issue, because the repository had no private reporting channel. OVHcloud deleted the swapped assets at 14:21 UTC and published the immutable v0.16.0 at 17:16 UTC.
with write access"] --> B["Releases API swaps six assets
on five published releases"] B --> C["checksums.txt replaced last,
so published hashes match"] C --> D["Install paths fetch the same URL
with no hash check anywhere"] D --> E["Secret-stealing build runs
on the developer's machine"] C --> F["Separate CI job rejects the download:
pinned SHA-256 mismatch"] style D fill:#f8d7da
The gap is at D: none of the four affected paths independently verified the downloaded file before running it. The separate pinned SHA-256 check at F failed and caught the swap before that CI job could run the replacement.
What can an attacker do?
A secret stealer running as the user can read and copy secrets available to that account. Treat these as exposed, following OVHcloud's guidance to change every secret on the affected machine:
- OVHcloud credentials: API credentials in
~/.ovh.confand environment variables. - Other readable secrets: SSH keys,
.npmrc,.netrc,.pypirc,.envfiles, and cloud or CI credentials.
These are the credentials to inventory and rotate; the deleted payload has not been analyzed publicly to establish its exact target list.
The conditions the attack needs:
- An install or update during the 94-minute window through one of the four affected paths, on one of the five swapped platform builds.
- Nothing else. No user interaction beyond the normal install command, no compromise of OVHcloud's source or build pipeline, and no action by the victim beyond running the tool they intended to run.
What should I do if I installed it?
If you ran a swapped build, or cannot prove you did not, follow OVHcloud's guidance, with OpenSourceMalware's addition:
- Treat every secret readable from that machine as exposed, and rotate all of them from a different machine, because the swapped binary may still be running on the affected one. Rotation comes first: updating the binary replaces the program but does not revoke a key that was already copied.
- Remove the binary and install v0.16.0 or later, the immutable release published the same evening.
- Verify the new archive with
gh release verify-asset v0.16.0 FILE --repo ovh/ovhcloud-cli, and pin the SHA-256 in any CI job or Dockerfile that fetches it, the control whose absence opened the path.
How do I check if I was compromised?
Check the binary without running it. Three markers distinguish the swapped build. The distinctions matter because blanket advice that any +dirty build or any build carrying vcs lines is malicious would flag OVHcloud's own genuine releases, which report v0.16.0+dirty and vcs.modified=true:
go version -m ./ovhcloud
- The module line is a pseudo-version,
v0.15.1-0.20261013083505-5ead8ef9b43d+dirty, rather than a release tag likev0.15.0orv0.16.0. - The recorded commit is 5ead8ef rather than the tag's commit, 760fd72 for v0.15.0.
- A
-ldflags="-s -w"line is present; genuine OVHcloud release builds show no-ldflagsline.
For existing ovhcloud version output, a genuine v0.16.0 release reports v0.16.0. The source default is undefined when the release version flag is omitted; that is the expected value for an otherwise unchanged build with the recorded flags. It is not a confirmed observation of the deleted payload. Use the static checks above instead of executing a suspect binary.
For the one archive with a published hash pair, compare directly:
- v0.15.0
ovhcloud-cli_Linux_arm64.tar.gz, swapped:3833daffcaaaab27e4e0e0833240ffcb1df8df648f4ec0bc4a75c176e28933cb - v0.15.0
ovhcloud-cli_Linux_arm64.tar.gz, original:5c2beb56e6d83a4b426565a1557791ef759316abcc4effb38dd627c90aeb80e0
The indicators OpenSourceMalware published for the account-compromise campaign are distinct from the CLI binary, because no copy of that binary was preserved:
- Hijacked GitHub account:
HassenBF(ID 4911006). - Loader:
preinstall.js, SHA-25643df42fd79f6f81e530892b54634598770d6e496796a863c596e616bcb3fec70. - Lifecycle script:
"preinstall": "node preinstall.js"entries inpackage.json. - Solana dead-drop wallet:
BjVeAjPrSKFiingBn4vZvghsGj9KCE8AJVtbc9S8o8SC. - Throttle file:
~/init.jsonin the home directory on macOS.
Timeline
All times UTC, from GitHub's server-side records as published by OpenSourceMalware, from the reporter's issue #300, and from the release records.
- 2026-03-05 and 03-06: four personal repositories of the
HassenBFaccount are force-pushed with amended tip commits and hidden loaders, the earliest server-verified use of the account by the attacker. - 2026-05-25 to 09-07: the account keeps landing ordinary pull requests in OVHcloud's documentation repository.
- 2026-10-13 08:35: commit 5ead8ef merges to main; the malicious binary is later built from this tree with additional uncommitted changes (
vcs.modified=true). - 2026-10-19 12:47: the first swapped assets land on v0.15.0.
- 12:57: v0.15.0
checksums.txtis replaced so the published hashes match. - 13:01 to 13:12: v0.14.0, v0.13.0, and in the same pass v0.12.0 and v0.11.0, are swapped; the replacement ends.
- 13:50: issue #300 is opened by the CI owner whose pinned SHA-256 failed.
- 14:21: OVHcloud deletes every replaced asset across the five releases.
- 16:00: the v0.16.0 tag push starts the build and release workflows.
- 17:16: v0.16.0 is published by the ovh-cds service account, immutable, with a release attestation; Klaba posts the alert on X.
- 2026-10-20: OVHcloud says first checks show no other repository affected and promises a postmortem.
- 2026-10-26: this post.
FAQ
Was Homebrew affected?
No. The cask in force during the window pinned the original SHA-256 of each archive, and Homebrew refuses a download whose hash does not match, so an install or upgrade during the window would have failed loudly instead of installing the swapped file. Early write-ups listed Homebrew among the exposed paths, but the tap's commit history shows nothing changed between the v0.15.0 cask and the v0.16.0 cask.
Was the source code or the build pipeline compromised?
No. The v0.15.0 tag still points at its September commit, the source tree was never modified, and no GitHub Actions workflow ran during the replacement: the run list is empty between October 16 and the v0.16.0 tag push. The attacker uploaded files to existing releases through the Releases API using a contributor account's token. Replacing the assets was possible because release immutability was off.
Why did the release immutability setting matter?
Anyone with write access to a repository can delete and re-upload the files attached to a release, and the download URL does not change, unless GitHub's immutable releases feature is enabled. OVHcloud had not enabled it; the feature, generally available since October 2025, locks a release's files and tag but only protects releases created after it is turned on. It is enabled now, which is why v0.16.0 cannot be modified the same way.
How do I know v0.16.0 is clean?
It was published by OVHcloud's ovh-cds service account rather than a personal account, it is marked immutable so its files cannot be replaced, and it carries a release attestation you can check yourself with gh release verify-asset v0.16.0 FILE --repo ovh/ovhcloud-cli, following GitHub's published verification flow.
Was anyone actually compromised?
Unknown. OVHcloud has not said how many copies of the swapped archives were downloaded during the 94-minute window, and no copy of a swapped archive was preserved, so there is no victim count and no payload analysis. For anyone who used an affected path in the window, the safe position is to rotate regardless of evidence.
Is this the GlassWorm campaign?
The account link is real, the attribution is not proven. The hijacked account's own repositories were force-pushed in March with loaders tied by their Solana dead-drop wallet to GlassWorm's ForceMemo campaign, and whether the same actor used the account in October is, in OpenSourceMalware's words, their inference: nothing from the October binary has been tied to GlassWorm, because no copy of it was preserved.
Find unpinned downloads before the next swap
Start with Ostorlab's source-code scan guide to review repository risks. For OVHcloud specifically, inspect CI configurations and Dockerfiles for unpinned release downloads, and check retained binaries with go version -m for the v0.15.1-0.20261013083505-5ead8ef9b43d+dirty pseudo-version and -ldflags="-s -w" line. These manual checks do not assume that a scanner has a dedicated signature for this incident.
References
- The Hacker News, "Hackers Hijack Five OVHcloud CLI Releases to Distribute Secret-Stealing Malware", October 20, 2026, with the exposure paths, the Homebrew tap verification, the detection markers and the counter-advice on
+dirtybuilds: https://thehackernews.com/2026/10/hackers-hijack-five-ovhcloud-cli.html (checked October 26, 2026) - OpenSourceMalware, "Glassworm Hacks OVH Clouds CLI tool", October 19, 2026, the server-side timeline, the weaknesses table, the campaign indicators and the attribution limits: https://opensourcemalware.com/blog/glassworm-hacks-ovh-clouds-cli-tool (checked October 26, 2026)
- Issue #300, the reporter's capture of the swap, the
go version -moutput and the hash pair, opened 13:50 UTC on October 19, 2026: https://github.com/ovh/ovhcloud-cli/issues/300 (checked October 26, 2026) - The clean, immutable v0.16.0 release, published 17:16 UTC on October 19, 2026, with its asset list and changelog: https://github.com/ovh/ovhcloud-cli/releases/tag/v0.16.0 (checked October 26, 2026)
- The install script at v0.15.0, which downloads from the release URL and checks no hash: https://github.com/ovh/ovhcloud-cli/blob/v0.15.0/install.sh (checked October 26, 2026)
- The CLI's self-upgrade code at v0.15.0, which downloads the release asset and replaces the binary with no hash check: https://github.com/ovh/ovhcloud-cli/blob/v0.15.0/internal/upgrade/install.go (checked October 26, 2026)
- GitHub's immutable releases documentation, and its general-availability changelog entry of October 28, 2025: https://docs.github.com/en/code-security/concepts/supply-chain-security/immutable-releases and https://github.blog/changelog/2025-10-28-immutable-releases-are-now-generally-available/ (checked October 26, 2026)
- GitHub's release-asset verification flow: https://docs.github.com/en/code-security/how-tos/secure-your-supply-chain/secure-your-dependencies/verify-release-integrity (checked October 26, 2026)
- The Homebrew tap's cask commit in force during the window, pinning the original SHA-256: https://github.com/ovh/homebrew-tap/commit/b70a654 (referenced via The Hacker News, October 26, 2026)
- The ovh/ovhcloud-cli repository, the official CLI to manage OVHcloud services, 218 stars as of October 26, 2026: https://github.com/ovh/ovhcloud-cli and https://api.github.com/repos/ovh/ovhcloud-cli (checked October 26, 2026)
- Download counts for the clean v0.16.0 release assets, 2,053 in the week to October 26, 2026: https://api.github.com/repos/ovh/ovhcloud-cli/releases/tags/v0.16.0 (checked October 26, 2026)
- Klaba's October 19 statements on X, quoted by OpenSourceMalware and by next.ink: https://next.ink/262824/le-client-en-ligne-de-commande-dovhcloud-compromis-par-un-pirate-sur-github/ (referenced via The Hacker News, October 26, 2026)