Les attaques de type "SolarWinds" en plus sournoises...

En leurrant les gestionnaires de packages des différents plateformes, notamment : (PyPI (Python Package Index), npm (Node.js) et RubyGems). Un hacheur éthique est parvenu à créer  Une porte d'entrée à l'infection par rebond aux systèmes informatiques de Microsoft, Apple, PayPal, Netflix ou encore Tesla.

Je ne vais pas rentrer dans les détails du sujet, le monde informatique l'a déjà fait.

Je rappelerai juste trois méthodes que préconise également Microsoft, pour tenter de réduire ce risque qui aujourd'hui avec la complexité des librairies de code, et la quantité devant être utilisé pour les logiciels moderne rend cette tâche difficile lorsque l'on a des objectifs de production de nouveautés très important.

Beaucoup d'organisations utilise les flux de packages publique comme Maven, npm, nuget ou encore PyPI. Cela permet d'avoir l'avantage d'un écosystéme très ouvert vis à vis de ce qui est offert par ces galeries de packages de code. Toutefois, le résultat est un échec des organisations à gérer les menaces provenant des flux non sécurisés qui sont sources de malwares. La confiance dans ces projets qui sont consommés depuis des sources publiques et privées est primordiale. Car ces projets peuvent exposés une chaine unique de vulnérabilités en mitigeant les configurations.

Quand les packages privés ne sont pas suffisament privés

Pendant la dernière décènnie, il y a eu une augmentation trés importante du périmétre, qualité et disponibilité de code "gratuit" notamment avec l'avénement du logiciel Open Source. Et ceux, même pour des applications commune relativement "fermé" comme des outils, librairies, ou encore systèmes d'exploitation. Un facteur critique dans le développement de l'écosystème autours des annuaires de package fut d'autoriser tout le monde à pouvoir publier, comme Maven, Nuget, ou encore PyPI.

Une galerie publique et ouverte à tous permet de partager le code sans fournir d'identité vérifiée. Cette facilité de publication a permis de créer de large écosystèmes de packages. Ils peuvent inclure de nombre éléments variés, de bloc de code à des aspects de niche ou encore des algorithmes trés spécifique à un domaine, ce sont des outils puissant pour découvrir et acquérir ces packages.

Les organisations (ou clients) utilisent ces flux privés comme annuaire de package sur des sites mirroirs ou les redistribuent en package interne pour se protéger contre des packages compromis. Et plus particulièrement contre les hijacking ou le squating de nom. Les outils de gestion de package autorise naturellement d'index plusieurs sources pour récupérer les packages afin que ce soit simple de récupérer des packages depuis des galeries privées ou publique.

Cette configuration hybride peut être utilisée par les hackeurs afin de rentrer dans l'infrastructure au travers de ces outils de gestion de packages multiple. C'est un sujet discuté ici dans un rapport.

Il faut avoir conscience qu'en moyenne un projet open source a 180 dépendances. Cette situation a attirée les hackeurs vers la fourniture de code source open source.

Quels sont les risques d'une configuration hybride ?

Une configuration hybride commune que les clients utilise est de stocker les packages interne sur une galerie privée mais en permettant de retrouver les dépendances depuis des flux publique. Cela permet de s'assurer que les derniers packages à disposition sont automatiquement adoptés lorsque le package est référencé et qu'il n'y aura pas besoin de mise à jour. Les développeurs internes publient leurs packages sur cette galerie privé et les consommateurs peuvent vérifiés les deux types de galeries (privée et publique) pour obtenir les meilleures versions disponibles concernant leurs packages requis. Cette configuration présente un risque dans la fourniture des dépendances : l'attaque par substitution.

Cet type d'attaque s'effectue lorsqu'un hackeur découvre qu'un client utilise un package d'une galerie privée qui n'est pas présent sur les galeries publique. Une fois que le hackeur a téléversé une version plus récente du package sur la galerie, le client va télécharger automatique le package puisque c'est le même nom de fichier, seule la version change. Les services qui autorise la fusion des sources de packages autoriseront cette substitution si le package de sources publique permet de surcharger ces sources privées. Un risque qui a autant d'impact qui puisse émerger si les informations de login de l'auteur du package ont été compromis.

Que faire pour prévenir ce genre d'attaque ?

Aussi simplement que de s'assurer que le package installé ne soit pas différent du client attendu. Cela concerne les builds automatisés ou les messages d'installations sont souvent révisés en "succès". Ces attaques par substitutions installeront correctement les packages et feront échoués la build plus tard, a moins que le hackeur est cloné également les fonctionnalités du package.

 Une build sans explication ou des échecs de tests devraient être vu comme une alerte sur des risques potentiels d'attaques par substitution. Quelques packages publiques autorise les auteurs de packages à supprimer ou enlever les packages de la liste malgrés le fait que cela peut provoquer des échecs de builds, en relançant à nouveau le processus à nouveau cela fonctionnera. De ce fait, cela rend les choses difficiles à détecter.  

De plus, il faut garder à l'esprit que même si la build à échouer, l'attaquant peut toujours avoir la possibilité d'obtenir l'éxécution de code distant.

Quand un package s'installe se sont des proxies qui permet de passer au travers d'internet qui sont utilisés concernant les packages privées. Si un package malicieu est supprimé du flux, il sera toujours potentiellement en cache sur la galerie privée. Ce qui rend plus simple la détection et l'analyse pour ce type de cas. Il faut se souvenir que chaque version de package original devra être vu sur au moins une ou plusieurs versions du packages dont les versions récentes sont cachés de la galerie publique.

Ci-dessous, nous allons voir une liste de méthodes permettant de minimiser ces risques. Chaque approche a un impact différent, au travers de ces trois approches il y aura des impacts variés sur les équipes de développements, de ce fait elle pourrait en préféré une à une autre.

Stratégie de réduction du risque :

1. Référence une seule galerie privée et pas plusieurs.

 La plus part des outils permettant de diffuser les packages vont effectuer des requêtes pour découvrir tous les packages qui sont listés dans la configuration locale sans prendre en compte d'ordre ou de priorités. Npm est une exception comme il requiert de vérifier par périmétre controlé. Nous en discuterons dans la seconde stratégie.

Pour ces galeries qui ne priorisent pas les flux, il est recommandé de configurer le client afin qu'il ne prenne en compte qu'un seul flux provenant une galerie privée. dans lequel seront mis à disposition les packages automatiquement.

Il faut alors la protéger contre les mauvaises mises à jour vis à vis d'attaque ciblé.  Pour cela, il faut configurer cette galerie privée en isolant seulement une seule source afin de limiter les galeries publique avec des changements inattendues. Cela inclut les mise à jours qui échouent ou les suppressions qui cible les attaques par substitution. Les Artifact Azure offre une combinaison des flux avec une notion de priorités. Si tu ne peux pas combiner ces flux dans une seule source. Alors vous aurez besoin de vous assurer que chaque package référencé soit controlé par depuis chaque flux.

Utilisé un seul flux est fortement recommendé pour l'index des packages python, Nuget et Maven. Il est préférable de suivre ces recommendations :

  • Pour Python : Utiliser l'option index-url dans le fichier de configuration ou la ligne de commande spécifiant l'url du flux de la galerie afin de surcharger la configuration par défaut. Eviter d'ajouter des index supplémentaire qui pourront créer une gestion multi flux.
  • Pour Nuget : Il faut s'assurer que le nuget.config définissant la liste des sources commence par un <clear /> qui permet de supprimer la configuration initiale et d'ajouter la seule entrée qui vous intéresse.
  • Pour Maven : Il faut configurer un seul miroir avec <mirrorOf>*</mirrorOf> pour rediriger toutes les requêtes sur un seul flux. Une autre option est de surcharger le flux publique par défaut en le désactivant de la balises <releases/> depuis la configuration.
  • Pour Gradle : Il n'y a pas flux par défaut, cela rend plus simple la configuration avec un seul flux privé.

Attention toutefois, avec cette configuration, si vous autorisez les flux des galeries publique  à surcharger le flux de votre galerie privée, alors une attaque par substitution sera toujours possible. Vous devriez alors vous assurer que votre flux soit configurer pour désactiver cela, et s'assurer que le nommage de vos packages privés lorsqu'ils sont publiés sur les galeries publique soit nommées distinctement afin d'éviter ce type de risques.

 2. Protéger vos packages en utilisant des périmétres controlés

Quelques gestionnaires de packages supportent cette fonctionnalité, d'utiliser des périmétres, espace de noms ou préfixes. Les détails varient selon les éco-systèmes. Mais l'objectif derrière cette fonctionnalité est de pouvoir protéger une plage de nom de package. Sur cette plage, les packages seront vérifiées en vous laissant vérifier ces packages et donc réduire les risques qu'un attaquant puisse pousser un package sur votre gestionnaire de package privé et produit une confiance supplémentaire à votre équipe gérant la publication des packages à l'extérieur.

Voici ci-dessous une liste de recommendations de stratégies afin de minimiser les risques d'attaques :

  • Pour NPM : Utiliser la notion de préfix permettant de définir un Scope dans la configuration cela vous autorise à spécifier une source pour chaque package. Ainsi seulement un seul flux pour fournir les packages avec un certain préfix dans le nom. Cela limitera les risques de substitution par nom sur les gestionnaires publique. Ces options peuvent-être configurées sur la globalité de la machine ou par projet en utilisant le fichier npmrc. Des options similaires existe pour Yarn avec le fichier .yanrc.yml
  • Pour NuGet : Un préfix sur l'ID qui correspond au nom du package permet de restreindre l'upload des packages dans les gestionnaires publique. Les packages qui sont enregistrés par un préfix peuvent seulement être uploadé par des comptes utilisateurs approuvés. Ce qui permet de prévenir des attaques de substitution. Cette réservation permet d'éviter qu'un attaquant puisse revendiquer des noms associés à vos packages.

Remarques : Ces deux approches peuvent nécessiter de revoir votre nommage et donc devoir mettre à jour le code qui les utilisent.

3. Utilisation côté client des fonctionnalités de vérifications

Au delà des protections offertent par les gestionnaires de packages que nous venons de voir. Il est possible également d'intervenir depuis le côté client ou des fonctionnalités de vérifications existent également afin de protéger les attaques de ce type.

Le verrouillage des versions ou "Version pinning" est recommandé comme base pour limiter les risques, de plus ce mécanisme est supporté par la pluspart des clients. Il faut préciser une version spécifique ou une plage de version qui permet de limiter les mises à jour non controllé et donc de prévenir la récupération d'un package avec une nouvelle version qui aurait été compromise. Cependant, cela ne permet pas de prévenir le fait que le gestionnaire de package est été compromis en fournissant la même version depuis un autre flux par exemple.

La vérification d'intégrité : Ainsi il est préférable d'utiliser des flux https et avoir des mécanismes permettant de s'assurer de l'intégrité des packages. Cela permet d'ajouter une couche supplémentaire de sécurité contre les attaques par substitution. Cette vérification peut-être effectuer localement en s'assurant que le nouveau package récupérer est identique à la première version qui a été récupéré, sinon cela annulera le processus.

Voici comment mettre en place ce type de fonctionnalité :

  • Pour NPM : Installé depuis package.json automatiquement les mises à jour mettra automatiquement à jour le fichier package-lock.json qui sont inclus dans vos projets, en lançant la commande npm ci, cela répliquera cette installation et permettra de verrouiller vos versions et pouvoir en même temps effectuer une vérification au cours du temps que les packages récupérer correspondent toujours à leur version initiale. Ce processus permet de s'assurer que gestionnaire de package n'a pas été compromis. 
  • Pour NuGet : Le fichier packages.lock.json peut-être activé sur vos projets lequel sera automatiquement crée en utilisant la commande nuget restore. Lorsque les fichiers existent, ils seront inclus à vos projets et seront ensuite utilisé par nuget restore --locked-- mode qui permettra de valider que les packages n'ont pas changé en effectuant une vérification d'intégrité grâce à ce mécanisme.
  • Pour Python : Le "Pip's hash-checking mode" permet de s'assurer que le fichier récupéré correspond au hash 256 qui a été stocké dans vos projets. Ainsi toute substitution pourra être détecté depuis le serveur ou le client. La génération des hashes est requier en plus l'outil pip-compile
  • Pour Maven : les dépendances sont vérifiés lors des modifications à partir du fichier original qui a été téléversé mais il n'est pas possible d'automatiquement vérifier vis à vis d'une ancienne installation.
  • Pour Gradle : La vérification des dépendances sur ce système provient du mécanisme de Maven. Toutefois il faut l'activer en suivant cette documentation.

Comparaison des outils :

Afin de s'y retrouver avec toutes ces informations, voici un tableau synthétisant les informations ci-dessus :

 
Nom de l'outil Méthode pour gérer plusieurs sources Recommendations de configuration Bonnes pratiques

Gradle

L'outil utilise uniquement les listes qui ont été explictement définies. Toutefois, cela récupérera toujours la version la plus à jour.

Il faut spécifier une seule source de packages à partir d'un gestionnaire de packages interne. Il est préferable d'activer la vérification par checksum dans la configuration.
Maven Central De multiples sources peuvent-être définies par url ou profils de projets. Cela permet d'effectuer les requêtes par priorité. Toutefois,  Spéficier un seul mirroir pour tous les flux et s'assurer que les flux privés soit les prioritaires. Pour cela il faut activer la gestion des flux privés. Maven n'a pas de fonctionnalité native pour effectuer des vérifications d'intégrité. Il faut pour cela plutôt s'orienté vers Gradle qui permettra d'utiliser le checksum et vérifier la signature du package.
NPM Les packages sont enregistrés en lien avec un scope, cela rend npm fiable dans l'utilisation des package plus sûr avec cette fonctionnalité. Soit utiliser les scopes pour tous les packages privés ou surchargé la configuration du flux par défaut pour pointer sur le gestionnaire de package interne. Il est aussi possible d'activer la fonctionnalité package-lock.json avec npm ci qui permet de s'assurer que les packages ne sont pas compromis sans devoir mettre à jour les packages initiaux.
Nuget Gallery L'utilisation de multiple sources est à spécifier par projet ou utilisateur. La derniére version du package est installé par défaut. Il faut supprimer tous les flux par défaut par surcharge ou via le fichier de configuration nuget.config et ajouter uniquement le flux du gestionnaire de package interne. Il est préférable d'utiliser le nuget restore -locked-mode afin d'inclure un fichier packages.lock.json sur votre projet.
Pip Un flux de package par défaut auquel il est possible d'ajouter d'autres flux. Par défaut, la dernière version de toutes les sources sera installées il faut utiliser le pip's index-url dans la configuration pour spécifier le flux interne et activer l'upstream tout en évitant l'extra-index-url. Il est préférable d'utiliser la notion pip-compile pour générer les fichiers lock qui permettront l'usage des hash pour vérifier ce qui a été installé.
Yarn Les packages sont liés à un périmétre de nommage par préfix ce qui rend Yarn sur pour l'utilisation de package par scope. Soit utiliser la notion de scope pour tous les flux interne ou surchargé la configuration par défaut pour ajouter le flux interne en activant l'upstream dessus. Il est aussi préférable d'ajouter le yarm.lock avec vos sources en utilisant yarm install -immutable --immutable-cache --check-cache pour pouvoir effectuer une vérification d'intégrité sur les packages.

 

N'hésitez pas à laisser  vos retours d'expériences sur ce type d'attaques et sur la manière dont vous avez procédé pour minimiser le risque associé.


Rejoindre la conversation