Dev.to Security 🔐 Cybersecurity 👁 0 📖 4 min read

Ninety-four minutes: how a GitHub release page became a credential thief

Ninety-four minutes: how a GitHub release page became a credential thief, and what pinning hashes actually bought On 19 October 2026, between 12:47 and 14:21 UTC, the download archives for five released versions of the

Ninety-four minutes: how a GitHub release page became a credential thief, and what pinning hashes actually bought

On 19 October 2026, between 12:47 and 14:21 UTC, the download archives for five released versions of the OVHcloud command-line tool were replaced with builds that read credentials from disk. The window lasted 94 minutes. The versions were v0.11.0 through v0.15.0, published between April and September. OVHcloud lists v0.10.0 as unaffected and released v0.16.0 as an immutable release once the problem was contained.
What makes the incident instructive is how little of the normal supply-chain story it follows. The source code was untouched. The release tags were untouched. The project's build pipeline was never involved. The version numbers stayed the same. Only the artefact on disk changed, and so did the checksums file next to it.

What was replaced

For v0.15.0, six files were substituted: the macOS x86_64 and arm64 archives, the Linux x86_64 and arm64 archives, the Windows x86_64 archive, and checksums.txt. The same six files were removed from v0.11.0 through v0.14.0. Linux i386, Windows i386, Windows arm64 and WebAssembly builds were untouched across all five versions.
Replacing the checksums file is the detail that reframes the incident. "Compare the hash against the publish page" is a control that assumes the publish page is trustworthy. Here the page and the payload agreed with each other. The official install.sh never verified a hash in the first place, so anyone using curl | sh had no checkpoint at all.

How it was caught

A customer's CI job pinned the SHA-256 of ovhcloud-cli_Linux_arm64.tar.gz and failed the build when the archive no longer matched. That is the entire detection story: one pipeline that had a hash written into it years earlier, for reasons that probably had nothing to do with threat modelling.
The follow-up analysis used go version -m, which reads build metadata without executing the binary. Three markers distinguished the malicious build from a legitimate one:

  • The module line reported a pseudo-version, v0.15.1-0.20261013083505-5ead8ef9b43d+dirty, generated for a build from an untagged commit on 13 October.
  • The recorded commit was 5ead8ef rather than 760fd72, the commit the v0.15.0 tag points to.
  • A -ldflags="-s -w" line was present. OVHcloud's release pipeline builds with a version flag that Go does not record in that field, so a genuine release shows no -ldflags line at all. There is a trap in that third point. A widely circulated rule of thumb held that any build marked +dirty or carrying a vcs line is malicious. The Hacker News ran the same command against OVHcloud's clean v0.16.0 binary and found it also reports +dirty and vcs.modified=true with its build commit. The discriminator is the specific combination, not the presence of a dirty marker.

What was not affected, and why

Homebrew's cask pinned the original hashes, so brew install and brew upgrade failed with a mismatch rather than installing the replaced archive. go install and source builds resolved to the September tag. The published container image had last been pushed on 21 September, four weeks before the window, and was not rebuilt. Copies installed before 12:47 UTC and not updated during the window were original.
Those exclusions matter for triage. An organisation that uses the tool but installs it through a package manager with hash pinning can reasonably conclude it did not receive the malicious build, provided it can prove the install path.

Why the pipeline was irrelevant

The attacker used a compromised contributor GitHub account with write access to the repository. Any account with write permission can delete the files attached to a release and upload new ones with the same names, and download URLs do not change.
GitHub's release immutability setting, generally available since October 2025, locks a release's files and tags after publication. OVHcloud had not enabled it, and the setting only protects releases created after it is turned on. OVHcloud's founder stated that the root cause was the missing immutability option, that a user with the relevant permission could modify an existing release directly on GitHub without any check from the CI/CD pipeline, and that the option is now enabled.
The pipeline was never bypassed because it was never on the path. That distinction is the transferable lesson.

Response, in the order that matters

Rotate first. Removing the binary does not revoke credentials that were already read. The affected machine should be treated as having had every readable secret exposed: the ~/.ovh.conf API credentials the CLI documents, environment variables, SSH private keys, ~/.kube/config, .npmrc, .netrc and environment files. OVHcloud's founder advised changing every key stored on the disk.
Then remove the binary and install v0.16.0, which is immutable and carries a release attestation. GitHub's gh release verify-asset command checks a downloaded archive against that attestation.
Then investigate from another machine, since the compromised host is not a trustworthy place to inspect itself.

What to change in the supply-chain checklist

The narrow lesson is to pin hashes in CI, which one customer already did and which is why we know about this at all.
The broader lesson concerns coverage claims. "We hardened the pipeline" describes the set of artefacts that pass through the pipeline. It says nothing about an artefact that is replaced after the pipeline finished. Distribution-path integrity, release immutability and artefact signatures cover a different part of the same risk, and an organisation that has done one of the three should not describe the result as supply-chain assurance.
A third lesson is about provenance questions that a version number cannot answer. For anything that sits on a build agent, the useful inventory entry is not "OVHcloud CLI v0.15.0" but "installed from the GitHub release archive on via , hash pinned: yes/no".

References

  • OVHcloud advisory and v0.16.0 release notes, 19 October 2026
  • The Hacker News, "OVHcloud CLI Release Hijacked to Deliver Credential Stealer", October 2026
  • OpenSourceMalware timeline of the release modifications
  • GitHub documentation on release immutability and release attestations
📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.