L’un des mythes de l’univers DevOps est que Docker et son environnement garantissent un plus haut niveau de sécurité qu’une architecture virtualisée : sa séparation des applications offrirait une meilleure sécurité . Mais est-ce vraiment le cas ? Malheureusement non. Dans cet article, nous allons vous démontrer qu’il est possible de mettre en péril un container, d’attaquer un hôte ou des actifs propres à Docker assez simplement et efficacement.

Virtualisation Vs Conteneurisation

Ces deux composants sont diamétralement opposés, c’est pourquoi il n’y pas de réelles confrontations entre eux. Ils ont leurs propres points forts, leurs propres points faibles, mais c’est surtout leur utilisation qui est totalement différente. Alors pourquoi aborder cette problématique ?

Tout simplement parce que le sujet est récurrent lors d’échanges avec certains clients, il était donc légitime de proposer une comparaison entre ces deux méthodes. Il faut savoir que sur un environnement Docker classique, donc avec un système de conteneurisation, la séparation des ressources entre hôte et conteneur est très fine. La virtualisation quant à elle, impose des surcouches supplémentaires dans le but de garantir une étanchéité des ressources entre l’hyperviseur et les systèmes Guest. Cela a pour conséquence de provoquer un manque de flexibilité et de scalabilité (capacité d’un produit à maintenir ses fonctionnalités en cas d’augmentation de la demande), à contrario d’un système de conteneurisation.

Mais cette finesse entre les différentes couches apporte également son lot de problèmes de sûreté. Pour les identifier, voici un schéma récapitulatif des vecteurs d’attaques possibles sur un système conteneurisé :


Docker et sécurité

Cette illustration nous permet de constater que la sécurité apparaît bien perméable. Sans oublier que d’autres actifs tels que les services Registry, l’orchestration ou encore la gestion de conteneurs tel que Portainer ne sont pas intégrés dans ce graphique.

Nous allons voir maintenant quels sont les risques et les attaques auxquelles vous pouvez vous exposez, si vous utilisez un environnement conteneurisé mal configuré ou pas suffisamment sécurisé.

Compromission distante d’un conteneur ou d’un hôte

Hôte

Il est possible de compromettre de manière simple un serveur hôte lorsque le port 2375 (ou 2376 via TLS (Transport Layer Security)) du deamon Docker est exposé sans authentification. Il s’agit là d’une vulnérabilité critique, car ce processus nécessite des droits élevés quand ces derniers ne sont pas de types « root ».

Prenons un exemple. Effectuons une recherche du port 2375 sur Shodan, présentant un en-tête HTTP JSON :


 

Docker et sécurité

 


Nous constatons que 4700 résultats sont disponibles via le filtre apposé. Imaginons un instant que 10 % de ces résultats soient réellement exploitables : cela fait une belle possibilité de vecteur d’attaques pour quelqu’un de malveillant.

Cette situation  est préoccupante, puisque le deamon Docker expose une API qui permet d’effectuer un grand nombre d’opérations, comme lister les conteneurs, en lancer un, interagir avec un autre, modifier son exécution, etc.

C’est pourquoi l’un des scénarios les plus probables serait qu’un attaquant crée un conteneur sur la cible. Il exécuterait une commande spécifique pour obtenir un reverse shell puis procèderait enfin à l’évasion du conteneur via le mode privilégié préalablement activé (technique que nous aborderons un peu plus tard dans l’article). Cette méthode permet à l’attaquant de prendre le contrôle de l’hôte de manière simple, rapide et efficace.

Sachez qu’il est également possible de s’évader directement d’un conteneur en utilisant cette technique.

Conteneur

Pour mettre en péril un conteneur, il existe autant de possibilités que de technologies différentes. Ce dernier utilise en effet des services et des librairies qui peuvent être vulnérables. Pour compromettre un conteneur, il est donc nécessaire d’attaquer ces services tierces (web, SQL, cache, proxy…).

D’autant plus que dans une architecture micro-service qui repose couramment sur Docker (ou un système de conteneurisation), des rebonds sont tout à fait envisageables entre les conteneurs. Notamment lorsque des mauvaises configurations de sécurité existent (présence de mot de passe sur un conteneur permettant de se connecter à un autre…).

D’autant plus que dans une architecture micro-service qui repose couramment sur Docker, des rebonds sont tout à fait envisageables entre les conteneurs. Notamment lorsque des mauvaises configurations de sécurité s’exécutent (présence de mot de passe sur un conteneur permettant de se connecter à un autre…).

Evasion d’un conteneur dans un système Docker

Kernel panic

Il est très difficile de s’évader d’un conteneur dans un système Docker totalement à jour. Cependant, l’expérience nous a montré qu’il n’est pas rare de trouver des éléments obsolètes.

Partons donc du principe qu’un kernel, ou noyau de système d’exploitation, n’est pas à jour. Pour rappel, les conteneurs et l’hôte se partagent le kernel. Si ce fameux noyau présente une vulnérabilité, de type « élévation de privilèges » par exemple, alors un attaquant peut en tirer profit dans un conteneur compromis. Cela lui permettra d’obtenir les droits les plus élevés. Non pas sur le conteneur, mais directement sur l’hôte.

Trop de privilèges abolie le privilège

En tant qu’auditeur on peut se retrouver face à d’autres problèmes. Notamment ceux liés à la configuration, avec par exemple des droits accordés aux conteneurs et à leurs ressources qui sont trop élevés. Lorsqu’un conteneur se lance en mode privilégié, un attaquant peut alors accéder à certaines ressources de l’hôte. Ainsi, il peut avoir la capacité de modifier les fichiers présents sur le disque. Tout ça dans le but d’obtenir un accès sur la machine et compromettre ainsi son fonctionnement.

Cap ou pas cap

De la même manière, le conteneur peut posséder certaines capacités pour des raisons diverses (debug ou maintenance). Ces dernières peuvent être utilisées et détournées pour s’évader du conteneur. C’est pourquoi nous avons identifiés les plus à risques ci-dessous :

  • SYS_ADMIN : proche du mode privilégié
  • SYS_PTRACE : permet de débuguer des processus
  • SYS_MODULE : insertion de modules dans le kernel
  • DAC_READ_SEARCH : permet la lecture de ressources partagées
  • DAC_OVERRIDE : permet d’écrire sur des ressources partagées

Capacité SYS_ADMIN 

La capacité SYS_ADMIN permet, comme pour le mode privilégié, de monter des ressources comme les partitions de l’hôte. Cela signifie qu’un attaquant peut modifier des fichiers critiques pour ainsi obtenir un accès sur l’hôte et mettre à mal la sécurité des données.

Capacité SYS_PTRACE

Si la capacité SYS_PTRACE est active et que le PID namespace est partagé entre plusieurs conteneurs (ou directement avec l’hôte), alors un attaquant peut en tirer profit pour s’évader. Il peut s’injecter dans l’un des processus, rebondir sur l’actif avec lequel il est partagé et ainsi exécuter les commandes avec le même niveau de privilèges que le processus.

Capacité SYS_MODULE

Cette capacité permet au conteneur, si besoin, d’intégrer un module au kernel. Il est alors possible de créer un module malveillant qui va s’y insérer. Pour rappel, le noyau se partage entre l’hôte et les conteneurs, c’est-à-dire qu’en insérant ce module malveillant, il sera par conséquent exécuté par l’hôte qui porte le kernel. L’attaquant peut donc obtenir un accès prioritaire sur ce dernier.

Capacité DAC_READ_SEARCH

La capacité DAC_READ_SEARCH mappe certains fichiers de l’hôte sur le conteneur. En utilisant un exploit connu appelé Shocker, il est alors possible de cartographier d’autres fichiers qui sont normalement non accessibles depuis le conteneur et de les lire. Il s’agit de fichiers hôtes tel que /etc/shadow, qui contiennent les mots de passe système. Cette capacité ne permet pas directement de s’évader, mais elle peut fortement aidée.

Capacité DAC _OVERRIDE

La capacité DAC _OVERRIDE est nécessaire au fonctionnement d’un conteneur, même si seule, elle n’a pas de grande utilité pour un acteur malveillant. Cependant, combiné à la capacité évoquée plus haut (DAC_READ_SEARCH), elle permet l’évasion totale du conteneur. Un attaquant va donc pouvoir utiliser le même procédé pour mapper des fichiers de l’hôte qui sont normalement inaccessible. Le pire, c’est qu’il va être capable de les modifier. L’attaquant peut changer le mot de passe d’un utilisateur sur l’hôte directement dans le fichier /etc/shadow et accéder ainsi à la machine via SSH.

Pour vous rassurer après tous ces scénarios catastrophes, il faut savoir que la plupart des capacités nécessitent d’avoir un accès root sur le conteneur. Ce qui rajoute donc une étape supplémentaire à l’attaquant. Mais le jeu peut en valoir la chandelle…

Les Registries comme vecteur de contamination

On appelle « Registry » un système qui stocke les images s’appuyant sur les conteneurs. Les Registries permettent donc aux DevOps de mettre à disposition des images toutes faites aux systèmes, l’objectif étant de ne pas avoir à les recréer à chaque fois.

Des Registries publiques existent tout de même, comme celui proposé par Docker.

Normalement, les Registries s’internalisent pour les infrastructures privées et dans certains cas, les protections ne sont pas suffisantes face aux risques qui peuvent exister.

En effet, atteindre un Registry permettrait à un attaquant d’extraire les images qu’il contient. Ce dernier pourrait contenir des informations sensibles, comme des mots de passes, des clés d’authentification ou encore des certificats. De la même manière, il est possible d’imaginer un attaquant pousser ou réécrire une image contenant une porte dérobée sur le système d’information.

Quel que soit le système de Registry, ils fonctionnent plus ou moins de la même manière et s’appuient donc sur la même API. Ce qui rend les actions relativement simples pour un attaquant à partir du moment où il se retrouve capable de communiquer avec le fameux Registry.

Et comment se défendre ?

Il est important de comprendre que ces vulnérabilités ciblent des problèmes de configurations menant à des problèmes de sécurité. Sachez qu’il est possible d’effectuer un audit afin de vérifier s’il y a une erreur de configuration ou un oubli.

Il existe des outils permettant d’analyser la sécurité des images avant déploiement, ou encore d’analyser le comportement des conteneurs en runtime. Ces derniers sont relativement efficaces et simples à mettre en place.


Docker et sécurité

Pour l’analyse sécurité d’images Docker, on utilise le plus souvent Clair et Trivy. Concernant l’exécution en temps réel, Falco permet de lever des alertes sur des actions suspicieuses. Celles-ci ayant lieu tant sur l’hôte que sur les conteneurs, il est alors idéal de le coupler à un outil de type SIEM.

AppArmor, SELinux ou encore Seccomp peuvent réduire les risques de compromission en limitant l’exécution de certaines actions via la création de profils de sécurité.

Pour plus de détails sur ces systèmes de protection, un article devrait faire écho à celui-ci dans les prochaines semaines.

Docker et sécurité : Conclusion

Cet article n’est pas exhaustif. Il existe d’autres techniques de compromission ou d’évasion, comme l’exploitation de vulnérabilité inhérentes à Docker par exemple. Il relève tout de fois d’un mythe en disant que Docker possède davantage de sécurité que d’autres architectures. Elles sont toutes à peu de choses près sur le même piédestal.

Malheureusement, nous constatons que les vulnérabilités s’introduisent le plus souvent à cause d’un défaut de configuration, ou suite à un manque de mise à jour. Alors n’oubliez pas qu’avec Docker, une ouverture de port ou une mauvaise capacité activée peut conduire à une compromission de l’architecture entière.

Rédigé par Ricardo Matias, Consultant Sécurité chez NBS System

Thibaut Cuttat
Stagiaire Marketing & Communication