Si votre équipe utilise gem install ou installation du paquet, que ce soit sur un ordinateur portable ou dans un environnement CI, cet article s'adresse à vous. C'est le genre de chose à laquelle on ne s'attendrait jamais. Un gem peut exécuter du code sur votre machine sans script d'installation. Voici comment détecter quatre gems responsables d'exfiltration de données que nous avons repérés sur RubyGems, désactiver ce comportement grâce à de nouveaux paramètres, et voir ce qu'Aikido e dans votre flux.
TL;DR
- L'installation d'un gem Ruby peut entraîner l'exécution du code de ce gem avant même que votre application ne le charge. Les scripts de compilation des extensions natives s'exécutent pendant l'installation. Les plugins RubyGems s'exécutent à la fin de
gem installet ainsi de suite pour chaque gemme suivantecommande, voireliste des gemmesougem -v. - Npm a déjà résolu ce problème. Depuis la version 12, npm propose désormais l'activation optionnelle des scripts d'installation, y compris pour les builds natifs. RubyGems continue quant à lui d'exécuter par défaut les builds d'extensions et les plugins.
- Les versions stables de RubyGems et de Bundler 4.0.x ne permettent pas de désactiver ces deux comportements. Les versions préliminaires de la version 4.1 intègrent des paramètres permettant de les désactiver, mais ceux-ci sont désactivés par défaut ; vous devez donc les activer vous-même.
- AikidoLes nouvelles règles de détection ont permis de repérer quatre gemmes sur RubyGems, publiées entre 2013 et 2015, qui transmettent le nom d'utilisateur de l'installateur, une capture d'écran de macOS ou une balise à un serveur distant. Au 4 novembre 2026, ces gemmes signalées pouvaient toujours être installées.
- Pour bloquer les gemmes malveillantes connues avant qu'elles n'atteignent la machine d'un développeur, utilisez Aikido Device Protection. Il vérifie
gem installetinstallation du paquetsous Ruby 3 et versions ultérieures, en utilisant Aikido Intel, et bloque les logiciels malveillants connus avant qu'ils n'atteignent le système de fichiers. - Si vous êtes client d'Aikido , Aikido signale ces éléments comme un problème de logiciel malveillant dans votre flux.
- En octobre, nous avons signalé à l'équipe de sécurité de RubyGems cette extension, les chemins d'exécution des plugins et les quatre gemmes permettant l'exfiltration de données que nous avions découvertes. Elle nous a ensuite posé des questions concernant les indicateurs de désactivation et n'a pas fait d'autres commentaires.
Paramètres de désactivation
(Les indicateurs et paramètres de désactivation ne s'appliquent qu'à la version 4.1.0.beta3) :
- Mettez à jour RubyGems et Bundler vers la version 4.1.0.beta3.
- Pour
gem install, passer--no-build-extension --no-install-plugin. - Pour Bundler, exécutez la commande « bundle config set »
--global no_build_extension trueetbundle config set --global no_install_plugin true. - Dans CI, définissez
BUNDLE_NO_BUILD_EXTENSION=trueetBUNDLE_NO_INSTALL_PLUGIN=truee.
Que faire si vous utilisez des gemmes Ruby ?
Si vous utilisez des gemmes Ruby, suivez ces étapes pour vous assurer que votre système est sécurisé. Sinon, vous pouvez passer cette section pour en savoir plus sur le fonctionnement de ces fonctionnalités.
1. Vérifiez la présence des quatre gemmes malveillantes. Dans chaque projet, recherchez tous les fichiers de verrouillage situés dans le répertoire courant :
grep -rE '^ +(xmastree|ancient-city-ruby|hacky|et_phone_home) \(' --include=Gemfile.lock .Pour vérifier les gemmes installées sans exécuter la commande `gem` (ce qui chargerait les plugins), exécutez :
ruby -e 'Gem.path.each { |p| puts Dir.glob("#{p}/gems/*") }' | grep -E '/(xmastree|ancient-city-ruby|hacky|et_phone_home)-[0-9]'Si vous utilisez rbenv ou un autre gestionnaire de versions de Ruby, répétez cette opération pour chaque version de Ruby. Les versions concernées figurent dans le tableau IOC à la fin de cet article.
2. Que faire si vous avez installé l'une de ces gemmes ? La charge utile s'exécute à partir d'un fichier Rakefile (extension native) lors de l'installation ; il a donc suffi d'installer la gemme pour qu'elle s'exécute.
- Arrêtez tous les processus lancés par le gem.
- Désinstallez le gem. Remplacez
GEM_NAMEen indiquant le nom de la gemme concernée :gem uninstall GEM_NAME --all --executables - Supprimez ce gem des dépendances de votre projet. Supprimez-le de votre Gemfile, puis exécutez la commande suivante :
installation du paquetpour régénérer leGemfile.lock. - Évaluez l’exposition potentielle. Passez en revue les actions de la gemme dans la section IOC afin de déterminer quelles données ont pu être exfiltrées, puis prenez les mesures qui s’imposent.
3. Vérifiez quels plugins RubyGems vous avez installés et désinstallez ceux dont vous n'avez pas besoin. Exécutez la commande ci-dessous et répertoriez tous les répertoires qu'elle affiche. RubyGems charge les plugins à partir de tous les chemins d'accès aux gemmes, et pas seulement du chemin par défaut.
ruby -e 'Gem.path.each { |p| puts Gem.plugindir(p) }'4. Désactivez la compilation des extensions et l'installation des plugins dans la mesure du possible. Cela nécessite RubyGems et Bundler 4.1.0.beta3. Installez-les à l'aide de la commande suivante : gem update --system 4.1.0.beta3, ce qui met également à jour Bundler. (gem update --system --pre ne l'installe pas.)
- Pour
gem install, passer--no-build-extension --no-install-plugin. - Pour Bundler, exécutez la commande suivante :
bundle config set --global no_build_extension trueetbundle config set --global no_install_plugin true. - Dans CI, définissez
BUNDLE_NO_BUILD_EXTENSION=trueetBUNDLE_NO_INSTALL_PLUGIN=true. Ne désactivez les versions précompilées des extensions que dans un job d'intégration continue (CI) qui ne charge pas l'application, comme une étape d'installation des dépendances ou d'audit, sauf si chaque gem de votre bundle dispose d'une version précompilée. Sinon, votre application et vos tests ne pourront pas charger les gems dont les extensions ont été ignorées.
Ces paramètres empêchent uniquement l'exécution du code lors de l'installation. Le code d'un gem malveillant s'exécute toujours lorsque votre application ou vos tests le chargent ; ils protègent donc uniquement les étapes d'installation, et non tout ce qui nécessite l'utilisation du bundle.
5. Attendez-vous à ce que les gems dont les extensions ont été ignorées ne se chargent plus. RubyGems ne chargera pas une gem dont les extensions déclarées n'ont pas été compilées, même si son code natif est facultatif, et affichera le message Ignoring <gem> because its extensions are not built. Bundler considère que le gem est manquant, donc bundle exec échoue avec Could not find <gem> in locally installed gems, et chaque installation du paquet le réinstalle.
Les versions précompilées pour les plateformes ne déclarent pas les extensions, ce qui explique qu'elles se chargent quand même. Une fois que vous avez vérifié un gem, réactivez son extension :
gem install: exécutergem pristine <gem> --extensions. Cela installe également le plugin du gem, s'il en possède un, et ce plugin s'exécute alors. Pour installer uniquement le plugin, exécutezgem pristine <gem> --only-plugins.- Bundler : non défini
no_build_extensionet exécuterinstallation du paquetencore. Continuezno_install_pluginDéfinissez cette option si vous souhaitez que les plugins restent désactivés. Cela entraîne la recompilation de tous les gemmes du bundle dont les extensions ont été ignorées. L'avertissement de Bundler recommande d'exécuterbundle pristine <gem>, mais dans la version 4.1.0.beta3, cela échoue avec la mêmeImpossible de trouvererreur, car Bundler ne détecte pas que le gem est installé.lot en parfait étatCela fonctionne pour un gem dont seul le plugin a été ignoré, même si l'extension est également recompilée.
6. Vérifiez les gemmes nouvelles ou récemment mises à jour avant de les installer. Dans un environnement propre, exécutez gem fetch et ensuite décompresser le fichier gem. Lire extconf.rb, mkrf_conf.rb, tout fichier Rakefile ou CMake répertorié sous extensions dans le fichier gemspec, ainsi que tout lib/rubygems_plugin.rb.
Nous avons testé ces commandes sous Ruby 4.0.9 avec RubyGems et Bundler 4.0.23 (la version stable actuelle) ainsi que 4.1.0.beta3. Les options de désactivation, les paramètres et les étapes de restauration ne s'appliquent qu'à la version 4.1.0.beta3.
Deux façons dont un gem exécute du code sur votre ordinateur
Ruby propose deux méthodes pour exécuter le code d'un gem sans recourir à un script d'installation : les extensions natives et les plugins RubyGems. Ces deux méthodes permettent à un attaquant d'exécuter une charge utile avant même que le développeur ne lance l'application. Un script de compilation d'extension malveillant pourrait voler des identifiants lors de l'installation. Un plugin RubyGems malveillant pourrait faire de même à la fin de cette même gem install, puis à chaque fois que le développeur exécute une commande gem.
Méthode 1 : Extensions natives
Un joyau déclare extensions dans son gemspec (le manifeste du gem). Si l'un d'entre eux est un extconf.rb script de compilation : RubyGems exécute ce fichier Ruby lors de l'installation, puis exécute le Makefile qu'il génère. Tout ce qui est écrit en Ruby dans ce fichier s'exécute lors de l'installation, de la même manière que node-gyp exécute les compilations natives pour les paquets npm.
RubyGems s'exécute également mkrf_conf.rb sous forme de script Ruby lors de l'installation, si un gem en mentionne un dans extensions, puis exécute râteau dans le même répertoire. Outre ces scripts Ruby, RubyGems prend en charge les compilations avec configure, Rakefile, CMake et Cargo, chacun exécutant le code via son propre outil de compilation.
Ce qu'un attaquant apporte à un gem :
- Un fichier,
extconf.rboumkrf_conf.rb(par exemple,ext/foo/extconf.rb), contenant tout le code Ruby de leur choix - Une ligne dans le fichier gemspec :
spec.extensions = ["ext/foo/extconf.rb"]
Pourquoi c'est facile de se laisser piéger : de nombreux outils fiables, tels que bcrypt et bootsnap, utilisent extconf.rb. Il ressemble à n'importe quel autre fichier de compilation, et il ne comporte pas de section « scripts » distincte susceptible d'attirer l'attention d'un relecteur.
Méthode 2 : plugins RubyGems
Si un gem contient un fichier nommé rubygems_plugin.rb, RubyGems l'installe en tant que plugin. Lors de l'installation, RubyGems crée un petit programme de chargement pour ce fichier dans son répertoire « plugins », et chaque commande « gem » exécutée par la suite charge le contenu de ce répertoire.
Ce qu'un attaquant ajoute à un gem : un fichier portant exactement le nom rubygems_plugin.rb à la racine d'un chemin « require » déclaré, généralement lib/rubygems_plugin.rb.
Lorsqu'il s'exécute :
- RubyGems charge immédiatement un plugin nouvellement installé à la fin de
gem install(si aucune autre version n'est installée). - Il charge ensuite tous les plugins installés lors de l'exécution de n'importe quelle commande gem, y compris les commandes en lecture seule telles que
liste des gemmes,recherche de gemmes, etgem -v, ainsi que des commandes relatives à des gemmes sans rapport avec le sujet. - Le plugin s'exécute à chaque commande, tant que le gem reste installé.
Pourquoi on a tendance à l'oublier : c'est un fichier situé dans lib\/ portant un nom spécifique. Aucune déclaration distincte du plugin n'est requise ; un réviseur à la recherche de points d'accrochage d'exécution explicites pourrait donc passer à côté. RubyGems signale les exceptions liées au chargement des plugins sous forme d'avertissements ; la commande se poursuit donc même en cas d'échec d'un plugin.
Gems que Bundler installe dans fournisseur/offre groupée ou une solution sur mesure BUNDLE_PATH fonctionnent un peu différemment. Un simple liste des gemmes ne charge pas leurs plugins, mais bundle exec gem list c'est le cas.
npm est déjà passé par là
Npm a appris à ses dépens que la sécurité lors de l'installation ne doit pas se limiter aux scripts de cycle de vie. Dans la campagne « Miasma », les pirates ont déplacé le déclencheur de l'attaque depuis package.json dans binding.gyp, le fichier de compilation natif de npm, qui node-gyp s'exécute automatiquement lors de l'installation. Vérifie que seuls les fichiers surveillés package.json L'installation des hooks a échoué. NPM 12 a réagi en rendant facultatifs les scripts d'installation des dépendances, y compris les builds node-gyp.
Ruby est confronté au même problème, et nous ne disposons pas encore d’une version stable qui le corrige. Un fichier `extconf.rb` ressemble à une configuration de compilation ordinaire, et un fichier `rubygems_plugin.rb` ne nécessite aucune déclaration ; aucun des deux ne semble donc être un script d’installation à examiner. Les pirates ciblent d’ores et déjà les développeurs Ruby. Dans le cadre de la campagne « SleeperGem », ils ont publié des versions malveillantes de gemmes à partir de comptes de mainteneurs inactifs qui semblent avoir été compromis.
Un exemple récent montre que ce chemin d'exécution est utilisé pour diffuser des logiciels malveillants. Oliver Smith, membre de l'équipe de recherche d'Aikido, ainsi que Socket ont signalé un problème concernant la version 1.0.1 de strutil-core. Son fichier extconf.rb téléchargeait le runtime Bun et lançait, lors de l’installation, une charge utile intégrée destinée à voler des identifiants. Ce logiciel malveillant partage des composants fonctionnellement identiques à ceux de descendants de Shai-Hulud déjà répertoriés, bien que le code commun ne permette pas d’identifier ses auteurs. RubyGems a supprimé les deux versions publiées le 1er novembre. Ce cas est distinct de notre divulgation d’octobre, mais illustre le même chemin d’exécution via les extensions natives décrit ci-dessus.
1 application populaire sur 13 exécute du code lors de son installation
Le 26 octobre 2026, nous avons sélectionné les quelque 1 000 gems les plus téléchargées sur bestgems.org et avons consulté leurs spécifications publiées afin de comprendre dans quelle mesure les extensions natives et les fichiers de plugins sont courants aujourd’hui. Il convient de garder à l’esprit que le fait que les gems utilisent ou non ces mécanismes au moment de l’installation ne permet pas d’évaluer le risque, car les attaquants peuvent les exploiter à tout moment. Cela montre toutefois à quel point ces étapes de compilation semblent normales aux yeux d’un évaluateur.
- Extensions natives : 74 sur 997 (soit environ 1 sur 13) déclarent des étapes de compilation d'extension, notamment bcrypt, bigdecimal, bootsnap, bson, debug, ainsi que de nombreux gemmes courants liés aux bases de données et à l'analyse syntaxique.
- Fichiers de plug-ins : 2 sur 997 (rdoc et yard) sont fournis
rubygems_plugin.rb. Une fois installées dans l'environnement actif, elles sont toutes deux chargées lors de l'exécution ultérieure de commandes gem.
Quatre petits bijoux de « preuve de concept » qui transmettent des données à distance peuvent toujours être installés
En testant nos règles de détection, nous avons découvert quatre « pépites », publiées entre 2013 et 2015, qui exécutent du code lors de leur installation : xmastree, ancient-city-ruby, hacky et et_phone_home. Elles envoient le nom d'utilisateur de la personne qui effectue l'installation, une capture d'écran de macOS prise à son insu ou un simple signal vers un serveur distant.
Elles ressemblent à des preuves de concept ou à des tests de recherche, mais elles s'exécutent tout de même une fois installées et transmettent des données hors de la machine. Les versions concernées et les destinations figurent dans le tableau des indicateurs de compromission (IOC) à la fin de cet article.
Calendrier de publication de nos conclusions
- 27 octobre 2026 : Nous avons signalé les chemins d'exécution des extensions et des plugins à l'équipe de sécurité de RubyGems. Celle-ci nous a posé des questions concernant les indicateurs de désactivation et n'a pas donné d'autres commentaires depuis.
- 30 octobre 2026 : Nous avons signalé ces quatre gemmes à l'équipe de sécurité de RubyGems.
- 4 novembre 2026 : Les gemmes mentionnées sont toujours disponibles sur RubyGems.
Les RubyGems « stables » ne disposent pas de bouton d'arrêt
On pourrait croire qu'il suffit de passer les drapeaux --no-build-extension et --no-install-plugin vers gem. Cependant, ces deux indicateurs n'existent que dans les versions préliminaires de la 4.1, et même dans la version 4.1.0.beta3, ces deux comportements restent activés à moins que vous ne les désactiviez. Sur la version stable 4.0.x, gem install les rejette car elles ne constituent pas une option valable.
Comment Aikido détecte cela
Si vous utilisez Aikido , consultez votre flux central et filtrez les problèmes liés aux logiciels malveillants. Lorsqu’un gem malveillant apparaît dans l’un des fichiers de verrouillage de vos dépôts, Aikido le signale comme un problème lié à un logiciel malveillant. Aikido effectue une nouvelle analyse chaque nuit, mais nous vous recommandons de lancer immédiatement une nouvelle analyse manuelle si vous pensez être concerné.
Pour bloquer les gemmes malveillantes connues avant qu’elles n’atteignent l’ordinateur d’un développeur, utilisez la protection des appareils d’Aikido . Elle vérifie les commandes « gem install » et « bundle install » sous Ruby 3 et versions ultérieures par rapport à la base de données Aikido Intel, et bloque les logiciels malveillants connus avant qu’ils n’accèdent au système de fichiers. Vous pouvez la déployer via votre outil de gestion des appareils mobiles (MDM).
