¡Menudo clúster!: vulnerabilidad de los volúmenes locales de Kubernetes
Comentario editorial y adicional de Tricia Howard
Resumen ejecutivo
Tomer Peled, investigador de seguridad de Akamai, ha detectado recientemente una vulnerabilidad de gravedad alta en Kubernetes a la que se asignó el CVE-2023-5528 con una puntuación del CVSS de 7,2.
Esta vulnerabilidad permite la ejecución remota de código con privilegios del SISTEMA en todos los terminales de Windows dentro de un clúster de Kubernetes. Para explotar esta vulnerabilidad, el atacante debe aplicar archivos YAML maliciosos en el clúster.
Esta vulnerabilidad puede dar lugar a un control total de todos los nodos de Windows de un clúster.
Esta vulnerabilidad se puede aprovechar en las instalaciones predeterminadas de Kubernetes (anteriores a la versión 1.28.4) y se ha probado tanto en implementaciones locales como en Azure Kubernetes Service.
- En esta entrada de blog, proporcionamos un archivo YAML de prueba de concepto, así como una regla de Open Policy Agent (OPA) para bloquear esta vulnerabilidad.
Introducción
Kubernetes y los contenedores en general se han convertido en una fuerza predominante en el mundo de la seguridad y, como tal, han sido un punto de interés para investigadores de todo el mundo (incluidos nosotros). Nuestro recorrido de investigación nos llevó inicialmente al CVE-2023-3676 (CVSS de 8,8): una vulnerabilidad de inyección de comandos que se podría explotar mediante la aplicación de un archivo YAML malicioso en el clúster. Dado que el marco de Kubernetes utiliza archivos YAML prácticamente para todo (desde la configuración de la interfaz de red de contenedores hasta la gestión de módulos e incluso la gestión de secretos), una explotación de esta vulnerabilidad podrías tener consecuencias desastrosas.
El descubrimiento de esta vulnerabilidad llevó al descubrimiento de otras dos que comparten la misma causa raíz: una llamada a una función no segura y la falta de saneamiento de la entrada de datos del usuario.
La falta de saneamiento del parámetro subPath en los archivos YAML que crea módulos con volúmenes ofrece la oportunidad para una inyección maliciosa. Este fue el hallazgo original, pero al final de esa investigación, observamos un posible lugar en el código que parecía conducir a otra vulnerabilidad de inyección de comandos. Después de varios intentos, pudimos obtener un resultado similar: ejecutar comandos como el servicio "kubelet" (privilegios del SISTEMA). Es en este punto donde iniciamos hoy nuestro recorrido con el CVE-2023-5528.
En esta entrada de blog, repasaremos los detalles de la vulnerabilidad y los problemas del código fuente de Kubernetes que la hacen posible, además de analizar el parche del equipo de Kubernetes y su eficacia. Aunque se recomienda aplicar parches lo antes posible, hemos incluido guías breves sobre cómo buscar los nodos afectados y cómo aplicar una regla de Open Policy Agent (OPA) para ayudar a detectar y bloquear este tipo de comportamiento.
En esta publicación se hace hincapié una vez más en la necesidad de verificar los YAML de configuración de Kubernetes, ya que falta el saneamiento de entrada en varias áreas de código de Kubernetes en sí y de sus proyectos adicionales (como la entrada, por ejemplo).
Detalles de la vulnerabilidad
Antes de adentrarnos en los detalles de esta vulnerabilidad en sí, debemos comprender algunos componentes clave que contiene Kubernetes.
¿Qué son los volúmenes de Kubernetes?
Los volúmenes de Kubernetes son una función diseñada para permitir el uso compartido de datos entre módulos o para almacenarlos de forma persistente más allá del ciclo de vida de un módulo. Existen muchos tipos de volúmenes diferentes que pueden utilizar los desarrolladores. Por ejemplo, en nuestra investigación anterior sobre el CVE-2023-3676, utilizamos volúmenes hostPath . Para esta vulnerabilidad, nos centraremos en los volúmenes locales, otro tipo de volumen dentro de Kubernetes. Los volúmenes locales están diseñados para permitir a los usuarios montar particiones de disco dentro de un módulo, mientras que los volúmenes hostPath están diseñados para permitir a los usuarios montar directorios desde su nodo (host) en un módulo.
Cuando se crea un módulo que incluye un volumen local, el servicio kubelet llegará (finalmente) a la función "MountSensitive()". En su interior, hay una llamada de línea de comandos (cmd) a "exec.command", que crea un enlace simbólico entre la ubicación del volumen en el nodo y la ubicación dentro del módulo (Figura 1).
Muchos terminales utilizan alguna versión de concatenación de comandos (Figura 2) en sus operaciones para facilitar su uso. Este es también el caso del símbolo del sistema de Windows (cmd) , mediante el uso del token "&&", el terminal ejecutará dos o más comandos uno tras otro.
C:\Users\user>echo "by using &&" && echo "we can execute multiple commands in the same command line"
"by using &&"
"we can execute multiple commands in the same command line"
C:\Users\user>
Fig. 2: Concatenación de comandos en cmd
El hecho de que podamos controlar uno de los parámetros en la ejecución de cmd significa que podemos utilizar la inyección de comandos. Sin embargo, existen algunos requisitos previos para ello: para que los usuarios puedan utilizar volúmenes locales, deben especificar o crear un persistentVolume (volumen persistente).
¿Qué son los volúmenes persistentes?
persistentVolumes son los recursos de almacenamiento que puede crear un administrador de clústeres para aprovisionar por adelantado un espacio de almacenamiento que tendrá una duración superior a la vida útil del módulo (Figura 3). Una vez que se ha creado un persistentVolume , un usuario puede solicitar espacio de almacenamiento mediante una persistentVolumeClaim (reclamación de volumen persistente).
Aquí es donde se puede colocar la inyección. Un atacante puede cambiar el valor del parámetro "local.path" dentro del archivo YAML persistentVolume para agregar un comando malicioso que se ejecutará durante el proceso de montaje.
En la Figura 4, hemos utilizado el archivo legítimo "&calc.exe&&" (que abre una calculadora en el nodo), pero este proceso se puede utilizar para un resultado mucho más malicioso.
La figura 5 muestra qué aspecto tendrá una explotación exitosa en un nodo de destino después de la inyección de nuestro comando de calculadora "malicioso".
Análisis de parches
En un esfuerzo por eliminar la oportunidad de inyección, el equipo de Kubernetes decidió eliminar la llamada de cmd y sustituirla por una función GO nativa que realizará la misma operación "os.Symlink()" (Figura 6).
Ahora bien, la biblioteca "os" de GO solo realizará una operación de enlace simbólico, como se pretendía inicialmente.
¿Soy vulnerable?
Para que los usuarios se vean afectados por esta vulnerabilidad, Kubernetes debe ser de una versión anterior a la 1.28.4. Si aún no ha aplicado este parche, es una buena idea darle prioridad. Esto se recomienda especialmente para las organizaciones con nodos de Windows dentro de un clúster, ya que ahí reside la vulnerabilidad.
Afortunadamente, este no parece ser el estándar en el sector. Un administrador puede probar fácilmente si el clúster de la organización contiene nodos de Windows ejecutando el comando que se muestra en la figura 7 en el controlador del clúster.
root@controller:~/$ kubectl get nodes -o wide --show-labels | grep “os=windows”
akswin000000 Ready agent 4d17h v1.26.6 agentpool=win,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=windows…
akswin000001 Ready agent 4d17h v1.26.6 agentpool=win,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=windows…
root@controller:~/$
Fig. 7: Comando que muestra los nodos de Windows en un clúster
Es fácil determinar si es vulnerable. Compruebe la parte “os=windows". Si no hay ningún nodo de Windows, el comando no tendría ninguna salida, lo que significa que no es vulnerable.
Mitigación
La única solución disponible es aplicar una parche de Kubernetes de una versión posterior a la 1.28.3.
Dicho esto, sabemos que la aplicación inmediata de un parche no es algo que se pueda hacer en algunas organizaciones y redes. Como ayuda en el caso de que exista el riesgo de que no se aplique el parche, hemos proporcionado una regla de OPA que sirve para detectar y bloquear este tipo de comportamiento.
package kubernetes.admission
deny[msg] {
input.request.kind.kind == "PersistentVolume"
path := input.request.object.spec.local.path
contains(path, "&")
msg := sprintf("malicious path: %v was found", [path])
}
OPA es un agente de código abierto que permite a los usuarios recibir datos sobre el tráfico que entra y sale de los nodos y tomar medidas basadas en políticas sobre los datos recibidos.
Tenga en cuenta que esta vulnerabilidad solo afecta a los nodos de Windows. Si su clúster de Kubernetes no tiene ningún nodo de Windows, no tiene que apresurarse a aplicar el parche para esta vulnerabilidad específica. No obstante, es importante que aplique el parche de todos modos en cuando tenga ocasión.
Dado que el problema reside en el código fuente, esta amenaza permanecerá activa y su explotación probablemente aumentará. Por este motivo, recomendamos encarecidamente la aplicación del parche en el clúster incluso si este no tiene nodos de Windows.
Conclusión
Esta vulnerabilidad es un buen ejemplo de por qué el modelo de responsabilidad compartida es crucial para la seguridad. Ser consciente de la falta de saneamiento de entrada en el código fuente de Kubernetes, le permite adoptar precauciones externas para evitar un grave impacto en la seguridad.
Solo en 2023 se descubrieron siete vulnerabilidades de inyección de comandos diferentes, con más oportunidades en otras áreas del código. Los equipos azules y sus organizaciones deberían estar más alerta ante esta tendencia creciente e intentar supervisar el contenido de los archivos YAML, ya que pueden contener amenazas ocultas. La regla de OPA que proporcionamos en esta publicación puede ayudar en este esfuerzo.
En el grupo de inteligencia sobre seguridad de Akamai seguiremos supervisando esta amenaza y otras similares, y publicando nuestros hallazgos. Para estar al tanto de las novedades relacionadas con esta vulnerabilidad y consultar otras investigaciones sobre seguridad, puede seguirnos en X (anteriormente Twitter).
Queremos dar las gracias al equipo de Kubernetes por su rápida respuesta y facilidad de comunicación..
Tiempo de divulgación
01/11/2023: vulnerabilidad revelada al equipo de Kubernetes.
11/11/2023: CVE asignadas por el equipo de Kubernetes
14/11/2023: publicación por parte de Kubernetes de las correcciones para las CVE
13/03/2024: publicación de esta entrada de blog