OVHcloud CLI Release-Asset Backdoor (v0.11.0 to v0.15.0)

A hijacked contributor GitHub account replaced the binaries on five existing releases of the OVHcloud CLI with a secret-stealing build on 2026-10-19. Built on OVHcloud's advisory, the reporter's issue and three independent write-ups.
Threat
OVHcloud CLI release-asset replacement
Severity
HIGH · SUPPLY CHAIN · SECRET THEFT
Type
Supply-chain compromise, credential stealer
Access Vector
Hijacked GitHub account, mutable releases
Version
v0.1 (Draft) · 2026-10-24
Author
HuntPack
Confidence
High on timeline and hashes; attribution unconfirmed
01

Executive Summary

Attacker objective. Steal developer and operator secrets. On 2026-10-19 someone using a legitimate OVHcloud contributor's GitHub account swapped the prebuilt archives on five existing releases of the OVHcloud CLI (v0.11.0 to v0.15.0) with a build that reads credentials and files from disk. [#1] [#2] [#4]

How it worked. The uploads came through the ordinary GitHub Releases API. No tag was pushed, no workflow ran, and the source and build pipeline were untouched. OVHcloud's own root cause is that release immutability was not enabled, so a user with write permission could overwrite assets on published releases. [#1] [#3] [#5] The bad files covered macOS (Intel and ARM), Linux (x86_64 and arm64) and Windows x86_64, and were live for about 94 minutes (14:47 to 16:21 Paris time, 12:47 to 14:21 UTC) before OVHcloud deleted them after a customer's pinned-hash CI check failed. [#1] [#2] [#3]

What the binary does. It is the real CLI recompiled with extra Go code. Per OVHcloud it collects IDE and AI-assistant databases, cloud, container and Terraform credentials, SSH keys, .netrc and git config into a folder named envchecker-NNNNNNNNN in the temp directory, archives it, and uploads it to a hard-coded address, using Solana RPC calls through one of five public endpoints. [#1] The advisory reports about 250 downloads. [#1]

Attribution is open. One analysis ties the hijacked account to the GlassWorm campaign through an earlier loader in the account's personal repositories and calls the link an inference. Another report says nothing from the October binary has been tied to GlassWorm because no copy was preserved, and the binary's Solana wallet in the advisory differs from the wallet in that earlier loader. [#3] [#4] [#1] This pack does not assert the attribution and ships none of that campaign's indicators.

Defender priority: find every host or CI runner that downloaded or ran an ovhcloud binary on 2026-10-19 between 12:47 and 14:21 UTC (Q7, Q8, Q1). For any hit, treat every secret readable on that machine as stolen and rotate it from a different machine. Then require immutable releases and pinned hashes for every tool you pull from GitHub Releases.

02

Source Review & Web Hunter Notes

#TierSourceKey findingCarry forward
1T1OVHcloud GitHub security advisory GHSA-862f-55f5-vmvw (published 2026-10-20)Timeline, affected versions v0.11.0 to v0.15.0, five malicious-binary MD5s, exfiltration address, Solana RPC endpoints, targeted file list, root cause.yes
2T1GitHub issue #300 on ovh/ovhcloud-cli (reporter alink-tothepast, 2026-10-19)First-hand capture: replaced asset names and times, original and swapped SHA-256 of the v0.15.0 Linux arm64 archive, go version -m build metadata.yes
3T2OpenSourceMalware: OVHcloud CLI supply chain compromise (2026-10-20)Server-side timeline, what was replaced, how to check a binary, account-compromise evidence. Did not analyze the payload. GlassWorm link is the authors' inference.yes, attribution only as unconfirmed
4T2The Hacker News: Hackers Hijack Five OVHcloud CLI Releases (2026-10-20)Independent check of exposure paths. Says the Homebrew cask pinned original hashes so brew installs would have failed, and warns that a +dirty or vcs flag alone also appears on OVHcloud's clean v0.16.0.yes
5T2Help Net Security: Info-stealing malware slipped into OVHcloud CLI releases (2026-10-21)Confirms versions, times, quotes from the OVHcloud CEO, immutability root cause.corroboration only
xn/aBleepingComputer article on the incidentFetch returned HTTP 403. Not read, not cited, nothing taken from it.no

Decisions

  • Hash roles corrected. The brief that started this pack described 5c2beb56…aeb80e0 as a partial malicious hash. Source 2 gives it in full and labels it the original (legitimate) v0.15.0 Linux arm64 archive. The swapped (malicious) archive is the 3833daff value. The legitimate hash is kept out of the IOC import.
  • Related-cluster indicators excluded. The advisory root-cause section and the OpenSourceMalware report list a wallet, a loader file hash, a key and IV, an address and an extension from the account's earlier compromise. They belong to a different incident and are not shipped. The wallet in the advisory's own IOC list (the binary's wallet) is shipped as a hunt string.
  • Time zones. OVHcloud's 14:47 to 16:21 is Paris time (UTC+2), equal to 12:47 to 14:21 UTC. OpenSourceMalware says 94 minutes, the advisory says 1h33.
  • Version detail. The reporter's build info is from the Linux arm64 binary and the advisory's from a Linux x86_64 build. Both show the same pseudo-version and commit, so those two strings are the signal. The +dirty flag is not, because the clean v0.16.0 build also reports it [#4].
03

Hunt Brief & Attack Chain

Seven hypotheses, ordered by fidelity.

  1. H1 (high): a host executed one of the five replaced binaries. Event: process start with MD5/SHA-256 (Q1, Q7). T1195.002. FP: none.
  2. H2 (high): a host connected to the advisory's upload address (Q2). T1041. FP: none, but the address may be changed by the operator.
  3. H3 (high): an ovhcloud process resolved a Solana RPC hostname (Q3, Q4). T1102. FP: blockchain tooling for Q4.
  4. H4 (medium): ovhcloud spawned shells, archivers or credential-store CLIs (Q5). T1119, T1552.001. FP: helper launches.
  5. H5 (medium): the CLI was fetched during 12:47 to 14:21 UTC on 2026-10-19 (Q8). T1195.002. FP: normal installs outside the window.
  6. H6 (low): a command line names an envchecker temp folder (Q6). T1119. Rarely visible, use N1.
  7. H7 (low): ovhcloud reached an unexpected public address (Q9). T1041. FP: OVHcloud API addresses.

Attack chain

StepBehaviorTelemetryHunt angleQueries
1Attacker holds a contributor's GitHub credentials (account compromised months earlier per OVHcloud and OpenSourceMalware)GitHub audit log (outside endpoint telemetry)Org audit log reviewN4
2Malicious binary built outside CI and uploaded over five existing releases via the Releases APIGitHub release records and server-side activity (outside endpoint telemetry)Compare asset uploader and times; verify attestationN4, N5
3Victim installs or updates the CLI: install.sh, direct download, CI fetch, self-upgradeProcess creation with command line, image path and parent (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents)Q8, Q7Q8, Q7
4Swapped binary runs; stealer code collects IDE databases, cloud and SSH credentials into a temp folderProcess creation with hashes (CrowdStrike: ProcessRollup2, MD5HashData and SHA256HashData. SentinelOne: Process Creation. Defender: DeviceProcessEvents); file reads are not loggedQ1, Q5, Q6, N1Q1, Q5, Q6
5Stealer resolves a Solana RPC endpoint and uploads the archive over HTTPSDNS lookup with requesting process (CrowdStrike: DnsRequest. SentinelOne: DNS Resolved. Defender: DeviceEvents DnsQueryResponse) and outbound network connection (CrowdStrike: NetworkConnectIP4. SentinelOne: IP Connect. Defender: DeviceNetworkEvents)Q2, Q3, Q4, Q9Q2, Q3, Q4, Q9

Not telemetry-visible: steps 1 and 2 happen in GitHub, and the stealer's reads of credential files and its creation of the temp folder are in-process Go file operations that the sensor does not log. They are flagged as gaps in Section 15.

04

Consolidated IOC Table

Hashes of OVHcloud's legitimate builds are listed for reference only and must never be blocked. Solana RPC hostnames are public services shared with legitimate users: hunt, never block.

TypeValueConfidenceActionContext
MD56f2f086239f3d88eb2750e871901927dhighdetectMalicious ovhcloud (Darwin_arm64 binary) per OVHcloud advisory [#1]. Single source.
MD5347e65eb7f2d78d1c6872690272a6cc3highdetectMalicious ovhcloud (Darwin_x86_64 binary) per OVHcloud advisory [#1]. Single source.
MD501f57206d37c28cf711677f612052913highdetectMalicious ovhcloud (Linux_arm64 binary) per OVHcloud advisory [#1]. Single source.
MD5301bff0b51980b9e76c93633e7cfd3bdhighdetectMalicious ovhcloud (Linux_x86_64 binary) per OVHcloud advisory [#1]. Single source.
MD56a3cf153338c416169cda2fe704c8848highdetectMalicious ovhcloud.exe (Windows x86_64 binary) per OVHcloud advisory [#1]. Single source.
SHA-2563833daffcaaaab27e4e0e0833240ffcb1df8df648f4ec0bc4a75c176e28933cbhighpreventSwapped v0.15.0 ovhcloud-cli_Linux_arm64.tar.gz [#2] [#3] [#4]. Archive, so prevention has no execution effect; it flags the file.
SHA-2565c2beb56e6d83a4b426565a1557791ef759316abcc4effb38dd627c90aeb80e0highdo not blockLEGITIMATE original v0.15.0 Linux arm64 archive [#2] [#3] [#4]. Known-good reference only.
SHA-256e9c31dca232cdaa8058cb59eadf425c182a23afb6ddb057eb92eda13b850dff4mediumdetectSample linked by the advisory on VirusTotal [#1]. The advisory does not say whether it is a binary or an archive. Single source.
SHA-2563c10bd782cd0dc5f720a33570af4651814cb82917e54d27ffce3849a3c5cef2amediumdetectSample linked by the advisory on VirusTotal [#1]. The advisory does not say whether it is a binary or an archive. Single source.
SHA-2563cbe1ac592beeca7ce0ad0ba945349a9ef33ef0f1324935c3a115f30b1d0c927mediumdetectSample linked by the advisory on VirusTotal [#1]. The advisory does not say whether it is a binary or an archive. Single source.
SHA-25625c68c5e9ae23883aebda59f4b6a7df78e1ce8aae5e4b4ba636419497d54bbecmediumdetectSample linked by the advisory on VirusTotal [#1]. The advisory does not say whether it is a binary or an archive. Single source.
SHA-256f97284530343b3903ffd19bbe70ab6f62e36f04eb7e5f885934bd21d10899e8amediumdetectSample linked by the advisory on VirusTotal [#1]. The advisory does not say whether it is a binary or an archive. Single source.
IPv445.32.148.174highdetectStealer upload endpoint, TCP/443, path /upload [#1]. Advisory says it can change at any time. Single source.
Solana walletBHaDaVbdCYFNaKZGn7yLXFneWoJHZgLiBYkHxVrjyALFhighhuntWallet used by the October binary for its RPC calls [#1]. Not importable to Falcon; search proxy and DNS-adjacent logs.
Domainsolana.drpc.orglowhunt (do not block)Public Solana RPC endpoint the stealer picks from [#1]. Legitimate shared service.
Domainsolana-rpc.publicnode.comlowhunt (do not block)Public Solana RPC endpoint the stealer picks from [#1]. Legitimate shared service.
Domainsolana.api.pocket.networklowhunt (do not block)Public Solana RPC endpoint the stealer picks from [#1]. Legitimate shared service.
Domainapi.mainnet-beta.solana.comlowhunt (do not block)Public Solana RPC endpoint the stealer picks from [#1]. Legitimate shared service.
Domainsolana.api.onfinality.iolowhunt (do not block)Public Solana RPC endpoint the stealer picks from [#1]. Legitimate shared service.
Folderenvchecker-[0-9]{9,10} in the temp directoryhighhuntStealer working folder, never deleted [#1]. Q6, N1.
Build stringv0.15.1-0.20261013083505-5ead8ef9b43d+dirtyhighhuntGo module pseudo-version in the malicious build [#1] [#2] [#3]. A clean v0.15.0 reports v0.15.0. Do not key on +dirty alone [#4].
Commit5ead8ef9b43d354dfb5caadff803b11afc8c5c58highhuntvcs.revision of the malicious build [#1] [#2]. The real v0.15.0 tag points to 760fd72de93cbea3b507eb36dc0095991e4048e5 [#2].
Filenameovhcloud / ovhcloud.exelowhuntName of the replaced binaries. Also the legitimate name, so name alone proves nothing.
GitHub accountHassenBFmediumenrichAccount that uploaded the assets [#2] [#3]. Context for audit-log review. A victim identity, not an attacker handle.
05

Affected Surface & Telemetry Matrix

SurfaceTelemetryPriorityGap risk
Developer workstations (macOS, Linux, Windows) that installed or upgraded the CLIProcess creation with command line, hashes and parent (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents)HighReads of credential files and the temp-folder creation are in-process and not logged, so a host that ran the binary shows only the execution
CI runners and build containers that fetch the release tarballProcess creation with command line (as above); outbound connection (CrowdStrike: NetworkConnectIP4. SentinelOne: IP Connect. Defender: DeviceNetworkEvents)HighEphemeral runners may have no sensor; use build logs (N2)
Network egress to the upload address and Solana RPC endpointsDNS lookup with requesting process (CrowdStrike: DnsRequest. SentinelOne: DNS Resolved. Defender: DeviceEvents)MediumDefender has no endpoint DNS table; use proxy and resolver logs (N3)
GitHub organisation release and membership activityGitHub audit logMediumOutside endpoint telemetry
Secrets the CLI could read (cloud, SSH, git, IDE, AI-assistant data)Identity provider and cloud audit logs after rotationHighOutside endpoint telemetry; watch for use of rotated or old keys
06

ATT&CK Mapping

TacticTechniqueObserved behaviorQuery or control
Initial AccessT1195.002 Compromise Software Supply ChainRelease assets replaced on legitimate releasesQ1, Q7, Q8; Hardening T1
Initial AccessT1078 Valid AccountsHijacked contributor GitHub account used to uploadN4; Hardening T2
ExecutionT1204.002 User ExecutionVictim runs the trojanised CLIQ1, Q7
Defense EvasionT1036 MasqueradingBinary carries the legitimate name and release versionQ7, N5
CollectionT1119 Automated Collection, T1005 Data from Local SystemCopies IDE, AI-assistant, cloud, SSH and git data into the temp folderQ5, Q6, N1
Credential AccessT1552.001 Credentials In Files, T1552.004 Private KeysReads ~/.aws, ~/.kube, ~/.ssh, ~/.netrc, Terraform and cloud CLI filesHardening T4
CollectionT1560 Archive Collected DataArchives the working folder before uploadQ5, Q6
Command and ControlT1102 Web ServiceSolana RPC endpoints used, chosen at randomQ3, Q4
ExfiltrationT1041 Exfiltration Over C2 ChannelArchive uploaded to a hard-coded address over HTTPSQ2, Q9
07

Native Audit-Log Hunts

These checks need no Falcon query.

N1 · Endpoint triage: stealer working folder

Where: Developer workstation or runner

# macOS / Linux (read-only)
ls -ld "${TMPDIR:-/tmp}"/envchecker-* 2>/dev/null
# Windows PowerShell (read-only)
Get-ChildItem -Path $env:TEMP -Directory -Filter "envchecker-*" -ErrorAction SilentlyContinue

The advisory says the folder is created on every run and never deleted. A match means the swapped binary ran on this machine. Absence is not proof: the tool may have run before the folder was cleaned.

N2 · CI and build logs: release downloads in the window

Where: CI system, container build logs, package mirror logs

# Search for the window 2026-10-19 12:47 to 14:21 UTC (14:47 to 16:21 Paris)
# Strings: ovhcloud-cli, releases/download, install.sh, ovhcloud-cli_Linux_x86_64.tar.gz
# Any job that fetched an archive without a pinned hash in that window is exposed

Pinned-hash jobs failed and are safe. Unpinned jobs, Dockerfiles and the CLI's own upgrade command pulled the swapped file.

N3 · Proxy, firewall and resolver logs

Where: Egress logging

# Destination 45.32.148.174 port 443 (path /upload)
# DNS or SNI: solana.drpc.org, solana-rpc.publicnode.com, solana.api.pocket.network,
#             api.mainnet-beta.solana.com, solana.api.onfinality.io
# Hits from build hosts with no blockchain role are suspicious

Covers hosts and runners with no sensor.

N4 · GitHub organisation audit log

Where: GitHub org settings, audit log

# Review release asset delete and upload events on every repo, by user
# Flag assets changed on releases older than 24 hours
# List members with Write on release-publishing repos; check for personal accounts
# Confirm immutable releases is enabled org-wide

This is how the swap could be spotted at the source. Event names vary by plan; check your own audit log schema.

N5 · Check a binary without running it

Where: Any host that kept a copy

go version -m ./ovhcloud
# Malicious: mod ... v0.15.1-0.20261013083505-5ead8ef9b43d+dirty and vcs.revision=5ead8ef9b43d354dfb5caadff803b11afc8c5c58
# Do NOT treat vcs.modified=true alone as malicious: the clean v0.16.0 build also reports it
md5sum ./ovhcloud        # compare with the five MD5 values in Section 10
shasum -a 256 ovhcloud-cli_*.tar.gz

Never run the binary to check it.

08

Hunt Queries: CrowdStrike CQL, SentinelOne PowerQuery and Defender XDR KQL

Show all queries in
Pick your tenant's cloud first — every "Open in Falcon" button below uses this selection.

All nine queries run on Falcon sensor telemetry and work on Windows, macOS and Linux hosts. For Q1, Q7 and Q8 set the time picker to cover 2026-10-19 onward, because the exposure window is a single afternoon and Q7 should also show what is installed now. Q3 relies on process context on DNS events; check it in your tenant.

Q1 · Known-bad OVHcloud CLI hashes: five replaced binaries and the swapped Linux arm64 archive
CONF highFP lowCOST low

Looks for: any process start or written executable whose MD5 matches the five malicious binaries listed in the OVHcloud advisory, or whose SHA-256 matches a malicious sample the reporting names (the swapped v0.15.0 Linux arm64 archive and the five advisory sample links). FP: none expected, these are hashes of the replaced files. The known-good original archive hash is deliberately not in this query.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptions, telemetry-gapProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: A file event the sensor did not hash is missed; the hash fields can be empty for large or in-flight files.
  • PowerQuery assumes: The process and file hash fields are populated on this tenant's events.
  • PowerQuery assumes: File Creation events stand in for the sensor's PE-written and new-executable-written events and also cover non-PE files.
  • KQL – Defender XDR: Files written to disk but never executed are not covered; run the same hash lists against DeviceFileEvents.
  • KQL – Defender XDR assumes: MD5 is populated in DeviceProcessEvents; SHA256 is often empty, so MD5 carries the match on most tenants.
// HUNT: OVHcloud CLI replaced release binaries by hash
// MITRE: T1195.002, T1204.002
// CONF: high
// FP: low
// COST: low
// REQUIRES: Falcon sensor process and Windows file-write telemetry with MD5HashData/SHA256HashData
// FALSE POSITIVES: none expected, hashes belong to the replaced files
// LOOKBACK: 7d
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2|PeFileWritten|NewExecutableWritten)$/
| (MD5HashData=/^(6f2f086239f3d88eb2750e871901927d|347e65eb7f2d78d1c6872690272a6cc3|01f57206d37c28cf711677f612052913|301bff0b51980b9e76c93633e7cfd3bd|6a3cf153338c416169cda2fe704c8848)$/i or SHA256HashData=/^(e9c31dca232cdaa8058cb59eadf425c182a23afb6ddb057eb92eda13b850dff4|3c10bd782cd0dc5f720a33570af4651814cb82917e54d27ffce3849a3c5cef2a|3cbe1ac592beeca7ce0ad0ba945349a9ef33ef0f1324935c3a115f30b1d0c927|25c68c5e9ae23883aebda59f4b6a7df78e1ce8aae5e4b4ba636419497d54bbec|f97284530343b3903ffd19bbe70ab6f62e36f04eb7e5f885934bd21d10899e8a|3833daffcaaaab27e4e0e0833240ffcb1df8df648f4ec0bc4a75c176e28933cb)$/i)
| table([@timestamp, #event_simpleName, aid, ComputerName, UserName, FileName, ImageFileName, MD5HashData, SHA256HashData, ParentBaseFileName, CommandLine])
| sort(@timestamp, order=desc)
Q2 · Connection to the exfiltration address
CONF highFP lowCOST low

Looks for: an outbound connection to the address the advisory names as the upload endpoint (TCP/443, path /upload). The advisory warns the URL is time-sensitive and the operator can change it, so a miss here does not clear a host. FP: none expected.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: Connections made before the sensor was installed, or by a host without network telemetry, are not captured.
  • PowerQuery assumes: src.process.name names the connecting process on IP Connect events.
  • KQL – Defender XDR: Connections the sensor did not record are not captured.
  • KQL – Defender XDR assumes: RemoteIP holds the destination address for outbound connections.
// HUNT: Outbound connection to the OVHcloud CLI stealer upload address
// MITRE: T1041
// CONF: high
// FP: low
// COST: low
// REQUIRES: Falcon sensor network telemetry
// FALSE POSITIVES: none expected
// LOOKBACK: 7d
#event_simpleName=NetworkConnectIP4
| RemoteAddressIP4="45.32.148.174"
| table([@timestamp, aid, ComputerName, ContextBaseFileName, RemoteAddressIP4, RemotePort, LocalAddressIP4])
| sort(@timestamp, order=desc)
Q3 · OVHcloud CLI process resolving a Solana RPC endpoint
CONF highFP lowCOST low

Looks for: a DNS lookup of one of the four Solana RPC hostnames from a process named ovhcloud. The advisory says the stealer picks one of its listed endpoints at random on each run. A cloud-management CLI has no reason to look these up. FP: none expected. ⚠ validate in tenant: if ContextBaseFileName is empty on DnsRequest events for your sensor version, rely on Q4. The advisory writes its fifth endpoint with a path (/public); only the hostname part can appear in DNS, and it is included.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: A resolution made through a proxy or a DNS-over-HTTPS client is not recorded as a DNS Resolved event.
  • PowerQuery assumes: event.dns.request is the populated DNS name field on this tenant; some schema versions use dns.request.
  • PowerQuery assumes: src.process.name names the resolving process on DNS Resolved events.
  • PowerQuery assumes: The hyphenated hostnames are written with a [-] class; the literal check reads their tails rpc.publicnode.com and beta.solana.com as separate names, so they are listed here.
  • KQL – Defender XDR: Tenants that do not record DnsQueryResponse return nothing here, which reads as a clean estate.
  • KQL – Defender XDR assumes: DnsQueryResponse is populated on this tenant and AdditionalFields carries DnsQueryString or QueryName.
  • KQL – Defender XDR assumes: InitiatingProcessFileName is the resolving process.
  • KQL – Defender XDR assumes: The hyphenated hostnames are written with a [-] class; the literal check reads their tails rpc.publicnode.com and beta.solana.com as separate names, so they are listed here.
// HUNT: ovhcloud process resolving Solana RPC hostnames
// MITRE: T1102, T1041
// CONF: high
// FP: low
// COST: low
// REQUIRES: Falcon sensor DNS telemetry with process context (⚠ validate in tenant: ContextBaseFileName on DnsRequest)
// FALSE POSITIVES: none expected
// LOOKBACK: 7d
#event_simpleName=DnsRequest
| ContextBaseFileName=/^ovhcloud(\.exe)?$/i
| DomainName=/^(solana\.drpc\.org|solana\-rpc\.publicnode\.com|solana\.api\.pocket\.network|api\.mainnet\-beta\.solana\.com|solana\.api\.onfinality\.io)$/i
| table([@timestamp, aid, ComputerName, ContextBaseFileName, DomainName])
| sort(@timestamp, order=desc)
Q4 · Solana RPC lookups from any process on non-blockchain hosts
CONF mediumFP mediumCOST low

Looks for: the same five Solana RPC hostnames resolved by any process, grouped by host and process. Use it where Q3 is blind and to find the stealer if it was renamed. FP: blockchain developers, wallets and node operators resolve these names legitimately. These are public services and are intentionally not in the IOC import.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: A resolution made through a proxy or a DNS-over-HTTPS client is not recorded as a DNS Resolved event.
  • PowerQuery assumes: event.dns.request is the populated DNS name field on this tenant; some schema versions use dns.request.
  • PowerQuery assumes: src.process.name names the resolving process on DNS Resolved events.
  • PowerQuery assumes: The hyphenated hostnames are written with a [-] class; the literal check reads their tails rpc.publicnode.com and beta.solana.com as separate names, so they are listed here.
  • KQL – Defender XDR: Tenants that do not record DnsQueryResponse return nothing here, which reads as a clean estate.
  • KQL – Defender XDR assumes: DnsQueryResponse is populated on this tenant and AdditionalFields carries DnsQueryString or QueryName.
  • KQL – Defender XDR assumes: The hyphenated hostnames are written with a [-] class; the literal check reads their tails rpc.publicnode.com and beta.solana.com as separate names, so they are listed here.
// HUNT: Solana RPC hostname lookups by any process
// MITRE: T1102
// CONF: medium
// FP: medium
// COST: low
// REQUIRES: Falcon sensor DNS telemetry
// FALSE POSITIVES: crypto wallets, blockchain developer tooling, Solana node or explorer apps
// TUNING: exclude known wallet/dev hosts with a lookup table of ComputerName or aid; alert only on hosts with no blockchain role (build runners, servers, CI agents)
// LOOKBACK: 7d
#event_simpleName=DnsRequest
| DomainName=/^(solana\.drpc\.org|solana\-rpc\.publicnode\.com|solana\.api\.pocket\.network|api\.mainnet\-beta\.solana\.com|solana\.api\.onfinality\.io)$/i
| groupBy([ComputerName, aid, ContextBaseFileName, DomainName], function=[count(as=lookups), min(@timestamp, as=first_seen), max(@timestamp, as=last_seen)], limit=1000)
| sort(lookups, order=desc)
| table([ComputerName, aid, ContextBaseFileName, DomainName, lookups, first_seen, last_seen])
Q5 · ovhcloud spawning shells or credential-tool children
CONF mediumFP mediumCOST low

Looks for: a process named ovhcloud launching a shell, archiver or a cloud, container or secret-store CLI. The advisory says the stealer prepares credential files by running various commands, but does not list them, so the child list is an informed guess. FP: legitimate subcommands may launch helper programs; baseline first.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: A child launched through a process that is not named ovhcloud, such as a renamed copy, is not captured.
  • PowerQuery assumes: src.process.user is the user of the creating process, which matches the CQL's user on a single-user launch.
  • KQL – Defender XDR: A child launched through a process that is not named ovhcloud is not captured.
  • KQL – Defender XDR assumes: The regexes are anchored on the file name only, so a renamed copy of the CLI is not matched.
// HUNT: ovhcloud spawning shell or credential-collection children
// MITRE: T1119, T1552.001, T1059
// CONF: medium
// FP: medium
// COST: low
// REQUIRES: Falcon sensor process telemetry with parent process
// FALSE POSITIVES: CLI subcommands that shell out (ssh helpers, browser launchers), wrapper scripts
// TUNING: exclude parent command lines of sanctioned wrapper scripts after a 14-day baseline; keep the shell and archiver children
// LOOKBACK: 7d
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| ParentBaseFileName=/^ovhcloud(\.exe)?$/i
| FileName=/^(sh|bash|zsh|dash|cmd|powershell|pwsh|security|aws|gcloud|az|doctl|kubectl|docker|git|npm|terraform|tar|zip|curl|wget|sqlite3|osascript|7z|7za)(\.exe)?$/i
| table([@timestamp, aid, ComputerName, UserName, ParentBaseFileName, FileName, CommandLine])
| sort(@timestamp, order=desc)
Q6 · Process command line naming the stealer working folder
CONF lowFP lowCOST low

Looks for: any process command line containing the temporary working folder name the advisory documents (envchecker followed by 9 or 10 digits). The stealer writes this folder from inside the Go process, so endpoint process telemetry is unlikely to see its creation. This query only catches follow-on tooling or an analyst command. The native check in Section 7 (N1) is the primary test. FP: none expected.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: The folder name is only seen when it appears on a created process's command line; file activity inside it is not covered.
  • PowerQuery assumes: src.process.user is the user of the creating process.
  • KQL – Defender XDR: The folder name is only seen on a created process's command line; file activity inside it is not covered.
  • KQL – Defender XDR assumes: ProcessCommandLine holds the full command line of the created process.
// HUNT: Command line referencing an envchecker-NNNNNNNNN folder
// MITRE: T1119, T1560
// CONF: low
// FP: low
// COST: low
// REQUIRES: Falcon sensor process telemetry
// FALSE POSITIVES: none expected
// LOOKBACK: 7d
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| CommandLine=/envchecker-[0-9]{9,10}/i
| table([@timestamp, aid, ComputerName, UserName, FileName, ParentBaseFileName, CommandLine])
| sort(@timestamp, order=desc)
Q7 · Exposure inventory: every host that ran an ovhcloud binary, with its hashes
CONF highFP lowCOST low

Looks for: every host that ran a binary named ovhcloud, with image path and hashes. Compare MD5HashData to the five advisory values (Q1 does this automatically) and review any host that ran the CLI on or after 2026-10-19 12:47 UTC (14:47 Paris) and before the files were deleted at 14:21 UTC. A binary built by OVHcloud is not the same file as the swapped one, so group by hash. FP: none, this is an inventory.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: A copy of the CLI renamed to something else is not inventoried.
  • PowerQuery assumes: tgt.process.image.md5 and tgt.process.image.sha256 are populated on this tenant's Process Creation events.
  • KQL – Defender XDR: A copy of the CLI renamed to something else is not inventoried.
  • KQL – Defender XDR assumes: MD5 is populated in DeviceProcessEvents; SHA256 is often empty.
// HUNT: ovhcloud execution inventory with hashes
// MITRE: T1195.002, T1204.002
// CONF: high
// FP: low
// COST: low
// REQUIRES: Falcon sensor process telemetry
// FALSE POSITIVES: none, inventory query
// LOOKBACK: 7d
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| FileName=/^ovhcloud(\.exe)?$/i
| groupBy([ComputerName, aid, ImageFileName, MD5HashData, SHA256HashData], function=[count(as=runs), min(@timestamp, as=first_seen), max(@timestamp, as=last_seen)], limit=1000)
| sort(last_seen, order=desc)
| table([ComputerName, aid, ImageFileName, MD5HashData, SHA256HashData, runs, first_seen, last_seen])
Q8 · Release-asset download or install-script run for the OVHcloud CLI
CONF mediumFP mediumCOST low

Looks for: curl, wget, a shell, brew or PowerShell command lines that fetch an ovhcloud-cli release archive or run the project install script. Run it over 2026-10-19 12:47 to 14:21 UTC to find hosts that pulled a swapped file. The reporting disagrees on Homebrew: one analysis lists it as exposed, another says the cask pinned the original hashes so the install would have failed. Treat brew hits as an inventory item, not proof of exposure. FP: normal installs and CI jobs outside the window.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: A download made by a process outside the listed names, such as a browser, is not captured.
  • PowerQuery assumes: src.process.user is the user of the creating process.
  • PowerQuery assumes: The unescaped dots in releases.download and ovh.tap.ovhcloud-cli match any single character, as in the CQL.
  • KQL – Defender XDR: A download made by a process outside the listed names, such as a browser, is not captured.
  • KQL – Defender XDR assumes: The unescaped dots in releases.download and ovh.tap.ovhcloud-cli match any single character, as in the CQL.
// HUNT: OVHcloud CLI release archive download or install.sh run
// MITRE: T1195.002, T1105
// CONF: medium
// FP: medium
// COST: low
// REQUIRES: Falcon sensor process telemetry
// FALSE POSITIVES: legitimate installs and CI jobs outside 2026-10-19 12:47-14:21 UTC
// TUNING: restrict the time picker to 2026-10-19 12:47 to 14:21 UTC for exposure scoping; outside that window these are normal installs
// LOOKBACK: 7d
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| CommandLine=/ovhcloud-cli/i
| CommandLine=/(releases.download|install\.sh|ovh.tap.ovhcloud-cli|ovhcloud-cli_(Linux|Darwin|Windows)_(x86_64|arm64)\.(tar\.gz|zip))/i
| FileName=/^(curl|wget|sh|bash|zsh|dash|brew|powershell|pwsh|python3?|ruby|ovhcloud)(\.exe)?$/i
| table([@timestamp, aid, ComputerName, UserName, ParentBaseFileName, FileName, CommandLine])
| sort(@timestamp, order=desc)
Q9 · Non-internal destinations contacted by the ovhcloud process
CONF lowFP mediumCOST low

Looks for: every public address an ovhcloud process connects to, grouped by host. The real CLI talks to OVHcloud API endpoints, so the baseline is small and anything else stands out, including the upload address in Q2. FP: OVHcloud API and update endpoints, which change over time.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: Connections through a proxy appear as the proxy address only.
  • PowerQuery assumes: src.process.name names the connecting process on IP Connect events.
  • PowerQuery assumes: dst.ip.address holds an IPv4 address, as the CQL's IPv4-only connection event does.
  • KQL – Defender XDR: Connections through a proxy appear as the proxy address only.
  • KQL – Defender XDR assumes: RemoteIP holds an IPv4 address; IPv6 destinations evaluate to null in the range test and are dropped, as the CQL's IPv4-only event does.
// HUNT: Public destinations contacted by ovhcloud
// MITRE: T1041, T1071.001
// CONF: low
// FP: medium
// COST: low
// REQUIRES: Falcon sensor network telemetry with process name
// FALSE POSITIVES: OVHcloud API endpoints, proxies, CDN addresses used by the real CLI
// TUNING: baseline 14 days of ovhcloud egress, then exclude the stable OVHcloud API addresses and your proxy; review what is left
// LOOKBACK: 7d
#event_simpleName=NetworkConnectIP4
| ContextBaseFileName=/^ovhcloud(\.exe)?$/i
| !cidr(RemoteAddressIP4, subnet=["10.0.0.0/8","172.16.0.0/12","192.168.0.0/16","127.0.0.0/8"])
| groupBy([ComputerName, aid, RemoteAddressIP4, RemotePort], function=[count(as=conns), min(@timestamp, as=first_seen), max(@timestamp, as=last_seen)], limit=1000)
| sort(conns, order=desc)
| table([ComputerName, aid, RemoteAddressIP4, RemotePort, conns, first_seen, last_seen])
09

Detection Rules by Platform

CrowdStrike: Custom IOA

IOA nameRule typePatternExclusionsPromotesDeployment path
ovhcloud resolving Solana RPCDomain Name / DNS requestProcess image name ovhcloud or ovhcloud.exe; domain matches the Solana RPC hostnames in Q3NonePromote Q3Detect for 7 days, then Prevent; the CLI has no legitimate use
ovhcloud spawning shells or credential toolsProcess CreationParent image ovhcloud or ovhcloud.exe; child image a shell, archiver or cloud/secret CLI from Q5Sanctioned wrapper scripts after baselinePromote Q5Detect for 14 days, then decide

Hash matches (Q1) and the upload address (Q2) belong in Falcon IOC Management. Use the CSV in Section 10. Solana RPC hostnames are shared public services and stay out of IOC Management.

Alert package: Q3

Q3: ovhcloud process resolved a Solana RPC endpoint

Subject: [HIGH] Detection: OVHcloud CLI compromised build, Solana RPC lookup from ovhcloud

A process named ovhcloud looked up a Solana RPC hostname. The OVHcloud advisory lists these as the stealer's exfiltration helpers. Treat the host as holding stolen secrets until proven otherwise.

Triage checklist

  1. Isolate the host (Falcon network containment).
  2. Run Q7 for the host: note MD5HashData and SHA256HashData of the ovhcloud image and compare with Section 4.
  3. Check for an envchecker-NNNNNNNNN folder in the temp directory (N1).
  4. Run Q2 and Q9 for the host for the upload address.
  5. List credentials readable by the user (Section 13 playbook P5) and rotate from another machine.
  6. Find how the binary arrived (Q8, shell history, CI logs).
  7. Replace with v0.16.0 or later and verify the attestation.

Pivot queries

  • Host scope: Q7 filtered to the host aid.
  • Environment scope: Q4 and Q8 over 2026-10-19.

SentinelOne: custom detection rules

RuleSource queryRun asNotes
ovhcloud resolving Solana RPCQ3, PowerQuery tabScheduled custom detection ruleBind entities so the alert names the host instead of "Unknown Device"
ovhcloud spawning shells or credential toolsQ5, PowerQuery tabScheduled custom detection ruleBind entities so the alert names the host instead of "Unknown Device". This pack's own text mentions a baseline or pilot; follow it as written

Create the rule with POST /web/api/v2.1/cloud-detection/rules, queryType: "scheduled" and queryLang: "2.0", with the query in data.scheduledParams.query. Use 2.0 operator spellings: a rule written with the 1.0 token ContainsCIS was accepted and never fired on a tenant. The API does not warn, so lint the body before deploying.

Microsoft Defender: custom detection rules

RuleSource queryRun asNotes
ovhcloud resolving Solana RPCQ3, KQL – Defender XDR tabCustom detection rule, alert only, every hourThe query returns Timestamp, ReportId and DeviceId, which Microsoft recommends for Defender for Endpoint tables so alerts carry the device group scope and the process tree. Remove the Timestamp filter and the final sort and take before saving the rule: the service applies the rule's lookback itself
ovhcloud spawning shells or credential toolsQ5, KQL – Defender XDR tabCustom detection rule, alert only, every hourThe query returns Timestamp, ReportId and DeviceId, which Microsoft recommends for Defender for Endpoint tables so alerts carry the device group scope and the process tree. Remove the Timestamp filter and the final sort and take before saving the rule: the service applies the rule's lookback itself. This pack's own text mentions a baseline or pilot; follow it as written

Q1, Q2, Q4, Q6, Q7, Q8 and Q9 stay a hunt in this pack, not a rule. A rule can generate only 150 alerts per run. Continuous (NRT) frequency needs a single table and no comment lines, so delete the generated comment header from the query first.

10

Machine-Readable IOC Appendix

Import the CSV below only. The five MD5s and the upload address come from one source (the OVHcloud advisory), so they ship as detect. Only the swapped-archive SHA-256, named by three independent write-ups, ships as prevent. The legitimate original archive hash and the Solana hostnames are deliberately excluded.

Falcon IOC Management CSVbulk import
type,value,action,severity,expiration,description,tags
md5,6f2f086239f3d88eb2750e871901927d,detect,high,2027-04-24,malicious ovhcloud Darwin_arm64 replaced binary,campaign:OVHcloud-CLI-Release-Backdoor
md5,347e65eb7f2d78d1c6872690272a6cc3,detect,high,2027-04-24,malicious ovhcloud Darwin_x86_64 replaced binary,campaign:OVHcloud-CLI-Release-Backdoor
md5,01f57206d37c28cf711677f612052913,detect,high,2027-04-24,malicious ovhcloud Linux_arm64 replaced binary,campaign:OVHcloud-CLI-Release-Backdoor
md5,301bff0b51980b9e76c93633e7cfd3bd,detect,high,2027-04-24,malicious ovhcloud Linux_x86_64 replaced binary,campaign:OVHcloud-CLI-Release-Backdoor
md5,6a3cf153338c416169cda2fe704c8848,detect,high,2027-04-24,malicious ovhcloud.exe Windows x86_64 replaced binary,campaign:OVHcloud-CLI-Release-Backdoor
sha256,3833daffcaaaab27e4e0e0833240ffcb1df8df648f4ec0bc4a75c176e28933cb,prevent,critical,2027-04-24,swapped v0.15.0 Linux arm64 archive,campaign:OVHcloud-CLI-Release-Backdoor
sha256,e9c31dca232cdaa8058cb59eadf425c182a23afb6ddb057eb92eda13b850dff4,detect,medium,2027-04-24,sample linked in OVHcloud advisory (type unstated),campaign:OVHcloud-CLI-Release-Backdoor
sha256,3c10bd782cd0dc5f720a33570af4651814cb82917e54d27ffce3849a3c5cef2a,detect,medium,2027-04-24,sample linked in OVHcloud advisory (type unstated),campaign:OVHcloud-CLI-Release-Backdoor
sha256,3cbe1ac592beeca7ce0ad0ba945349a9ef33ef0f1324935c3a115f30b1d0c927,detect,medium,2027-04-24,sample linked in OVHcloud advisory (type unstated),campaign:OVHcloud-CLI-Release-Backdoor
sha256,25c68c5e9ae23883aebda59f4b6a7df78e1ce8aae5e4b4ba636419497d54bbec,detect,medium,2027-04-24,sample linked in OVHcloud advisory (type unstated),campaign:OVHcloud-CLI-Release-Backdoor
sha256,f97284530343b3903ffd19bbe70ab6f62e36f04eb7e5f885934bd21d10899e8a,detect,medium,2027-04-24,sample linked in OVHcloud advisory (type unstated),campaign:OVHcloud-CLI-Release-Backdoor
ipv4,45.32.148.174,detect,high,2027-04-24,stealer upload endpoint TCP/443 /upload,campaign:OVHcloud-CLI-Release-Backdoor
# NOT imported, LEGITIMATE original v0.15.0 Linux arm64 archive (known-good reference only): 5c2beb56e6d83a4b426565a1557791ef759316abcc4effb38dd627c90aeb80e0
# NOT imported, public Solana RPC hostnames are shared services: hunt with Q3 and Q4
# NOT imported, the other swapped archives have no published hash: REPLACE_WITH_SHA256_WHEN_PUBLISHED
Microsoft Defender indicators CSVSettings, Endpoints, Indicators, Import: audit and block
indicatorType,indicatorValue,action,title,description,expirationTime,severity,recommendedActions,rbacGroups,category,mitretechniques,GenerateAlert
FileSha256,3833daffcaaaab27e4e0e0833240ffcb1df8df648f4ec0bc4a75c176e28933cb,Block,swapped v0.15.0 Linux arm64 archive,swapped v0.15.0 Linux arm64 archive,2027-04-24T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True
FileSha256,e9c31dca232cdaa8058cb59eadf425c182a23afb6ddb057eb92eda13b850dff4,Audit,sample linked in OVHcloud advisory,sample linked in OVHcloud advisory (type unstated),2027-04-24T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True
FileSha256,3c10bd782cd0dc5f720a33570af4651814cb82917e54d27ffce3849a3c5cef2a,Audit,sample linked in OVHcloud advisory,sample linked in OVHcloud advisory (type unstated),2027-04-24T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True
FileSha256,3cbe1ac592beeca7ce0ad0ba945349a9ef33ef0f1324935c3a115f30b1d0c927,Audit,sample linked in OVHcloud advisory,sample linked in OVHcloud advisory (type unstated),2027-04-24T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True
FileSha256,25c68c5e9ae23883aebda59f4b6a7df78e1ce8aae5e4b4ba636419497d54bbec,Audit,sample linked in OVHcloud advisory,sample linked in OVHcloud advisory (type unstated),2027-04-24T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True
FileSha256,f97284530343b3903ffd19bbe70ab6f62e36f04eb7e5f885934bd21d10899e8a,Audit,sample linked in OVHcloud advisory,sample linked in OVHcloud advisory (type unstated),2027-04-24T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True
IpAddress,45.32.148.174,Audit,stealer upload endpoint TCP/443 /upload,stealer upload endpoint TCP/443 /upload,2027-04-24T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True

Column names and allowed values are from Microsoft's documented parameter table. Left out because this block maps only SHA-1, SHA-256, IPv4, domain and URL indicators: md5 5. Prevent rows import as Block, without remediation; network indicators cannot use BlockAndRemediate. Check each expirationTime before importing: an indicator whose date has passed is already expired. Download the sample CSV from the Import page to confirm the column order before a bulk import. Each file takes at most 500 indicators. Learn describes the import per entity type, so if a mixed file is rejected, split it by indicatorType. An imported indicator does nothing unless the matching Advanced features are on (for example Custom network indicators and Allow or block file).

SentinelOne IOC importnot included

This pack does not ship a SentinelOne import block: the import format could not be verified against SentinelOne's own documentation. Export a sample from your console and the block can be added with confidence.

Behavioral Signaturessearch strings
# Hunt strings (not IOC Management values)
Go build info (go version -m): mod github.com/ovh/ovhcloud-cli v0.15.1-0.20261013083505-5ead8ef9b43d+dirty
vcs.revision=5ead8ef9b43d354dfb5caadff803b11afc8c5c58   (real v0.15.0 tag = 760fd72de93cbea3b507eb36dc0095991e4048e5)
# Do not alert on +dirty or vcs.modified=true alone: the clean v0.16.0 build reports both
Temp folder: envchecker-[0-9]{9,10}  (created each run, never deleted)
Exfil: 45.32.148.174:443 path /upload (time-sensitive)
Solana wallet used by the binary: BHaDaVbdCYFNaKZGn7yLXFneWoJHZgLiBYkHxVrjyALF
RPC endpoints: solana.drpc.org, solana-rpc.publicnode.com, solana.api.pocket.network, api.mainnet-beta.solana.com, solana.api.onfinality.io (advisory writes it with a /public path)
Process lineage: ovhcloud -> shell / archiver / aws / gcloud / az / kubectl / doctl / docker / git / terraform
Named Product and Scopecontext
Product: OVHcloud CLI (ovh/ovhcloud-cli), binary names ovhcloud and ovhcloud.exe
Affected releases: v0.11.0, v0.12.0, v0.13.0, v0.14.0, v0.15.0   Clean: v0.10.0 and earlier, v0.16.0 and later
Replaced assets: Darwin arm64, Darwin x86_64, Linux arm64, Linux x86_64, Windows x86_64, checksums.txt
Not replaced: Linux i386, Windows i386, Windows arm64, Js wasm
Uploader seen by the reporter: HassenBF (a hijacked account, not an attacker handle)
Exposure window: 2026-10-19 12:47 to 14:21 UTC (14:47 to 16:21 Paris)
Attribution: unconfirmed (see Section 2)
Targeted Paths and Verificationrotate and verify
# Paths the stealer targets per the OVHcloud advisory (rotate these)
~/.aws/credentials  ~/.aws/config  ~/.aws/cli/cache
~/.config/gcloud/  ~/.azure/  ~/.docker/config.json  ~/.dockercfg  ~/.kube/config
~/.ssh  ~/.netrc  ~/.gitconfig  ~/.config/doctl/config.yaml  ~/.terraform.d/credentials.tfrc.json
~/.claude  ~/.hermes/  ~/.local/share/opencode/  ~/.config/opencode/
VS Code and Cursor globalStorage state.vscdb; Continue and Sourcegraph Cody databases

# Verify v0.16.0 or later (do not run first)
# gh release verify-asset v0.16.0 <FILE>
# gh attestation verify <FILE> --repo ovh/ovhcloud-cli
11

Detection Validation Gates

GateCheckPass condition
Telemetry readyProcess creation with command line, hashes and parent (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents); outbound network connection (CrowdStrike: NetworkConnectIP4. SentinelOne: IP Connect. Defender: DeviceNetworkEvents); DNS lookup with process (CrowdStrike: DnsRequest. SentinelOne: DNS Resolved. Defender: DeviceEvents); plus proxy and CI logs.A sample event from each source is searchable in the last hour. Confirm ContextBaseFileName is filled on DnsRequest for Q3.
Benign baselineRun Q4, Q5, Q8 and Q9 over 14 days before alerting.Fewer than 10 hits per week per query, each explained by a named tool, wrapper or install job.
Positive testsQ7: run an ovhcloud --help on a lab host with the legitimate CLI and confirm it appears. Q6: run echo envchecker-123456789 on a lab host. Q4: run nslookup solana.drpc.org on a lab host. Q3 cannot be exercised safely without the malicious binary, so validate its field mapping with Q4 output. Never run a malicious sample.Each test fires its query within ingest delay. Use no live malware.
PromotionPromote Q1, Q2, Q3 first (high confidence, low FP), then Q5 after baselining. Q7 and Q8 stay as scoping hunts.Detect for 14 days with no unexplained hits, then enable prevention where supported.
12

Hardening: Tiered & Deployable

Immediate (this week)
  • T1. Turn on immutable releases for every repository that publishes binaries, at the organisation level. A release created after the setting is on cannot have assets changed or removed. OVHcloud's own fix. Existing releases are not protected retroactively, so re-publish critical old releases. Framework: ATT&CK T1195.002 and M1051; GitHub immutable-releases documentation (GA since Oct 2025 per [#4]). Verify: Playbook P1.
  • T2. Remove personal accounts and excess Write access from release-publishing repositories. The uploader here was a contributor who was not a public org member. Publish only through a service account or workflow identity with short-lived credentials. Framework: M1018, M1026; T1078. Verify: N4.
  • T3. Pin and verify the hash of every release asset you download in CI, Dockerfiles and install scripts. The single pinned-hash job here failed and exposed the swap. Pin against a source other than the same release page (checksums.txt was swapped too). Framework: ATT&CK T1195.002 and M1051; ⚠ best-practice, no formal benchmark. Verify: Playbook P2.
  • T4. Rotate every secret readable on any host that ran a swapped build, from a different machine. Framework: M1027 Password Policies, M1026; T1552.001. Verify: Playbook P5.
Near term (1 to 4 weeks)
  • T5. Block or alert on Solana RPC lookups from build runners and servers with no blockchain role. Public services, so block by role, never globally. Framework: M1037 Filter Network Traffic; T1102. Verify: Q4 and Playbook P4.
  • T6. Require signature or attestation verification before installing release binaries. OVHcloud v0.16.0 ships a release attestation; check it with the GitHub CLI. Framework: ⚠ best-practice, no formal benchmark; vendor guidance [#3] [#4]. Verify: Playbook P2.
  • T7. Stop piping install scripts to a shell on developer machines. Download, hash-check, then run. Framework: M1017 User Training; ⚠ best-practice.
Strategic (1 to 3 months)
  • T8. Keep developer credentials short-lived and out of well-known files. Use SSO-backed cloud sessions, hardware-backed SSH keys and a secret manager, so a file-scraping stealer finds nothing durable. Framework: M1041 Encrypt Sensitive Information, M1022 Restrict File and Directory Permissions; T1552.
  • T9. Sign commits and tags, use CODEOWNERS on release workflows, and link release rights to central identity management. OVHcloud lists these as its own next steps [#1]. Framework: M1032 Multi-factor Authentication; T1078.
  • T10. Control IDE extensions on developer machines. The advisory attributes the original account takeover to a malicious editor extension [#1]. Allowlist extensions. ⚠ best-practice, no formal benchmark.
13

Deployable Playbooks

P1 · Enable immutable releases (GitHub, organisation owner)

1. GitHub org Settings > Repository defaults (or each repo: Settings > General > Releases) > enable "Release immutability".
   ⚠ Menu names change; confirm in GitHub's current immutable-releases documentation.
2. Publish new releases from CI as a draft, attach all assets, then publish. Assets are locked at publish.
3. Verify an immutable release locally: gh release verify v0.16.0 --repo ovh/ovhcloud-cli
Prerequisites: organisation owner rights; release process that attaches assets before publishing.
Reboot required: no
Rollback: untick the setting. Releases already published while it was on stay immutable by design.

P2 · Hash-pin and verify a release asset in CI (POSIX shell)

#!/bin/sh
set -eu
VERSION="v0.16.0"
ASSET="ovhcloud-cli_Linux_x86_64.tar.gz"
# EXPECTED_SHA256 comes from your own reviewed config, not from the same release page
EXPECTED_SHA256="${OVH_CLI_SHA256:?set the pinned hash}"
curl -fsSL -o "$ASSET" "https://github.com/ovh/ovhcloud-cli/releases/download/${VERSION}/${ASSET}"
echo "${EXPECTED_SHA256}  ${ASSET}" | sha256sum -c -
gh attestation verify "$ASSET" --repo ovh/ovhcloud-cli
tar -xzf "$ASSET"
Prerequisites: curl, sha256sum (shasum -a 256 on macOS), GitHub CLI authenticated; the hash pinned after you verified it once.
Reboot required: no
Rollback: remove the step from the pipeline; nothing on the host changes.

P3 · Read-only exposure triage on a host (do not run the CLI)

# macOS / Linux
ls -ld "${TMPDIR:-/tmp}"/envchecker-* 2>/dev/null
for f in $(command -v ovhcloud); do md5sum "$f" 2>/dev/null || md5 "$f"; done
go version -m "$(command -v ovhcloud)" 2>/dev/null | grep -E 'mod|vcs.revision'
# Windows PowerShell
Get-ChildItem $env:TEMP -Directory -Filter "envchecker-*" -ErrorAction SilentlyContinue
Get-Command ovhcloud.exe -ErrorAction SilentlyContinue | ForEach-Object { Get-FileHash $_.Source -Algorithm MD5 }
Prerequisites: shell access; Go toolchain optional (go version -m only reads the file).
Reboot required: no
Rollback: none required, every command is read-only.

P4 · Resolver block for Solana RPC on build hosts (dnsmasq example)

# /etc/dnsmasq.d/block-solana-rpc.conf  (apply ONLY to build and server networks)
address=/solana.drpc.org/0.0.0.0
address=/solana-rpc.publicnode.com/0.0.0.0
address=/solana.api.pocket.network/0.0.0.0
address=/api.mainnet-beta.solana.com/0.0.0.0
address=/solana.api.onfinality.io/0.0.0.0
Prerequisites: a resolver you control for the build network; confirm no blockchain workload depends on these names.
Reboot required: no (restart dnsmasq: systemctl restart dnsmasq)
Rollback: delete the file and restart dnsmasq.

P5 · Secret rotation checklist after exposure

Work from a different, clean machine. For the affected user and host:
1. OVHcloud API keys / consumer keys; ~/.ovh.conf and any OVH_* environment variables
2. AWS, GCP, Azure sessions and keys; Terraform Cloud tokens; doctl tokens
3. SSH keys in ~/.ssh (generate new, remove old public keys from servers and GitHub)
4. Kubernetes configs; registry logins in ~/.docker/config.json
5. npm / .netrc / git credentials; GitHub personal access tokens
6. Tokens held by IDEs and AI coding assistants (editor databases, assistant data directories)
7. Review cloud and identity audit logs for use of old credentials since 2026-10-19
Prerequisites: admin rights on each service; a clean workstation.
Reboot required: no
Rollback: not applicable; a rotated secret should not be restored. Keep the old value revoked.
14

Containment Runbook

PhaseActionsOwnerEvidence
IsolateNetwork-contain any host with a Q1, Q2 or Q3 hit or an envchecker folder (Falcon network containment). Pause CI jobs that fetched the CLI in the window. Do not run the binary.SOC / DevOpsContainment timestamp, host list from Q7 and Q8
Isolate: by platformCrowdStrike: network-contain the host. SentinelOne: network quarantine (Disconnect from Network; confirm the label in your console version). Microsoft Defender: Isolate device (full network isolation).SOC / DevOpsContainment timestamp, host list from Q7 and Q8
ScopeRun Q7 and Q8 for 2026-10-19 12:47 to 14:21 UTC. Check N2 build logs and N3 egress logs. Mark each host as swapped, clean, or unknown by hash.SOCHash table per host
EradicateRotate secrets (P5) from a clean machine. Delete the binary and any envchecker folders after imaging if evidence is needed. Reinstall v0.16.0 or later with a verified attestation (P2).IR / Owner of each secretRotation ticket per secret class, attestation check output
RecoverReturn hosts to service after reinstall. Re-enable CI with pinned hashes. Watch for use of old credentials in cloud and identity audit logs for 30 days.DevOps / Cloud securityAudit-log queries, clean Q1 to Q3 runs
HardenEnable immutable releases, restrict Write, and apply Section 12 controls.Engineering leadershipOrg settings screenshot or export
15

Detection Coverage Map

TechniqueBehaviorCQLIOACrowdStrike coverageSentinelOneDefender XDR
T1078Hijacked GitHub account uploads assetsGAPGAPGAP
T1195.002Swapped release assets installed or downloadedQ1, Q7, Q8PartialPartial: same limit as CrowdStrikePartial: Q1 telemetry gap
T1204.002Swapped binary executedQ1, Q7GoodGood, with assumptionsPartial: Q1 telemetry gap
T1119, T1005, T1552Credential and file collection into the temp folderQ5, Q6Q5PartialPartial: same limit as CrowdStrikePartial: same limit as CrowdStrike
T1560Archive of the working folderQ5, Q6GAPGAP: same limit as CrowdStrikeGAP: same limit as CrowdStrike
T1102Solana RPC endpoint lookupsQ3, Q4Q3GoodGood, with assumptionsGood, with assumptions
T1041Upload to hard-coded addressQ2, Q9PartialPartial: same limit as CrowdStrikePartial: same limit as CrowdStrike

Gaps. The account takeover and release swap are outside endpoint telemetry (N4). The stealer's file reads and temp-folder creation are in-process and not logged by the sensor, so only the execution itself, the DNS lookup and the upload connection are visible. The other swapped archives have no published hash, so Q1 covers only the five binaries, one archive and the five advisory sample links.

Validation. Run Section 11 before promoting: telemetry ready, 14-day baseline, safe positive tests, then staged promotion.

16

Hunt Summary Ticket

TITLE: OVHcloud CLI release-asset backdoor (v0.11.0 to v0.15.0, 2026-10-19)
SEVERITY: High. Supply-chain compromise stealing developer and operator secrets; exposure
          window closed (12:47 to 14:21 UTC on 2026-10-19), roughly 250 downloads per OVHcloud
SCOPE: Developer workstations and CI runners on macOS, Linux (x86_64, arm64) and Windows x86_64
       that downloaded or ran the CLI in the window; any secret readable on those machines
HYPOTHESIS: A hijacked contributor GitHub account replaced release assets through the Releases
       API because immutable releases was off; the swapped binary copies credentials into an
       envchecker temp folder and uploads them, using Solana RPC endpoints.
QUERIES: 9 CQL queries (Q1-Q9). Q1, Q2, Q3 high confidence; Q3 and Q5 promotable to Custom IOA;
       hashes and the upload address go to IOC Management.
DO FIRST: 1) Run Q7 and Q8 for 2026-10-19. 2) Run Q1, Q2, Q3. 3) Check N1 on any host that ran
       the CLI. 4) Rotate secrets (P5) from a clean machine. 5) Enable immutable releases.
FINDINGS: [fill in after the hunt]
GAPS: GitHub account takeover and release changes are outside endpoint telemetry. File reads
       and the temp-folder creation are not visible in process, network or DNS telemetry.
ACTIONS: Rotate secrets; reinstall v0.16.0+ with attestation check; pin hashes in CI; immutable
       releases; remove personal accounts from release repos; block Solana RPC on build hosts.
OWNER: REPLACE_WITH_OWNER
VERSION: v0.1 (Draft) 2026-10-24
17

Changelog

v0.12026-10-24Initial draft. 9 CQL queries, 2 IOA candidates, 1 alert package, tiered hardening with 5 playbooks. 5 sources read and snapshotted; 1 fetch blocked (HTTP 403) and not used. SentinelOne PowerQuery and Microsoft Defender KQL tabs added to Q1-Q9: SIEM Query Rosetta release 0.1.0 (public-beta; static-reviewed translations).
18

References

TierSourceUsed forAccess date
T1OVHcloud: ovhcloud-cli security incident, October 19th, 2026 (GHSA-862f-55f5-vmvw)Timeline, affected versions, MD5s, upload address, Solana endpoints, targeted files, root cause, remediation.2026-10-24
T1GitHub issue #300: Release assets of v0.13.0 to v0.15.0 replacedFirst-hand hashes of the original and swapped archive, build metadata, maintainer responses.2026-10-24
T2OpenSourceMalware: OVHcloud CLI supply chain compromiseServer-side timeline, replaced-asset table, how to check a binary, attribution inference.2026-10-24
T2The Hacker News: Hackers Hijack Five OVHcloud CLI ReleasesIndependent exposure analysis, Homebrew correction, caution about +dirty.2026-10-24
T2Help Net Security: Info-stealing malware slipped into OVHcloud CLI releasesCorroboration of versions, times, immutability root cause.2026-10-24

Generated 2026-10-24 by HuntPack. Defensive content only.