У Kubernetes много инструментов защиты поставляется прямо из коробки. Но все равно степень выстроенной защиты зависит от компетенции специалистов, которые ее настраивают, требований бизнеса и ресурсов, выделенных на безопасность. В итоге сложно гарантировать, что под видом «мирного и безобидного» контейнера не скрывается «зомби», который может нанести существенный урон.
Я Лев Хакимов, главный специалист по разработке в Sber, соорганизатор соревнований VrnCTF по информационной безопасности, преподаватель курса по DevOps. Я расскажу о возможностях для взлома, основных типах атак изнутри кластера, инструментах защиты, а также об основных ошибках разработчиков и DevOps-специалистов, которые приводят к уязвимостям.
Материал написан на основе моего выступления на конференции VK Kubernetes Conf «The Walking Pod: основные стратегии атак изнутри кластера».
Путь злоумышленника: от URL к токену kube-system
Будем рассматривать способы взломов с помощью коровки — так нагляднее и интереснее.
Вместе с коровкой у нас есть URL сервиса, в котором добавле-на RCE (Remote Code Execution) — возможность исполнения кода на удаленном сервере, в том числе там, где изначально такой функции нет. То есть можно внедрить код, заставить сервер его исполнять и использовать результат.
Для примера условимся, что наша коровка принимает через URL какие-то аргументы и без проверок исполняет их внутри себя. В данном случае у нас Bash RCE.
Изучение инвайтов
Инвайты содержат не самую чувствительную информацию, но их содержимого достаточно, чтобы:
- подтвердить принадлежность кода к Kubernetes;
- узнать IP-сервер кластера;
- определить IP пода (после выполнения Hostname).
Этой информации достаточно, чтобы понять, что Kubernetes имеет классическую типовую схему с Ingress и сервисом, но без Istio, Service Mesh и других сущностей.
При дальнейшем изучении кода можно найти Curl — его часто оставляют для отладки в контейнерах и общей проверки сетевых связностей. На основании Curl можно сделать вывод, что защиты от доступа в интернет нет.
Далее злоумышленник может попробовать прочитать токен сервис-аккаунта. Если он не нужен в приложении и для обращения к API-кластерам, его может и не быть. Но на практике он зачастую есть — при включенном Automount эту настройку многие упускают из виду.
Далее можно сделать запрос к Endpoints дефолтного Namespace — зачастую у сервис-аккаунтов доступ к нему есть по умолчанию. В тестовом варианте, запущенном локально, на выходе мы получаем локальный IP, но в случае с реальным кластером можно получить External login. Получить СА-сертификат и токен на этом этапе — простая задача.
То есть простые манипуляции позволяют узнать:
- состояние (включение/выключение) Automount;
- возможность подключения к API-серверу через токен;
- внешний Endpoint кластера.
Сборка kubeconfig и подключение к kubectl
В подах в Default может быть все заблокировано, но даже в таком случае они содержат подсказки для злоумышленников: имя токена всегда содержит название Namespace и сервис-аккаунт. В нашем случае они указывают, что приложение запущено Namespace Cowns.
Исходя из этой информации, можно:
- Посмотреть поды.
- Посмотреть права. Например, в нашем случае нет прав в Default Namespace, но в Namespace Cowns они практически не ограничены. Ситуации, в которых токену дают права на Namespace, например для экспериментов в рамках песочницы, не редкость.
Security Context
Секция Security Context в манифестах отвечает за настройки безопасности. Она позволяет задействовать разные варианты защиты, в том числе использовать:
- runAsNotRoot,
- runAsUser/runAsGroup,
- Privileged,
- Capabilities,
- readOnlyRootFileSystem,
- procMount,
- Sysctls и другие.
В данном случае большой выбор часто играет на руку злоумышленникам. При подготовке Kubernetes разработчики часто забывают о некоторых флажках и оставляют пробелы в защите. Например, контейнер может оказаться Rootless, в нашем случае именно так.
Одновременно с этим для контейнера может остаться возможность записи файлов, если система выставлена не только на чтение.
Далее можно попробовать запустить под благодаря правам с Root внутри. Для этого подойдет Alpine, в нем это легко сделать, поскольку есть Root по умолчанию.
apiVersion: vl
kind: Pod
metadata:
name: root-pod
spec:
containers:
- name: justsieep
image: alpine
command: ["/bin/sleep", "999999"]
Если политика безопасности настроена и контейнеры с запуском Root заблокированы, возникнет ошибка. Но это не значит, что в таком случае защита абсолютна: злоумышленник может использовать уязвимость Hostpath и смаунтить на ноде любую папку по умолчанию.
volumeMounts:
- mountPath: /chroot
name: host
securitycontext:
runAsUser: 999
privileged: true
volumes:
- name: host
hostPath:
path: /
type: Directory
Далее можно пробовать запустить контейнер в привилегированном режиме (Privileged). Он может быть заблокирован, но это не последний путь для взлома, потому что можно взять под, в котором есть как Root-пользователь, так и не Root-пользователь.
apiVersion;
vl kind: Pod
metadata:
name: good-pod
spec:
containers:
- name: good-pod
image: ubuntu:22.04
securitycontext:
runAsUser: 999
imagePuttPoticy: Always
В таком случае для получения Root достаточно запуститься не от Root и эскалироваться; если настройки по эскалации не заданы, права будут получены.
Таким образом, к этому этапу злоумышленник сможет узнать, что:
- некоторый Security Context заблокирован;
- Privilege Escalation работает;
- можно запустить Root-код в Namespace.
Попытка захвата кластера
После получения массива информации из прошлых блоков попытка захвата кластера сводится к довольно простым действиям:
- Сканирование портов хоста с помощью утилиты nmap.
- Нахождение открытых портов, сканирование Namespace, построение к нему «мостика» из Socat с пробросом. Затем можно попасть в Default Namespace — для примера предположим, что в нем запущено такое же приложение с аналогичной уязвимостью.
- Построение «мостика» из Socat для Default Namespace, выполнение PortForward, попадание в приложение в Default Namespace, эскалация RCE.
Аналогичным способом можно забрать и токен от Default. После этого можно пробовать запуститься под Hostpath-уязвимостью, в случае успеха можно попасть на ноду, на которой установлен под из Default Namespace.
Конечно, нет гарантии, что получится воспользоваться Kubelet Token, который лежит на ноде, но это небольшая преграда: увидеть все секреты можно, сделав Describe пода с ETCD.
Так можно получить всю нужную информацию, в том числе расположение сертификатов на ноде. После можно запустить ETCD клиента и спокойно выгрести секреты вместе с токеном kube-system.
ARP Spoofing
Еще одна угроза в Kubernetes — ARP (Address Resolution Protocol): протокол, который предназначен для определения MAC-адресов по IP-адресу и существует в IPv4-сетях. Работает на канальном уровне (L2). Основная функция ARP — передача IP-пакетов через Ethernet-кадры. Рассмотрим, как он работает НЕ в Kubernetes. Есть сервер-1, сервер-2 и компьютер, все они подключены к роутеру.
Схема работает следующим образом:
- Когда сервер-1 хочет передать пакет данных серверу-2, он делает широковещательный запрос в сети: «У кого такой-то IP, верните мне свой MAC-адрес».
- Сервер-2 получает запрос и отправляет в ответ свой MAC-адрес.
- Сервер-1 устанавливает соединение, настраивается ARP-таблица.
- После этого сервер-1 начинает слать все запросы по полученному MAC-адресу.
Проблема в том, что ARP выстроен на доверии: по умолчанию нет проверок на правильность адреса и другие параметры. Поэтому, если компьютер-1 начнет играть в «конкурентную войну» с сервером-2 и отправлять свой MAC-адрес как верный, сервер-1 запишет его и перенаправит запросы. Одновременно с этим сервер-2 получит отказ в обслуживании.
Вместе с тем есть риск, что компьютер-1 будет «умным», в таком случае он будет выступать в роли прокси (передавать пакеты с себя на сервер-2) и сможет долго оставаться незамеченным для остальных участников сети, поскольку добавятся только незначительные задержки. Такая уязвимость есть в CNI Plugin Flannel, который работает через мосты. Рассмотрим, как это работает на примере классической схемы, в которой:
- есть корневой Namespace в операционной системе с Ethernet0;
- каждый под располагается в своем сетевом Namespace;
- у каждого Namespace есть свой Veth, цепляющийся к мосту br0;
- есть Overlay, kube-proxy и Network plugin;
- за всю коммутацию между подами отвечает интерфейс br0.
Именно на интерфейсе br0 сейчас и будем все менять. Итак, при просмотре Capabilities можно обнаружить выставленную Capabilities
net_raw
.У нее есть несколько особенностей:
- присутствует по умолчанию;
- позволяет контейнеру подделывать пакеты;
- позволяет подключаться к любому сетевому интерфейсу.
Вместе с тем
CAP_NET_RAW
редко нужна веб-приложениям. Исключение — сценарии, когда нужно намеренно работать с сетевыми пакетами. В нашем случае важно, что при наличии CAP_NET_RAW
легко организовать ARP Spoofing, например, с помощью готового сплойта — любого из тех, которые можно найти в бесплатном доступе. Такой скрипт, как правило, работает по следующему алгоритму: - Запрашивает реальный IP kube-DNS.
- Запрашивает MAC всех устройств подсети и сравнивает его с тем, что он получил.
- Собирает MAC и IP моста br0.
- В бесконечном цикле начинает слать пакеты на br0, просто забивая порт.
При использовании Flannel высок шанс уязвимости к такому ARP. Но это не значит, что вся проблема во Flannel: Cilium и Calico с kube-proxy также уязвимы, например, к MITM-атакам. Скажем, Zero Trust — принцип нулевого доверия — и соответствующая CVE. Чтобы воспользоваться уязвимостью, достаточно:
- создать новый сервис ClusterIP, который будет вести на сервис, запущенный хакером, и иметь какую-либо уязвимость;
- добавить ExternalIP с IP, который нужно перехватывать, после чего запросы этого приложения начинают попадать к злоумышленнику.
После этого приложение будет подконтрольно хакеру, а при обращении к нужному IP-адресу оно получит не реальный запрос, а то, что ему отправят в качестве истины. Исправить такую уязвимость можно установкой правил на Gatekeeper для фильтрации поступающих запросов.
Есть и другой тип CVE — Fake IPv 6. Он использует базовую уязвимость протоколов. Так, в кластерах обычно работает адресация IPv4, но в спецификации IPv6 говорится, что при появлении в сети роутера или устройства, объявляющего себя роутером версии IPv6, все пакеты сначала будут отправлены ему, а только потом по IPv4. То есть, имея под с установленной настройкой Сapabilities
CAP_NET_RAW,
можно прозрачно перехватывать весь трафик. При наличии TLS расшифровать его не получится, но это не мешает создать отказ в обслуживании.Решить проблему можно двумя способами:
- отключить доверие роутеру IPv6;
- убрать
CAP_NET_RAW
, что приведет к потере ряда полезных настроек.
Почему Kubernetes становится уязвимым
Для опытных специалистов игнорирование политик безопасности и их настроек — нонсенс. Но на практике это встречается часто. Причин много:
- изменение команды и незнание того, как настроены политики сейчас;
- нежелание обновлять политики из-за опасения нарушить выстроенные процессы;
- отсутствие ресурсов на дополнительные меры безопасности;
- отсутствие у команды компетенций в сетевых политиках.
Вместе с тем как незнание законов не освобождает от ответственности, так и отсутствие корректно настроенных политик, независимо от причин, не снижает риск угроз.
Что в итоге
На первый взгляд, у Kubernetes много уязвимостей. Но на практике это не так:
- большая часть уязвимостей автоматически обнаруживается сканерами SAST, DAST;
- современные кластеры имеют автоматические аудиты и определенный набор требований к безопасности;
- своевременное обновление политик и проверка их настроек помогает исключить случайные уязвимости и быстро устранять бреши в защите.
Более того, безопасность кластера — это зачастую не одна какая-то конкретная уязвимость в конкретной библиотеке, а целый накопленный кластер различных проблем. Поэтому причиной многих проблем являются не свойства Kubernetes, а халатность при настройке политик безопасности или допущенные ошибки на этапе формирования защиты. При корректной настройке даже базовых мер достаточно для недопущения и отражения многих атак. Вместе с тем надо понимать, что перед настройкой защиты необходимо оценить стоимость принятых решений и приоритеты: накрутить безопасность можно настолько мощно, что вам просто не хватит денег ее содержать.
Вы прямо сейчас можете воспользоваться Kubernetes с от VK Cloud. Для тестирования мы начисляем новым пользователям 3000 бонусных рублей и будем рады вашей обратной связи.
А еще присоединяйтесь к телеграм-каналу «Вокруг Kubernetes», чтобы быть в курсе новостей из мира K8s! Регулярные дайджесты, полезные статьи, а также анонсы конференций и вебинаров.