Среда оркестрации контейнеризированных приложений Kubernetes получила в последние годы широкое распространение. Для этого есть множество причин.
Прежде всего это все те преимущества, которые даёт использование контейнеров: возможность построения микросервисной архитектуры, когда мы можем разделить приложение на отдельные компоненты, работающие в контейнерах и реализовывать в них нужный нам функционал, независимо от других элементов нашего решения. Также, контейнеры позволяют эффективнее использовать оборудование, их удобно применять там, где нужно развернуть несколько идентичных экземпля��ов приложения, например, при миграции из среды разработки в среду тестирования.
И наконец, контейнеры позволяют сделать архитектуру приложения более безопасной за счет изоляции отдельных компонентов. Тогда, в случае компрометации одного микросервиса злоумышленник не сможет (или по крайней мере, не должен) выбраться за пределы контейнера и захватить среду контейнеризации.
Когда серверов, на которых запущены контейнеры становиться несколько десятков и более, возникает необходимость в использовании инструмента для централизованного управления узлами контейнеризации и здесь на помощь приходит Kubernetes. С его помощью можно развернуть множество экземпляров одного приложения, автоматически поддерживать необходимое их количество, обеспечить прозрачный для пользователей процесс обновления множества микросервисов, из которых оно состоит, разграничить доступ к разным экземплярам контейнеров и многое другое.
Таким образом, совершенно очевидно, что Kubernetes является по сути основой для всей инфраструктуры, на которой работает приложение, и то, насколько безопасно оно будет работать во многом зависит от того, насколько правильно будет настроена сама среда оркестрации.
Конечно, можно внедрить различные механизмы защиты, например ролевую модель доступа, о которой мы уже говорили ранее, но судить о том, насколько хорошо защищена та или иная система можно только попытавшись пойти по пути хакера, то есть попытаться ее взломать. В реальной жизни эту задачу обычно выполняют так называемые «белые хакеры» — пентестеры. В этой статье мы попробуем посмотреть на безопасность Kubernetes как раз глазами пентестеров.
Сначала разведка
По традиции, злоумышленники обычно начинают поиск информации из открытых источников. И Kubernetes в этом плане не является исключением. Так, зная какие домены принадлежат данной организации и сервисы каких облачных провайдеров она использует, можно попробовать поискать нужные поддомены с помощью запросов вида Identity LIKE "k8s.%.com" на специализированных ресурсах типа crt.sh, где помимо информации об используемых сертификатах будут также указаны поддомены, связанные с Kubernetes.

Узнав используемые домены, хакеру будет легче понять, какие компоненты атакуемого сервиса работают непосредственно в среде Kubernetes. Возьмем, к примеру, клиент серверное приложение, серверная часть которого размещается в облаке. Если адрес этой части соответствует адресу поддомена, который мы нашли на предыдущем шаге, то это верный признак того, что приложение работает в среде контейнеризации под управлением Kubernetes.
Разработчики (как впрочем и другие ИТ-шники) в основной своей массе довольно ленивы и если кто-то за них сделал хотя бы часть работы, то они этим с удовольствием воспользуются. Поэтому, если атакующему известно какие сервисы и компоненты используются для работы приложения, то он может попробовать поискать на github файлы YAML для этих сервисов.
Давайте рассмотрим небольшой пример. Допустим, из-за некорректной настройки веб сервера нам доступен файл phpinfo.php, из которого мы узнали, что целевое приложе��ие крутится на Nginx и использует PHP7. Теперь можно пойти на github и с помощью запросов вида «k8s nginx php 7» попробовать найти готовый набор YAML файлов, описывающий создание и развертывание всех необходимых для работы данных сервисов сущностей.

Вариантов будет не слишком много, к тому же не все результаты содержат нужные файлы, но если нам повезет, и мы найдем тот набор файлов, который использовали разработчики, то сможем как минимум узнать: какие компоненты как между собой взаимодействуют (порты, сервисы, протоколы), а как максимум, возможно в настройках и/ или в используемом ПО имеются уязвимости, которые можно попытаться проэксплуатировать.
Помним про порты
Еще один древний, но до сих пор рабочий способ — это сканирование сетевых портов. Конечно, рассчитывать, что все из перечисленных ниже портов будут смотреть в интернет не стоит, хотя Shodan вполне может найти что-то открытое в сети. Однако, если у нас уже есть доступ во внутреннюю сеть, которая не слишком хорошо сегментирована, то сканирование портов вполне может принести результат.
Итак, ниже представлен список портов с краткими пояснениями. Конечно, вряд ли все эти порты будут открыты, однако при пентесте вполне можно столкнуться с ситуацией, когда некоторые из них могут оказаться открыты. В таком случае важно понимать, что можно попытаться сделать на нем.
На компонентах управления Kubernetes для входящих соединений открыты следующие порты:
6443 (TCP) (Kubernetes API server), 2379-2380 (TCP) (etcd server client API kube-apiserver, etcd), 10250 (TCP) (Kubelet API Self, Control plane), 10259 (TCP) (kube-scheduler), 10257 (TCP) (kube-controller-manager).
На рабочих узлах будут открыты следующие порты:
10250 (TCP) (Kubelet API), 10256 (TCP) (kube-proxy, Load balancers), 30000-32767 (TCP) (NodePort Services).
Также имеется ряд вспомогательных сервисов, предназначенных для мониторинга состояния компонентов k8s, поэтому в примере ниже мы будем опрашивать несколько больше портов. Например, на всякий случай проверим небезопасный порт 8080.
Для того, чтобы быстро поискать в сети только открытые порты k8s и вообще не создавать в трафике слишком много ненужного шума, просканируем сеть с помощью Nmap, указав только нужные порты
nmap -p 443,2379,4194,6443,8443,8080,10250,10255,10256,9099,6782-6784,44134
<pod_ipaddress>/16
Если в результате сканирования портов нам удалось получить ненулевой результат, а проще говоря, найти открытые порты, то с помощью curl можно попробовать посмотреть, что на них работает.
Для узла, представленного на рисунке, такие проверки будут иметь следующий вид:
curl -k https://192.168.49.2:8443/api/v1
Итак, мы провели разведку и смогли обнаружить узлы Kubernetes. Теперь нам необходимо попытаться найти и проэксплуатировать уязвимости в настройках и компонентах среды оркестрации.
В этой статье мы не будем подробно рассматривать архитектуру Kubernetes, так как этой теме посвящено уже достаточно публикаций. Так что, мы сразу перейдем к поиску уязвимостей в компонентах k8s.
Kubelet API
Служба kubelet работает на каждом узле кластера. C ее помощью осуществляется управление подами внутри узла. Kubelet API взаимодействует с основным модулем kube-apiserver. При этом важным моментом является то, что по умолчанию HTTP-запросы, которые не прошли аутентификацию, но не были отклонены, обрабатываются как анонимный доступ и считаются пользователями system:anonymous в группе system:unauthenticated. Соответственно, если вы обнаружите, что эта служба открыта, то возможно, вам удастся выполнить произвольный код.
Попробуем найти доступные ресурсы с помощью curl. Например, можно посмотреть метрики:
curl -k https://192.168.49.2:10250/metrics
Или рабочие поды:
curl -k https://192.168.49.2:10250/pods
Если в ответ мы получим «Unauthorized», то нам не повезло и требуется аутентификация. А вот если в ответ мы получили какой-нибудь JSON, то можно попробовать сделать что-нибудь интересное.
Для начала можно получить список всех подов, работающих на узле:
curl -sk https://192.168.49.2:10250/runningpods/
Далее можно попытаться выполнить в контейнерах нужные команды. Так, получив список подов, работающих на узле мы можем найти что-то интересное, например СУБД. Попробуем прочитать содержимое каталога:
$ curl -k -XPOST "https://192.168.49.2:10250/run/default/mysql-epg0f/mysql" -d
"cmd=ls -la /"Пароль от СУБД можно поискать с помощью переменных окружения ОС:
$ curl -k -XPOST "curl -sk
https://192.168.49.2:10250/run/default/mysql-epg0f/mysql" -d 'cmd=env'Для специалистов по анализу кода могут оказаться интересными исходники Kubelet, которые можно взять по адресу:
curl -s https://raw.githubusercontent.com/kubernetes/kubernetes/master/pkg/kubelet/server/server.go
Итак, основной вектор атаки мы обозначили. Однако, что делать если у нас десятки узлов и сотни различных подов. В таком случае можно прибегнуть к небольшому скрипту, который позволит нам составить запросы curl на основе данных, полученных с помощью kubectl:
kubectl get nodes -o custom-columns='IP:.status.addresses[0].address,KUBELET_PORT:.status.daemonEndpoints.kubeletEndpoint.Port' | grep -v KUBELET_PORT |
while IFS='' read -r node; do
hst=$(echo $node | awk '{print $1}')
prt=$(echo $node | awk '{print $2}')
echo "curl -k --max-time 30 https://$hst:$prt/pods"
done
Как видно, здесь мы с помощью kubectl формируем запрос, для подключения к kubelet API.
Etcd API
Напомним, что компонент etcd это база данных для Kubernetes, являющаяся критически важным элементом любого кластера. Он хранит в себе всю информацию, нужную для его стабильной работы и соответственно, будет не очень хорошо, если злоумышленник получит к нему доступ.
При наличии доступа по порту 2379 можно также обратиться к базе etcd с помощью curl:
url -k https://192.168.49.2:10250/version
etcdctl --endpoints=https://192.168.49.2:2379 get / --prefix --keys-onlyДля автоматизации поиска etcd можно немного модифицировать пример из предыдущего раздела для обращений к порту 2379:
kubectl get nodes -o custom-columns='IP:.status.addresses[0].address,KUBELET_PORT:.status.daemonEndpoints.kubeletEndpoint.Port' | grep -v KUBELET_PORT |
while IFS='' read -r node; do
hst=$(echo $node | awk '{print $1}')
echo "curl -k --max-time 30 https://$hst:2379/version"
done
Таким образом, мы можем обнаружить узлы с доступным сервисом etcd.
Helm и другие
До этого мы говорили об обнаружении и использовании компонентов самого k8s. Теперь рассмотрим выявление и использование такого интересного дополнительного элемента как сервис Helm.
Helm это популярный диспетчер пакетов для K8s, который значительно упрощает процесс установки, управления и масштабирования приложений в кластере. Устаревшие версии Helm используют сервис Tiller, который живет на порту 44134 (TCP). Помним, что устаревший софт, это находка для хакера, поэтому, если нам удалось обнаружить открытый порт, то можно попробовать к нему обратиться с помощью клиента Helm. В примере ниже мы узнаем версию Helm:
helm --host 192.168.49.2:44134 version
Еще одним интересным сервисом является cAdvisor. Это инструмент с открытым исходным кодом, предназначенный для мониторинга состояния контейнеров. Он используется для чтения характеристик производительности и использования ресурсов контейнеров, работающих в кластере. Данный сервис работает на порту 4194 TCP и к открытому порту можно обратиться с помощью curl:
curl -k https://192.168.49.2:4194
Сервис NodePort
Сервис NodePort мы уже упоминали, когда говорили о портах, открытых на рабочих нодах кластера. Через NodePort, один и тот же порт открывается во всех узлах, передающих трафик в сервис. По умолчанию этот порт будет находиться в диапазоне 30000–32767. Таким образом, через эти порты могут быть доступны новые непроверенные службы.
Можно отдельно поискать эти службы с помощью nmap:
sudo nmap -sS -p 30000-32767 192.168.49.2
Подводим итоги
В этой статье мы пока что ничего не взломали, так как наша основная задача заключалась в том, чтобы выявить сначала сами компоненты Kubernetes и их размещение в сети, а затем уже попытаться идентифицировать сервисы на открытых портах для последующего развития атаки. В следующей статье мы рассмотрим некоторые методы эксплуатации уязвимостей в настройках k8s и его компонентах.
