Exploit Me, Baby, One More Time: Inyección de comandos en la consulta de registros de Kubernetes
Comentario editorial y adicional de Tricia Howard
Resumen ejecutivo
Tomer Peled, investigador de seguridad de Akamai, descubrió recientemente una vulnerabilidad en Kubernetes a la que se asignó el código CVE-2024-9042.
La vulnerabilidad permite la ejecución remota de código (RCE) con privilegios del SISTEMA en todos los terminales de Windows de un clúster de Kubernetes. Para explotar esta vulnerabilidad, el clúster debe estar configurado para ejecutar el nuevo mecanismo de registro "Consulta de registros".
La vulnerabilidad se puede activar con una simple solicitud GET al nodo remoto.
La explotación correcta de esta vulnerabilidad puede provocar la usurpación de todos los nodos de Windows de un clúster.
Esta vulnerabilidad se puede explotar en las instalaciones predeterminadas de Kubernetes que aceptaron el uso de funciones beta (anteriores a la versión 1.32.1) y se ha probado tanto en implementaciones locales como en Azure Kubernetes Service.
En esta entrada de blog, proporcionamos un comando curl de prueba de concepto y hablaremos de las posibles mitigaciones.
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 a CVE-2023-3676: una vulnerabilidad de inyección de comandos que se podría explotar al aplicar un archivo YAML malicioso al clúster. Esta investigación nos llevó a descubrir otros problemas en el código fuente de Kubernetes que también permiten la usurpación del clúster.
No solo encontramos esas vulnerabilidades: también hicimos un descubrimiento importante en el proyecto adicional git-sync. Presentamos estos resultados en Red Team Village en DEF CON 32 y, durante la preparación de la charla, nos encontramos con el tema de esta entrada de blog. Detectamos otra oportunidad para la inyección de comandos: la función beta de Kubernetes para su marco de registro más amplio, la consulta de registros.
¿Qué es la consulta de registros?
La consulta de registros permite a los usuarios consultar el estado del sistema de los equipos remotos mediante la CLI o un comando curl. Por ejemplo, un usuario puede escribir el siguiente comando para consultar el estado del servicio kubelet en un nodo remoto:
kubectl get --raw "/api/v1/nodes/node-1.example/proxy/logs/?query=kubelet"
Cuando vimos el comando de ejemplo anterior, nos vino a la mente nuestra investigación anterior: ¿se validaría la consulta que se envía al equipo remoto?
La Figura 1 es un ejemplo del código fuente para la consulta de registros en el que se han creado varios comandos de PowerShell.
Estos comandos se utilizan para recuperar registros de un nodo determinado en función de una serie de parámetros diferentes, como los nombres de servicio de Windows, el patrón de tiempo de uso, etc.
Por la experiencia anterior (CVE-2023-3676), sabemos que Kubernetes no valida necesariamente la entrada del usuario antes de insertarla como parámetro al crear un comando de PowerShell. Queríamos confirmar si ese es también el caso de la consulta de registros, y descubrimos que los nombres de servicio efectivamente se están validando a través de una regla regex predefinida (Figura 2).
El nombre reServiceNameUnsafeCharacters sugiere que esta comprobación es solo para servicios. Esta hipótesis se refuerza aún más por el hecho de que no hay ninguna otra regla regex presente en el proceso de análisis.
Después de examinar otros parámetros, era evidente que, efectivamente, el único parámetro que se está validando es el nombre del servicio. Esto significa que podemos examinar otros parámetros con la esperanza de lograr una inyección de comandos. En la consulta de registros, hay varios parámetros opcionales diferentes que los usuarios pueden proporcionar, pero el único que es una cadena es el parámetro Pattern (Figura 3). Como hemos mencionado anteriormente, no hay validación para el parámetro (aparte de su longitud).
Cabe destacar que explotar esta vulnerabilidad no fue fácil. Intentamos activar los comandos en el equipo remoto utilizando varias permutaciones diferentes para la carga útil de PowerShell, como agregar corchetes, utilizar separadores, etc., pero nada parecía funcionar. Tampoco había errores, lo que dificultaba aún más averiguar cómo funcionaba la explotación.
La carga útil no es el problema
Después de probar muchos métodos diferentes de inyección de comandos, finalmente descubrimos que el problema no está en la carga útil en sí. Para explotar esta vulnerabilidad, necesitábamos especificar un servicio que registrara su estado de forma nativa en el seguimiento de eventos para Windows (ETW) y no en el marco klog normal, ya que la comprobación de vulnerabilidad solo existe con el registro en ETW.
Un entorno de Kubernetes que utilice Calico (una conocida interfaz de red de código abierto para Kubernetes) proporciona una forma fiable de explotar esta vulnerabilidad a través de Non-Sucking Service Manager (NSSM).
En una configuración de Calico, NSSM suele estar presente para controlar el estado de los servicios de Kubernetes. En otros entornos/configuraciones utilizan diferentes formas de gestionar los estados de servicio.
- Kubernetes no gestiona la salida de registro de NSSM, lo que garantiza que los registros se escriben directamente en ETW y no a través de klog.
Ejemplo de una consulta de explotación
Este es el aspecto que tendría una consulta de explotación:
curl "<Kubernetes API Proxy server IP>/api/v1/nodes/<NODE name>/proxy/logs/?query=nssm&pattern=’\$(Start-process cmd)’"
*El token de autenticación necesario para comunicarse con el servidor de API se redacta.
Es posible que los lectores con vista de lince se hayan percatado de los apóstrofos que se han utilizado para escapar de nuestro comando malicioso. Los necesitamos porque Kubernetes inserta nuestra entrada con el siguiente comando:
…Where-Object -Property Message -Match '%s'...
De forma similar a los ataques de inyección SQL clásicos, necesitamos agregar apóstrofos para escapar su patrón y que nuestra entrada se analice como un comando independiente (Figura 4).
Parche prioritario
Para que se vea afectado por esta vulnerabilidad, debe tener una versión de Kubernetes que sea anterior a la 1.32.1. Si aún no ha aplicado el parche a esta vulnerabilidad, es una buena idea darle prioridad. Esto es especialmente cierto en el caso de 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 siguiente comando en el controlador del clúster (Figura 5).
¿Soy vulnerable?
Es fácil determinar si es vulnerable. Observe 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.
Puesto que se trata de una función beta, el clúster también debe configurarse para utilizar el propio marco.
Para comprobar si la función está activada, los administradores pueden consultar el parámetro de inicio "feature-gate" para "kubelet". Puede obtener más información en el sitio de Kubernetes.
Análisis de parches
Para corregir esta vulnerabilidad, Kubernetes decidió utilizar una variable de entorno denominada "kubelet_pattern" antes de pasar el valor al propio comando de PowerShell.
De esta forma, la entrada del usuario se considerará como un literal de cadena en lugar de una subexpresión que debe evaluarse.
Mitigación
Para ayudar a mitigar esta vulnerabilidad, los administradores pueden utilizar el módulo de control de acceso basado en funciones (RBAC) para controlar quién tiene acceso a la consulta de registros e incluso desactivar el acceso a ella por completo. El RBAC es un método que segmenta las operaciones de usuario según el usuario en cuestión. Por ejemplo, los usuarios solo podrán crear módulos en su propio espacio de nombres o solo podrán ver la información de los espacios de nombres para los que tienen permiso. Esto reduciría el riesgo de RCE no solo en ejecución, sino también en detección: el comportamiento anómalo del usuario es a menudo una señal reveladora de malicia.
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 preocuparse por esta vulnerabilidad. Sin embargo, se recomienda mantener los parches siempre actualizados para evitar otras vulnerabilidades desconocidas.
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
En esta entrada de blog hemos mostrado cómo un atacante con privilegios de consulta puede ejecutar comandos en cualquier nodo de Windows dentro del clúster. Esta vulnerabilidad se puede explotar mediante un simple comando curl, en lugar de tener que enviar un archivo YAML. Este hecho impone un gran riesgo porque su explotación es más difícil de mitigar y detectar. Los problemas de saneamiento de Kubernetes no son exclusivos de la consulta de registros, como ya hemos comentado anteriormente.
Los miembros de los equipos azules deben estar atentos a los comportamientos inusuales que se presentan en sus organizaciones. Este vector de ataque puede provocar la usurpación del clúster; por lo tanto, es importante para nosotros concienciar y ayudar a los administradores de seguridad para que conozcan el peligro potencial.
El grupo de inteligencia sobre seguridad de Akamai seguirá investigando amenazas como estas e informando sobre ellas para nuestros clientes y la comunidad de seguridad en general. Para obtener actualizaciones en tiempo real sobre todo aquello en lo que estamos trabajando, síganos en X (anteriormente Twitter).
Queremos dar las gracias al equipo de Kubernetes por su respuesta y su discurso.
Cronología
28/06/2024: vulnerabilidad revelada al equipo de Kubernetes.
18/07/2024: el equipo de Kubernetes comenzó a trabajar para solucionar el problema.
30/07/2024: CVE asignadas por el equipo de Kubernetes
16/01/2025: publicación por parte de Kubernetes de las correcciones para las CVE.
24/1/2025: entrada de blog publicada.