Всем привет, сегодня хотел бы разобрать самые базовые дыры в Kubernetes (или сокращенно K8S). Но давайте сначала разберемся с базовыми понятиями в K8S.
Зачем вообще нужен Kubernetes (K8S)?
K8S предназначен для следующих целей:
- Предназначен для контейнизированных приложений
- Автоматизация развертывания приложений
- Автоматизации масштабирования приложений
- Автоматизация управлением приложением
Основной используемый компонент K8S - Cluster ()
K8S Cluster создается с помощью Nodes (некого сервера)
Есть два типа Nodes:
- Worker Node - сервер на котором запускаются и работают контейнеры
- Master Node - сервер, который управляет Worker Node
Master Node имеет три главных процесса k8s:
- kube-apiserver
- kube-controller-manager
- kube-scheduler
- kubelet
- kube-proxy
Функции у K8S следующие:
- Service discovery and load balancing - k8s дает доступ к контейнеру через IP/Worker Node-порт/DNS. При создании копии контейнера k8s сделает load balancing вашего контейнера
- Storage orchestration - присоединение локального (или AWS, Docker Hub) диска к одному или нескольким контейнерам
- Automated rollouts and rollbacks - автоматическое обновление docker image на новую/старую версию
- Automating bin packing - когда вы указываете сколько ресурсов нужно для вашего контейнера, то k8s выбирает подходящую Worker Node для этого контейнера.
- Self Healing - замена нерабочего контейнера на рабочий
- Secret and configuration management - позволяет хранить конфиденциальную информацию вне контейнера.
- Pod - состоит из контейнера/контейнеров
- Deployment - копия podов
- Service - дает доступ к deployment
- Node - сервер где работает service
- Cluster - объединение нескольких Node
Основное взаимодействие с K8S происходит через kubectl, который позволяет выполнять команды для ваших кластеров. Снизу я разобрал буквально несколько команды, но все остальные приколы можете найти
Ссылка скрыта от гостей
Список всех pod:
kubectl get pods
Запуск pod:
kubectl run <nickname_for_pod> –image=<name_of_image>
Теперь предлагаю перейти уже к самим вулнам
Sensitive keys in codebases
Этот сценарий призван обратить внимание на некоторые популярные ошибки разработчиков и DevOps-команд при упаковке артефактов и кодовой базы приложений. Это имеет реальные последствия, такие как компрометация организаций и их инфраструктуры
Цель этого сценария - определить ключи, имеющиеся в кодовой базе. Которая включает в себя код приложения, контейнер и инфраструктуру.
С помощью ffuf/gobuster можно попытаться найти ключи. Иногда эти ключи могут лежать в
.git
http://localhost:1230/.git
Давайте глянем конфиги
http://localhost:1230/.git/config
С помощью
git-dumper
мы можем клонировать весь репозиторийgit-dumper http://localhost:1230/.git k8s-goat-git
И потом можно посмотреть логи с помощью
git log
А с помощью
git checkout
берем любой коммит и потом смотрим .env
файл где есть ключикиDIND (docker in docker) exploitation
В этом сценарии мы сосредоточимся на распространенных и стандартных способах создания систем и pipeline, использующих контейнерные сокеты для создания, сборки и запуска контейнеров из базовой среды выполнения контейнеров. Этим пользовались с первых дней существования контейнерной экосистемы, и до сих пор мы видим эту неправильную конфигурацию/случаи использования в реальном мире.
Большинство систем CI/CD и pipeline систем используют базовый хост Docker runtime для создания контейнеров для вас в рамках pipeline, используя нечто под названием DIND (docker-in-docker). В данном сценарии мы пытаемся использовать эту неправильную конфигурацию и получить доступ к хост-системе, выйдя из контейнера docker.
Целью этого сценария является выход из запущенного докер-контейнера на хост-систему, где запущен контейнер, и возможность доступа и выполнения действий на хост-системе. Вообще как вы можете понять из названия и описания, данная атака затрагивает больше приколы Docker нежели чем K8S.
На примере нам предлагают пропинговать локальный хост и сразу же туда можно попробовать сделать command injection, что дает возможность получить инфу и с помощью команды
mount
мы видим, что /custom/docker/docker.sock
смонтирован в файловой системе, и если предположить, что он смонтирован из хост-системы, нам нужно обратиться к нему для связи с UNIX.Далее мы можем загрузить официальный статический бинарник docker
https://download.docker.com/linux/static/stable/
. После разархивируем и теперь мы можем получить доступ к хост-системе, выполнив docker с передачей UNIX-сокета docker.sockSSRF in the Kubernetes (K8S)
Этот сценарий призван продемонстрировать популярную уязвимость безопасности приложений, которая повсеместно эксплуатируется в облачных средах. Теперь мы попытаемся увидеть, как она влияет на кластеры Kubernetes, внутренние службы и микросервисы. Мы посмотрим, как можно использовать уязвимость SSRF, для получения доступа к метаданным облачного экземпляра, а также к информации метаданных внутренних сервисов. В частности, мы видим возможности встроенных функций Kubernetes, таких как обнаружение сервисов, для использования и получения доступа к другим внутренним микросервисам.
169.254.169.254 - это динамически конфигурируемый адрес. Он действителен только в одном сегменте сети и не подлежит маршрутизации. Большинство облачных провайдеров используют этот адрес для метаданных вычислений для экземпляров, включая таких крупных провайдеров, как AWS, GCP Azure, Digital Ocean и др.
Мы можем получить доступ к службе метаданных экземпляра по умолчанию, используя 169.254.169.254. Нам также нужно определить, какой облачный провайдер использует службу для запуска этого вычисления, чтобы мы могли использовать определенные заголовки и запросы. Если он не размещен в облаке провайдера, то мы можем пропустить это и перейти к запросам внутренней службы, как это делают другие микросервисы и внутренние службы в кластере Kubernetes.
Мы можем начать с энумерации, чтобы понять, какие службы запущены в текущем экземпляре и других сетях, основываясь на доступной информации
Мы также можем запросить текущий контейнер/подсистему, чтобы узнать, запущены ли другие сервисы, запросив различные порты и адреса.
Давайте сделаем запрос к 5000 порту в том же контейнере.
http://127.0.0.1:5000
И потом после перебора c помощью ffuf/gobuster, мы получаем такой путь:
Ссылка скрыта от гостей
Видим что это base64 и декодируем полученное
NodePort exposed services
Когда Kubernetes создает службу NodePort, она выделяет порт из диапазона, указанного во флагах, которые определены в конфигурации вашего кластера Kubernetes. (По умолчанию это порты в диапазоне 30000-32767).
В этом сценарии мы видим еще одну неправильную конфигурацию, которая может дать злоумышленникам доступ к внутренним службам и нераскрытым службам. Это одна из простых ошибок, допущенных при создании служб Kubernetes, а также при установке и конфигурации кластера.
Если кто-то из пользователей открыл какой-либо сервис в кластере Kubernetes с помощью NodePort, это означает, что на узлах, где запущены кластеры Kubernetes, не включен брандмауэр/сетевая безопасность. Нам нужно увидеть неаутентифицированные и неавторизованные сервисы.
Получаем список информации о внешних IP-адресах узлов Kubernetes, выполнив следующую команду
kubectl get nodes -o wide
Теперь давайте выясним открытые порты. В этом случае вы можете использовать традиционные утилиты сканирования, такие как nmap. Как только мы определили, что существует открытый порт NodePort, мы можем просто проверить его, подключившись и получив к нему доступ
Теперь мы видим, что мы можем получить доступ к внутренним службам, которые не выставлены на всеобщее обозрение, к ним можно получить доступ и обойти их благодаря конфигурации NodePort
RBAC least privileges misconfiguration
Kubernetes предоставляет ряд встроенных механизмов для аутентификации API-серверов, однако они, скорее всего, подходят только для непроизводственных или небольших кластеров.
RBAC существует, для реализации принципа безопасности наименьших привилегий. Тем не менее, большинство реальных рабочих нагрузок и ресурсов в итоге имеют более широкие привилегии, чем предполагалось. В этом сценарии мы увидим, как простая неправильная конфигурация может получить доступ к секретам, ресурсам и информации.
В реальном мире мы часто видим, как разработчики и команды DevOps склонны предоставлять дополнительные привилегии, чем требуется. Это предоставляет злоумышленникам больше контроля и привилегий, чем они предполагали. В этом сценарии вы можете использовать учетную запись службы, привязанную к pod, для предоставления доступа webhookapikey, но с ее помощью злоумышленник может получить контроль над другими секретами и ресурсами.
По умолчанию Kubernetes хранит всю информацию о токенах и учетных записях служб в стандартном месте
В нашем случае переходим по следующему пути:
cd /var/run/secrets/kubernetes.io/serviceaccount/
Теперь мы можем использовать эту информацию для запроса и общения со службой API Kubernetes с доступными разрешениями и привилегиями. Чтобы указать на имя хоста внутреннего сервера API, мы можем экспортировать его
export APISERVER=https://${KUBERNETES_SERVICE_HOST}
Устанавливаем путь к токену ServiceAccount
export SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount
Экспортируем неймспейсы
export NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)
Чтобы прочитать токен ServiceAccount, также его экспортируем
export TOKEN=$(cat ${SERVICEACCOUNT}/token)
Указать путь ca.crt, чтобы мы могли использовать его при запросе в запросах curl
export CACERT=${SERVICEACCOUNT}/ca.crt
Теперь мы можем исследовать API Kubernetes с помощью токена и построенных запросов
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api
Чтобы запросить доступные секреты в пространстве имен по умолчанию, выполните следующую команду
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api/v1/secrets
Для запроса секретов, специфичных для пространства имен
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api/v1/namespaces/${NAMESPACE}/secrets
Для запроса pod в определенном пространстве имен
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api/v1/namespaces/${NAMESPACE}/pods
И получаем значение k8svaulapikey
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api/v1/namespaces/${NAMESPACE}/secrets | grep k8svaultapikey
Ура, теперь можем расшифровать закодированное значение base64 с помощью следующей команды
echo "azhzLWdvYXQtODUwNTc4NDZhODA0NmEyNWIzNWYzOGYzYTI2NDlkY2U=" | base64 -d
И еще про Kubernetes CIS benchmarks analysis
The Center for Internet Security (CIS) Kubernetes Benchmark - это набор рекомендаций по настройке Kubernetes для поддержки надежной защиты. Эталон привязан к конкретному выпуску Kubernetes. CIS Kubernetes Benchmark написан для дистрибутива Kubernetes с открытым исходным кодом и призван быть максимально универсальным для всех дистрибутивов.
Этот сценарий очень полезен при проведении аудитов и оценок безопасности Kubernetes. Здесь мы научимся проводить популярный эталонный аудит CIS для кластера Kubernetes и использовать результаты для дальнейшего использования или устранения неправильных конфигураций и уязвимостей. Это очень важно и обязательно, если вы занимаетесь аудитом и обеспечением соответствия в современном мире контейнеров, Kubernetes и облачных экосистем.
Мы можем развернуть Kubernetes CIS benchmarks, выполнив следующую команду
kubectl apply -f scenarios/kube-bench-security/node-job.yaml
Теперь мы можем получить список заданий и информацию о связанных с ними стручках, выполнив следующую команду
kubectl get jobs
kubectl get pods
После того, как мы определили pod, мы можем получить результаты аудита, посмотрев логи.
Мы видим, что он возвращает все проблемы вашего кластера.
На этом все
Данные проблемы я смог разобрать, благодаря kube-goat (некий аналог OWASP Juice Shop), который предоставляет вам заранее подготовленный кластер с проблемами выше.
Последнее редактирование модератором: