Skip to main content

Comment j’ai piraté des conteneurs Docker en exploitant des vulnérabilités d’ImageMagick

Écrit par
Blog Header Hacking Docker

11 mars 2021

0 minutes de lecture

Et si je vous disais que l’utilisation d’images Docker vulnérables vous expose à un risque important et imminent de faille d’injection de commandes, permettant de pirater les conteneurs Docker qui les utilisent ?

Dans cet article, je vous guide pas à pas dans le piratage d’un conteneur, en exploitant une application web basée sur Node.js qui utilise une image Docker de base officielle pour Node.js, mais vulnérable.

Piratage d’un conteneur à partir d’une image Node.js vulnérable

Pour illustrer cette vulnérabilité, je vais utiliser une ancienne version du runtime Node.js avec une application Fastify qui redimensionne des images. Le redimensionnement consiste à déléguer le traitement à la bibliothèque ImageMagick, qui propose le pratique outil en ligne de commande convert.

ImageMagick est un ensemble de liaisons pour langages de programmation et d’outils en ligne de commande couramment utilisés dans les applications web pour traiter des images : conversion d’un format à un autre, redimensionnement, recadrage, etc.

Malheureusement, ImageMagick a présenté au fil des ans de nombreuses vulnérabilités de sécurité, dont la célèbre faille ImageTragick (CVE-2016-3714). Cette faille est classée comme une validation incorrecte des entrées, mais des exploits de preuve de concept circulent depuis 2016 et peuvent entraîner une injection de commandes à distance.

Cette histoire montre comment des conteneurs peuvent être piratés, non pas en raison du non-respect des bonnes pratiques de sécurité ou de dépendances vulnérables des applications Node.js, mais à cause de composants open source tiers qui peuvent être présents dans une application Node.js basée sur Docker.

L’application Node.js

Commençons par l’image Docker qui regroupe l’application Node.js. Pour simplifier, je vais utiliser une configuration Dockerfile très légère :

FROM node:6.1.0-wheezy 

RUN mkdir /usr/src/goof
COPY . /usr/src/goof
WORKDIR /usr/src/goof

RUN npm update
RUN npm install
CMD ["npm", "start"]

Notez que ce Dockerfile ne respecte pas les recommandations de sécurité pour la création d’images Docker et qu’il est présenté ainsi uniquement par souci de concision. Consultez notre aide-mémoire sur les 10 bonnes pratiques pour conteneuriser des applications web Node.js avec Docker pour connaître les bonnes pratiques de sécurité.

L’application web Fastify expose une route **/**upload qui accepte le téléversement de fichiers et exécute le processus /usr/bin/convert avec l’image envoyée pour la convertir à la taille demandée, puis redirige l’utilisateur vers une page de confirmation.

fastify.post("/upload", (req, res) => {
  req.multipart(uploadFileHandler, err => {
    if (err) {
      fastify.log.error(err);
    }

    fastify.log.info("upload completed");
    fastify.log.info("commencing image resizing");

    child_process.execFile(
      "/usr/bin/convert",
      [FILE_OUTPUT, "-resize", "280x150", FILE_RESULT],
      () => {
        res.redirect("/result.html");
      }
    );
  });
});

Le code utilise bien une API Node.js sécurisée pour exécuter le processus, sans concaténer de chaînes.

Est-ce une bonne pratique de lancer des shells système et d’exécuter des processus ? Pas vraiment, du moins sans bonne raison. Si ce processus de travail Node.js traite des messages d’une file d’attente ou reçoit des tâches d’un ordonnanceur, ce cas d’usage est probablement pertinent.

Mais qui sommes-nous pour juger, quand un cas d’usage similaire a été présenté en direct lors de Google I/O 2017 ?

Lecteur vidéo affichant du code Firebase et Google Cloud qui télécharge, redimensionne et téléverse des images.

Piratons un conteneur Docker

J’ai commencé par importer le projet dans Snyk, qui détecte automatiquement les fichiers manifeste pris en charge ; dans mon cas, il s’agit du package.json et du Dockerfile de l’application.

Comme vous pouvez le constater, même la dernière version de l’image Node.js 6 (node:6-stretch) contient par défaut 866 vulnérabilités de sécurité. Heureusement, nous utilisons Snyk, qui nous recommande plusieurs autres images de base pouvant améliorer la sécurité de l’application de deux façons au moins :

  1. un nombre réduit de vulnérabilités, ce qui diminue la surface d’attaque ;

  2. des images de base dans lesquelles le runtime Node.js lui-même n’est pas vulnérable. Un avantage majeur, souvent négligé, des recommandations d’images de base.

Vue d’ensemble du projet Snyk présentant les détails du Dockerfile et un tableau de recommandations de mise à niveau de l’image de base, des vulnérabilités et de leur niveau de gravité.

Transformons la faille ImageTragick présente dans la version d’ImageMagick de l’image du conteneur en attaque par injection de commandes à distance.

L’exploit s’appuie sur des fichiers image spécialement conçus pour contourner la fonction d’analyse des délégués de la bibliothèque ImageMagick. Cette fonctionnalité exécute des commandes système associées aux instructions présentes dans le fichier image. En sortant du contexte d’entrée attendu, un attaquant peut injecter des commandes système.

Ce dépôt Git contient un exploit de preuve de concept permettant l’exécution de commandes à distance, avec le téléchargement de netcat pour obtenir un shell inversé sur les distributions qui n’incluent pas netcat par défaut (comme Debian wheezy).

Voici à quoi ressemble la charge utile de preuve de concept dans le fichier image rce1.jpg :

push graphic-context
viewbox 0 0 640 480
fill 'url(https://127.0.0.0/oops.jpg"|touch "rce1)'
pop graphic-context

Le fichier image est ici contrôlé par l’utilisateur. Un attaquant malveillant pourrait donc créer cette charge utile, qui exécute une commande prise en charge par les systèmes d’exploitation de type UNIX pour créer un fichier vide — ici, touch rce1.

Une fois l’image du conteneur créée, nous pouvons l’exécuter :

docker run --rm --name rce rce

Notre application web toute simple nous permet de téléverser un fichier :

Page web simple affichant « hello there », un contrôle de téléversement d’image et un bouton Redimensionner pour convertir les photos au format 280 × 150 pixels.

Lorsque nous cliquons sur le bouton Resize pour traiter le fichier rce1.jpg, l’injection de commandes se déclenche.

Connectons-nous à l’application exécutée dans le conteneur Docker pour vérifier cette attaque. Comme vous pouvez le constater, un nouveau fichier nommé rce1.jpg a été créé dans le répertoire racine de l’application Node.js :

Terminal affichant des commandes Docker et le contenu d’un répertoire de projet Node.js, avec notamment package.json, Dockerfile, des exploits et un fichier mis en évidence.

En résumé : le piratage de conteneurs

Je suis sûr que le nombre de vulnérabilités de sécurité présentes dans diverses images Docker ne vous étonne pas outre mesure. Nous non plus, puisque nous avons constaté que les 10 images Docker les plus populaires sur Docker Hub contenaient des vulnérabilités, comme le montre notre rapport State of Open Source Security.

Graphique à barres intitulé « Vulnérabilités dans les images de conteneurs officielles », comparant le nombre de vulnérabilités en 2018 et 2019 pour Node, PostgreSQL, Nginx et d’autres images.

L’attaque présentée ici contourne complètement toutes les conventions de codage sécurisé et va au-delà de la sécurité du runtime Node.js ou des dépendances des modules open source Node.js intégrées à l’application. Elle souligne à quel point il est nécessaire de sécuriser vos images Docker. C’est précisément pour cela que Snyk a été créé !

En mettant en évidence les vulnérabilités par ordre de priorité, Snyk vous propose des conseils de correction sous la forme d’autres images de base vers lesquelles vous pouvez migrer :

Tableau recommandant des mises à niveau des images de base Node.js, avec le nombre de vulnérabilités et leur niveau de gravité : élevé, moyen ou faible.

Si vous souhaitez reproduire l’attaque étape par étape, suivez les instructions du README dans le dépôt open source, qui explique également comment réaliser une attaque par shell inversé à distance à partir de cette vulnérabilité ImageTragick.

Et ensuite ?

Que les dépôts de votre projet d’application conteneurisée avec Docker soient open source ou privés, vous pouvez utiliser l’offre gratuite de Snyk pour détecter et corriger les vulnérabilités de sécurité connues dans vos images Docker. Profitez-en aussi pour analyser et corriger vos applications Node.js !

Notez que le Dockerfile présenté ici et dans le dépôt open source associé n’est pas recommandé, car il ne respecte pas les bonnes pratiques de sécurité. Consultez plutôt les articles de blog suivants pour découvrir des pratiques utiles de sécurité Docker :

La sécurité des conteneurs, pensée pour les développeurs

Snyk détecte et corrige automatiquement les vulnérabilités dans les images de conteneurs et les workloads Kubernetes.

Lire la suite

feature customer snowflake
Article

Sécuriser dès la conception : découvrez l’intégration de Snyk Studio à Snowflake Cortex Code

Snyk Studio s’intègre à Snowflake Cortex Code pour analyser le code généré par l’IA, les dépendances et les conteneurs à la recherche de vulnérabilités, dès le développement.

blog feature pypi spoof
Article

La Snyk AI Security Platform complète, gratuite pour les responsables de projets open source

Les responsables de projets open source croulent sous les signalements de vulnérabilités avérées et ont besoin d’aide pour les prioriser, les corriger et déployer les correctifs plus rapidement. Le Secure Developer Program de Snyk donne aux projets admissibles un accès gratuit à la Snyk AI Security Platform.

feature insights announcement
Blog

Compromission de la chaîne d’approvisionnement Node-gyp : un ver npm à propagation autonome dissimulé dans binding.gyp

Un nouveau ver npm détourne binding.gyp pour déclencher node-gyp lors de l’installation et permettre à des packages malveillants d’exécuter du code sans scripts de cycle de vie. Il vole des identifiants, s’installe durablement sur GitHub et se propage de mainteneur en mainteneur.