← blog
"CLI OVHcloud compromise : un hash qui change..."

"CLI OVHcloud compromise : un hash qui change..."

Pourquoi j'en parle

Ce qui m'intéresse, c'est comment le problème a été détecté, et ce que je peux en tirer pour mon propre pipeline.

Ce qui s'est passé

Le 19 octobre 2026, les binaires publiés dans les releases GitHub de ovh/ovhcloud-cli (versions v0.11.0 à v0.15.0) ont été remplacés par des versions piégées. Pas de modification du code source, pas de compromission du pipeline de build : juste les fichiers téléchargeables, échangés après coup.

D'après l'advisory, le binaire malveillant est la vraie CLI recompilée avec un module en plus, envchecker, qui ramasse tout ce qui traîne sur le disque : credentials AWS, GCP, Azure, Docker, Kubernetes, Terraform, NPM, clés SSH, config Git, mais aussi les bases de données de VS Code, Cursor et de plusieurs assistants IA, dont le dossier de configuration de Claude. Le tout est archivé puis exfiltré.

Le binaire est resté disponible 1h33, pour environ 250 téléchargements.

Comment c'est arrivé

Le point d'entrée n'est pas l'infra d'OVH. C'est le compte GitHub personnel d'un développeur, dont le MacBook perso avait été compromis… un an plus tôt, en octobre 2025, par une extension d'éditeur piégée (color-picker-universal), liée au groupe connu sous le nom de GlassWorm. Les clés volées sur cette machine ont servi à remplacer les assets des releases.

OVH résume lui-même la cause principale en une phrase : le dépôt n'était pas configuré pour garantir l'immuabilité des releases. Un compte avec les droits d'écriture pouvait donc remplacer un binaire déjà publié, sans changer le tag ni le code.

Qui l'a vu, et comment

L'alerte n'est pas venue d'un outil de sécurité sophistiqué, mais d'un client dont la CI a simplement refusé de continuer.

Dans son pipeline, il téléchargeait ovhcloud-cli_Linux_arm64.tar.gz en version v0.15.0, et vérifiait son empreinte SHA-256 par rapport à une valeur figée dans sa config. Le jour J, le hash ne correspondait plus. Même version, même URL, fichier différent : le pipeline a cassé.

Au lieu de mettre à jour le hash pour que ça repasse (ce qu'on serait tenté de faire), il a creusé :

  • les assets des versions qu'il a vérifiées (v0.13.0 à v0.15.0) avaient été réuploadés par un autre compte que celui qui avait publié la release ;
  • go version -m sur le nouveau binaire indiquait un commit postérieur à la release, avec le flag +dirty et vcs.modified=true ;
  • il manquait le numéro de version normalement injecté au build par GoReleaser (-ldflags -X …Version) : le binaire ne sortait donc pas du pipeline de release officiel ;
  • le tag Git, lui, pointait toujours vers le bon commit.

Il a ouvert l'issue #300 avec tout ça, et l'équipe a supprimé les binaires dans la foulée.

Ce que j'en retiens pour mon pipeline

  • Épingler par empreinte, pas par nom. Un tag peut pointer vers autre chose demain, un SHA-256 non. J'utilise déjà les digests, mais c'est encore améliorable au niveau de mes jobs.
  • Un check qui casse, on enquête avant de le "réparer".
  • Ce qui est publié ne bouge plus. Une fois une version publiée, personne ne devrait pouvoir remplacer son contenu, c'est exactement ce qui a manqué ici. Côté registry Docker c'est pareil : il faut empêcher qu'on puisse repousser une autre image sous un tag existant. Le registry de base (registry:2) ne sait pas le faire. Harbor propose des règles d'immuabilité par projet, Zot y arrive en ne donnant au compte de la CI que les droits read et create (sans update ni delete, sinon on supprime puis on repousse), et chez AWS, ECR a une option IMMUTABLE par dépôt.
  • Signer ce qu'on publie. Une signature cosign générée par la CI aurait trahi un binaire uploadé à la main.
  • Des secrets à durée de vie courte. Les clés volées étaient toujours valides des mois après.

Pour conclure

La réaction d'OVHcloud a été correcte : binaires supprimés en moins de deux heures, annonce publique le jour même, puis un advisory détaillé avec la cause racine, la chronologie et les indicateurs de compromission. Ce niveau de détail permet à tout le monde de profiter du retour d'expérience, et c'est grâce à lui que je peux écrire ce billet et revoir mon propre pipeline.

Sources