Cómo hackeé contenedores Docker al explotar vulnerabilidades de ImageMagick
11 de marzo de 2021
0 minutos de lectura¿Qué pasaría si te dijera que usar imágenes de Docker vulnerables puede exponerte a un riesgo significativo e inminente de una vulnerabilidad de seguridad de inyección de comandos que permite hackear contenedores Docker que usan esa imagen vulnerable?
En este artículo, te guiaré paso a paso por el proceso de hackeo de contenedores, en el que explotaremos una aplicación web basada en Node.js que usa una imagen base oficial de Docker para Node.js que, aunque es vulnerable, sigue siendo oficial.
Hackeo de un contenedor con una imagen vulnerable de Node.js
Para demostrar esta vulnerabilidad, usaré una versión antigua del entorno de ejecución de Node.js con una aplicación Fastify que cambia el tamaño de las imágenes a una medida específica. La acción para cambiar el tamaño de las imágenes delega el trabajo en la biblioteca ImageMagick, que ofrece una práctica herramienta de línea de comandos llamada convert.
ImageMagick es un conjunto de enlaces para lenguajes de programación y herramientas de línea de comandos que se usan con frecuencia en aplicaciones web para procesar imágenes; por ejemplo, para convertirlas de un formato a otro, cambiarles el tamaño, recortarlas y mucho más.
Sin embargo, la desafortunada realidad es que ImageMagick ha presentado muchas vulnerabilidades de seguridad a lo largo de los años. Una de ellas es la famosa vulnerabilidad ImageTragick (CVE-2016-3714). Se clasifica como validación incorrecta de entradas, pero desde 2016 existen exploits de prueba de concepto que podrían permitir la inyección remota de comandos.
Esta es una historia sobre el hackeo de contenedores, no por la falta de buenas prácticas de seguridad ni por dependencias vulnerables de aplicaciones Node.js, sino por componentes de código abierto de terceros que pueden estar presentes en una aplicación Node.js basada en Docker.
La aplicación Node.js
Empecemos con la imagen de Docker que incluye la aplicación Node.js. Para simplificar, usaré una configuración de Dockerfile muy liviana:
Ten en cuenta que este Dockerfile no sigue las pautas de seguridad para crear imágenes de Docker y solo se usa para simplificar. Consulta nuestra guía rápida sobre 10 prácticas recomendadas para contenerizar aplicaciones web de Node.js con Docker y conoce las prácticas recomendadas de seguridad.
La aplicación web Fastify ofrece una ruta **/**upload que acepta cargas de archivos y ejecuta un proceso, /usr/bin/convert, con la imagen cargada para convertirla a un tamaño determinado y redirigir al usuario a una página de éxito.
El código usa una API segura de Node.js para ejecutar procesos y no permite la concatenación de cadenas.
¿Es una buena práctica iniciar shells del sistema y ejecutar procesos? En realidad, no, al menos si no hay una buena justificación. Si se trata de un proceso worker de Node.js que consume mensajes de una cola o recibe tareas de un programador de trabajos, probablemente sea adecuado para este caso.
Pero ¿quiénes somos para juzgar si en el evento Google I/O 2017 se hizo una demostración en vivo de un caso de uso similar?

Hackeemos un contenedor de Docker
Empecé por importar el proyecto a Snyk, que detecta automáticamente los archivos de manifiesto compatibles; en mi caso, son el package.json y el Dockerfile de la aplicación.
Como puedes ver, incluso la versión más reciente de la imagen Node.js 6 (node:6-stretch) incluye 866 vulnerabilidades de seguridad de forma predeterminada. Por suerte, usamos Snyk, que nos recomienda varias opciones para actualizar la imagen base y mejorar la seguridad de la aplicación de al menos dos maneras:
un menor número de vulnerabilidades, lo que reduce la superficie de ataque.
imágenes base en las que el propio entorno de ejecución de Node.js no sea vulnerable. Esta es una ventaja muy importante y, a menudo, pasa desapercibida al usar las recomendaciones de imágenes base.

Convirtamos la vulnerabilidad de seguridad ImageTragick presente en la versión de ImageMagick de la imagen del contenedor en un ataque de inyección remota de comandos.
El exploit usa archivos de imagen especialmente diseñados para eludir la función de análisis de la funcionalidad de delegados de la biblioteca ImageMagick. Esta función de ImageMagick ejecuta comandos del sistema asociados con instrucciones dentro del archivo de imagen. Al escapar del contexto de entrada esperado, un atacante puede inyectar comandos del sistema.
Este repositorio de Git incluye un exploit de prueba de concepto para ejecutar comandos de forma remota mediante la descarga de netcat y obtener un shell inverso en distribuciones que no incluyen netcat de forma predeterminada, como Debian wheezy.
Este es el aspecto de la carga útil de prueba de concepto en el archivo de imagen rce1.jpg:
El usuario controla este archivo de imagen, por lo que un atacante malicioso podría crear esa carga útil, que ejecuta un comando compatible con los sistemas operativos tipo UNIX para crear un archivo vacío; en este caso, touch rce1.
Una vez creada la imagen del contenedor, podemos ejecutarla:
Nuestra sencilla aplicación web nos permite cargar un archivo:

Al hacer clic en el botón Resize para procesar el archivo rce1.jpg, se activa la inyección de comandos.
Conectémonos a la aplicación que se ejecuta en el contenedor de Docker para validar este ataque. Como puedes ver, se creó un archivo nuevo llamado rce1.jpg en el directorio raíz de la aplicación Node.js:

Resumen del hackeo de contenedores
Seguro que no te sorprende demasiado la cantidad de vulnerabilidades de seguridad presentes en varias imágenes de Docker. A nosotros tampoco, ya que observamos que las 10 imágenes más populares de Docker Hub contenían vulnerabilidades de seguridad, como se explica en nuestro informe El estado de la seguridad del código abierto.

El ataque que demostramos aquí elude por completo todas las convenciones de programación segura y va más allá de la seguridad del entorno de ejecución de Node.js o de las dependencias de módulos de código abierto incluidas en la aplicación. Esto destaca la necesidad de proteger tus imágenes de Docker. ¡Para eso se creó Snyk!
Al destacar las vulnerabilidades según su prioridad, Snyk te ofrece recomendaciones para corregirlas, como otras imágenes base a las que puedes cambiarte:

Si quieres recrear el ataque paso a paso, puedes seguir las instrucciones del archivo README del repositorio de código abierto, donde también se explica cómo realizar un ataque de shell inverso remoto aprovechando esta vulnerabilidad de ImageTragick.
¿Qué sigue?
Tanto si los repositorios de tu proyecto de aplicaciones en Docker son de código abierto como privados, puedes usar el nivel gratuito de Snyk para probar tus imágenes de Docker y corregir las vulnerabilidades de seguridad conocidas. ¡Aprovecha también para analizar y corregir tus aplicaciones Node.js!
Ten en cuenta que no se recomienda usar el Dockerfile que se muestra aquí ni el del repositorio de código abierto que lo acompaña, ya que no sigue prácticas de seguridad. En su lugar, consulta estas publicaciones del blog para conocer prácticas valiosas de seguridad para Docker:
10 prácticas recomendadas para contenerizar aplicaciones web de Node.js con Docker
10 prácticas recomendadas para crear un contenedor Java con Docker
Docker para desarrolladores de Node.js: 5 cosas que debes saber para no poner en riesgo tu seguridad
Seguridad de contenedores centrada en los desarrolladores
Snyk encuentra y corrige automáticamente vulnerabilidades en imágenes de contenedores y cargas de trabajo de Kubernetes.
