У 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.



Исходя из этой информации, можно:

  1. Посмотреть поды.


  2. Посмотреть права. Например, в нашем случае нет прав в 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.

Попытка захвата кластера


После получения массива информации из прошлых блоков попытка захвата кластера сводится к довольно простым действиям:

  1. Сканирование портов хоста с помощью утилиты nmap.
  2. Нахождение открытых портов, сканирование Namespace, построение к нему «мостика» из Socat с пробросом. Затем можно попасть в Default Namespace — для примера предположим, что в нем запущено такое же приложение с аналогичной уязвимостью.
  3. Построение «мостика» из 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. Когда сервер-1 хочет передать пакет данных серверу-2, он делает широковещательный запрос в сети: «У кого такой-то IP, верните мне свой MAC-адрес». 
  2. Сервер-2 получает запрос и отправляет в ответ свой MAC-адрес.
  3. Сервер-1 устанавливает соединение, настраивается ARP-таблица. 
  4. После этого сервер-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, например, с помощью готового сплойта — любого из тех, которые можно найти в бесплатном доступе. Такой скрипт, как правило, работает по следующему алгоритму: 

  1. Запрашивает реальный IP kube-DNS.


  2. Запрашивает MAC всех устройств подсети и сравнивает его с тем, что он получил.


  3. Собирает MAC и IP моста br0.


  4. В бесконечном цикле начинает слать пакеты на 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! Регулярные дайджесты, полезные статьи, а также анонсы конференций и вебинаров.

Комментарии (0)