Poisoned Pipeline

Poisoned Pipeline / Incidents / AS310

AS310 · 2026-10

GlassWorm-hijacked contributor replaced OVHcloud CLI release assets on five published versions

Git / GitHubGitHub ActionsDeveloper workstationmacOS softwareLinux distro / packagesWindows software

On 19 October 2026, between 12:47 and 13:12 UTC, release binaries of
five already-published versions of the official OVHcloud CLI
(ovh/ovhcloud-cli) were replaced through GitHub's ordinary Releases
API. v0.11.0 through v0.15.0 each lost up to six assets: both macOS
builds, Linux x86_64 and arm64, Windows x86_64, and the checksums
file that vouches for them. The i386 builds, Windows arm64, and the
WASM build were left untouched, which reads as platform selection
aimed at developer and ops workstations rather than a defacement. A
downstream CI job that pins the SHA-256 of
ovhcloud-cli_Linux_arm64.tar.gz failed, and the reporter opened
issue 300 at 13:50 UTC. OVHcloud deleted the replaced assets at
14:21 UTC, about 94 minutes after the first upload, and shipped a
clean v0.16.0 at 17:16 UTC marked immutable with a release
attestation.

The uploads came from the personal account HassenBF, a legitimate
OVH contributor with merged pull requests in ovhcloud-docs and
ovh/manager, Write on the repository, and no public org membership
or commits in ovhcloud-cli itself. No tag was pushed and no
workflow ran: the Actions run list shows nothing between 16 October
and the v0.16.0 tag push, so GoReleaser was never invoked. The
injected binary carries Go build metadata that gives the fake
away - a pseudo-version (v0.15.1-0.20261013083505-5ead8ef9b43d+dirty)
instead of the release tag, vcs.modified=true from uncommitted
changes, and vcs.* lines that GoReleaser's -trimpath would strip.
It is a plain go build of a 13 October commit made somewhere other
than the release pipeline, uploaded one asset at a time with
checksums.txt replaced last. OVHcloud has not published what the
binary reads or where it sends data. Octave Klaba described it as
looking for secrets stored on disk and called its quality mediocre
but functional.

OpenSourceMalware ties the account to GlassWorm. Four of
HassenBF's personal repositories were force-pushed on 5 and
6 March 2026; each new tip is an amend of the previous one that
keeps the original author date and message and adds a preinstall.js
loader (inlined into src/index.js in crwn-clothing). The loader
hides its payload in Unicode variation-selector characters, decrypts
an AES-256-CBC stage, and pulls its next-stage URL from the memo
field of a Solana wallet transaction - a wallet that appears in
public GlassWorm indicator lists. OpenSourceMalware is explicit
that the link between the March account compromise and the October
uploads rests on the shared account identity, and OVHcloud has not
said how the credentials were taken or whether the workflow's
long-lived TAP_GITHUB_TOKEN was exposed. A post-mortem is promised.

Mutable releases and human publishers made the swap cheap. The
project's own GoReleaser config sets replace_existing_artifacts:
true, so replacing assets on a months-old release is documented
behaviour. install.sh and the Homebrew cask in ovh/homebrew-tap
fetched the release URL without independent verification, and the
checksums file was replaced along with the binaries. Anyone who
installed through install.sh, brew install --cask ovh/tap/ovhcloud-cli,
or a direct release download in the window on the mainstream
platforms got the swapped file. go install, source builds, and
OVHcloud's internally built Docker image were not affected. The
v0.16.0 response - immutable release, service-account publisher
(ovh-cds), signed tag, GitHub attestation - closes the mutable
release and personal-publisher rows. Anyone who ran the swapped
binary is told to treat every secret on the machine as exposed.

Mapped techniques

supply-chain Hijacked contributor account

T0113 · Initial Access · Compromised user account

HassenBF, a real OVH contributor, held Write on the repo -
most likely through the @ovh/ovhcloud-cli-owners team that
CODEOWNERS grants on everything. Its public repos were
amended with the loader in March; the earliest server-verified
attacker use of the account is 5 March 2026. How the
credentials were taken is not published.

T0111 · Resource Development · Malicious code contribution to an open-source repository

The amend-and-force-push on four personal repos planted a
preinstall.js variation-selector loader. That is staging on
the hijacked account, not a change to ovh/ovhcloud-cli - the
source repository was never touched.

T0121 · Resource Development · Compromised legitimate artifact

Release assets of v0.11.0 to v0.15.0 were deleted and
re-uploaded through the Releases API, checksums.txt last so
the published hashes matched. Tags still point at the real
commits; the Go module proxy serves clean source.

T0207 · Defense Evasion · Skip or forge artifact integrity verification

install.sh does not verify checksums.txt and the cask trusts
the release URL, so the swapped files passed every automated
path. The one catch came from a downstream CI that hand-pinned
a SHA-256.

post-supply-chain Workstation that fetched a swapped binary

T0159 · Execution · Malicious artifact execution

install.sh, brew upgrade, or a CI/Dockerfile fetch during the
94-minute window ran the dirty build. It lacks the version
ldflag, so ovhcloud version printed a pseudo-version instead
of the tag.

T0184 · Credential Access · Harvesting sensitive information from files

Klaba described the binary as looking for secrets stored on
disk. The replaced assets were deleted before anyone archived
them, so no public sample analysis names paths or exfil
endpoints.

Framework gaps noted

Sources