Среда оркестрации контейнеризированных приложений 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 и его компонентах.
Kliffoth
В каком году опубликован оригинал этой статьи? Helm не использует Tiller лет 5 уже.
cdnnow-creator
При пентесте всегда проверяется наличие устаревших сервисов, поэтому мы проверяем наличие tiller, несмотря на то, что он уже давно не должен использоваться.