Hacking

OVHcloud CLI Breach: Malicious Release Archives

Published  ·  9 min read

Your OVHcloud CLI install might be carrying a build that harvests every secret on your disk, because an attacker used a hijacked contributor account to replace the download archives on five existing releases, and those files sat on GitHub for up to 94 minutes on Monday, October 19, reaching anyone who installed or updated through a path that checks for no hash, which is most of the paths people actually use.

The clean build is out. The compromised window is closed. The secrets copied during those 94 minutes are a separate problem, and they don't go away when you upgrade.

What Makes the OVHcloud CLI Compromise So Unsettling

Nothing about this attack touched the source code. Not a line.

The release tag still pointed where it always did. The build pipeline never ran. The repository was never breached in the way supply chain stories usually describe.

The attacker simply deleted the archives attached to existing releases and uploaded different ones under the same names. The download URLs never changed, because release immutability was never enabled.

That's the whole trick, and it worked on five releases published between April and September.

Detail

Value

Attack date

Monday, October 19, 2026

Window live

12:47 to 14:21 UTC, up to 94 minutes

Compromised releases

v0.11.0 through v0.15.0

Clean release

v0.10.0 and earlier

Files replaced per release

Six

Files left untouched

Linux i386, Windows i386, Windows arm64, WebAssembly

Detection

A customer's CI job with a pinned SHA-256

Fix

Clean v0.16.0, published as immutable at 17:16 UTC

Which Files the Attacker Replaced

On v0.15.0, six files were swapped:

  • The macOS x86_64 archive
  • The macOS arm64 archive
  • The Linux x86_64 archive
  • The Linux arm64 archive
  • The Windows x86_64 archive
  • The checksums.txt file

That last one is the part worth pausing on. The attacker replaced the checksums file too, so the published hashes matched the new binaries. Anyone verifying against GitHub's own checksums verified against the attacker's.

The same six files are now gone from v0.11.0 through v0.14.0 as well. Those releases now offer only the four archives the attacker never touched.

Who Was Actually Exposed

This is where most people will want to know whether they need to act, and the answer depends on how you installed and when.

You got a swapped file if, during that window and on an affected platform, you did any of these:

  • Ran the official install script. The one-liner downloads the latest release archive and installs it, and per the reporting, it has never checked a hash.
  • Downloaded an archive from any of the five release pages. Straight from GitHub, during the window.
  • Ran a CI job or build step that fetches the archive from the release download URL without a pinned hash.
  • Ran the CLI's own upgrade command from a v0.12.0, v0.13.0, or v0.14.0 install on macOS or Linux, where the tool was installed by the script or by hand. That command downloads the archive and replaces the binary with no hash check. On a Homebrew or go install setup, it only prints the command to run instead.

Who Was Not Exposed

Install method

Status

Homebrew

Safe. The cask pinned the original SHA-256, so brew would have failed rather than installed the swapped file

go install or built from source

Safe. The v0.15.0 tag still points at commit 760fd72

Docker image

Safe. Its latest tag was last pushed September 21

Installed before 12:47 UTC and not updated

Safe. That's the original binary

The Homebrew point deserves emphasis, because some early write-ups listed it as an exposed path. The Hacker News checked the tap's commit history and found nothing changed between the v0.15.0 cask on September 16 and the v0.16.0 cask on Monday evening.

So the one distribution path that pinned hashes was also the one that couldn't be poisoned. That's not a coincidence, and it's the lesson.

How to Tell If You Downloaded the Bad Build

A customer pulled the build information out of the new v0.15.0 Linux arm64 binary using go version -m, which reads the file without running it, and posted the output in a GitHub issue.

Three things mark the bad build:

  • The module line reads a pseudo-version. Specifically v0.15.1-0.20261013083505-5ead8ef9b43d+dirty. Go generates that string for a build from an untagged commit, and this one dates to October 13. A genuine release carries its tag number.
  • The recorded commit is 5ead8ef. The v0.15.0 tag points at 760fd72.
  • An -ldflags="-s -w" line appears. OVHcloud's release pipeline builds with a version flag that Go leaves out of the recorded flags, so a genuine release shows no -ldflags line at all.

The Advice Circulating That Will Mislead You

Some guidance going around says any build marked +dirty or carrying vcs lines is malicious.

That would flag OVHcloud's own release. The Hacker News ran the same command on the clean v0.16.0 Linux x86_64 binary and got v0.16.0+dirty, vcs.modified=true, and the commit it was built from.

+dirty is not the signal. The commit hash and the -ldflags line are.

The Only Published Hash Pair

The v0.15.0 Linux arm64 archive is the one swapped file with published hashes:

  • Swapped: 3833daffcaaaab27e4e0e0833240ffcb1df8df648f4ec0bc4a75c176e28933cb
  • Original: 5c2beb56e6d83a4b426565a1557791ef759316abcc4effb38dd627c90aeb80e0

A build without the version flag would report its version as undefined. Running a suspect binary to find that out is not a good trade.

What to Do If You Ran It

  • OVHcloud founder Octave Klaba's advice on X was direct. Anyone affected should change every secret stored on the disk.
  • The CLI keeps OVHcloud API credentials in ~/.ovh.conf and reads them from environment variables, but the advice covers everything else on that disk, not just those files.
  • OpenSourceMalware, which investigated the incident, added a step that matters. Do the rotation from a different machine.
  • The order is the point. Rotating after you reinstall means the credential that was already copied is still live. Rotate first.
  • Rotate every secret readable from that machine. SSH keys, cloud credentials, API tokens, database passwords, environment files, everything.
  • Remove the binary and install v0.16.0. That release is immutable and carries a release attestation, a signed record of its files.
  • Verify a downloaded archive against the release with gh release verify-asset v0.16.0 FILE, as GitHub documents.
  • Updating replaces the program. It does not revoke a key that was already copied.

Why This Was Possible

Anyone with write access to a GitHub repository can delete files attached to a release and upload new ones with the same names. The download URL doesn't change unless release immutability is enabled.

That setting has been generally available since October 2025. It locks a release's files and tag, but it only protects releases created after it's turned on.

OVHcloud hadn't enabled it.

Klaba's second post on Monday night, quoted by Next, said the problem stemmed from the immutability option not being activated, so a user with the right permissions could modify existing releases directly on GitHub without any checks by the company's CI/CD pipeline. The option is now enabled, and the pipelines themselves weren't affected.

The uploads came through the account of a contributor whose merged work sits in OVHcloud's documentation repository and who isn't a public member of the organization, per OpenSourceMalware's timeline built from GitHub's server-side records.

The release workflow never ran. The run list shows no workflow between October 16 and the v0.16.0 tag push at 16:00 UTC on Monday.

OVHcloud hasn't said how the account's credentials were taken.

The 94 Minutes, Minute by Minute

A customer's CI job caught it, and the reason is worth noting. That job pins the SHA-256 of the Linux arm64 archive, and it started failing.

With no private reporting channel on the repository, the customer filed a public issue at 13:50 UTC, per OpenSourceMalware's timeline. OVHcloud deleted the swapped files about half an hour later, at 14:21 UTC.

v0.16.0 followed at 17:16 UTC, published under the company's ovh-cds account with a signed tag.

A hash-pinned pipeline caught what a hijacked account and a silent archive swap could not. That's the entire detection story in one sentence.

The GlassWorm Question

OpenSourceMalware ties the account to GlassWorm, the campaign that hides code in invisible Unicode characters and fetches its next stage from the memo field of a Solana wallet transaction.

Four of the account's personal repositories were force-pushed on March 5 and 6 with an amended commit that added a loader of that kind. The wallet that loader reads is the one named in GlassWorm's ForceMemo campaign, which force-pushed malware into hundreds of Python repositories in March using stolen GitHub tokens.

The Hacker News confirmed the loader file is still in one of those repositories, with the hash the researchers published.

Whether the same actor did both is, in the researchers' own words, their inference. The shared account is the link. Nothing from the October binary has been tied to GlassWorm, because no copy of it was preserved.

That last fact is the uncomfortable one. OVHcloud hasn't said which files the binary reads, where it sends them, or how many copies were downloaded. No swapped archive has been analyzed in public. A postmortem has been promised.

FAQ

What happened in the OVHcloud CLI compromise?

An attacker used a hijacked contributor account to replace the download archives on five existing releases, v0.11.0 through v0.15.0, with a build designed to steal secrets from the disk of any machine running it. The files were live for up to 94 minutes on October 19, 2026.

Am I affected if I installed OVHcloud CLI before the window?

No, as long as you didn't update during the window. Releases published between April 14 and September 16 are the originals for anyone who installed before 12:47 UTC on Monday and hasn't updated since.

Was Homebrew affected?

No. The Homebrew cask in force during the window pinned the original SHA-256 of each archive, so brew would have refused the swapped download rather than installing it.

How do I check whether my binary is the malicious build?

Run go version -m on the file, which reads it without executing it. The bad build shows a pseudo-version starting v0.15.1-0.20261013083505, the commit 5ead8ef instead of 760fd72, and an -ldflags="-s -w" line that genuine releases don't show.

Does a +dirty marker mean the binary is malicious?

No. OVHcloud's own clean v0.16.0 build reports v0.16.0+dirty and vcs.modified=true. Treating +dirty as the signal would flag the legitimate release.

What should I do first if I ran a swapped build?

Rotate every secret readable from that machine, and do it from a different machine. Install v0.16.0 afterward and verify it with gh release verify-asset. Updating removes the malware but doesn't revoke a credential that was already copied.

Source: The Hacker News
Professional Services

Explore Our Cybersecurity Services

Our insights are backed by hands-on service delivery. If your business needs professional cybersecurity support, our UK-based specialists are ready to help.

© 2016 – 2026 Red Secure Tech Ltd. Registered in England and Wales — Company No: 15581067