mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-25 22:00:58 +03:00
Initial commit - 611 cybersecurity skills across all subdomains
This commit is contained in:
@@ -0,0 +1,256 @@
|
||||
---
|
||||
name: None
|
||||
description: Kubernetes penetration testing systematically evaluates cluster security by simulating attacker techniques against the API server, kubelet, etcd, pods, RBAC, network policies, and secrets. Using tools
|
||||
domain: cybersecurity
|
||||
subdomain: container-security
|
||||
tags: [containers, kubernetes, security, penetration-testing, offensive-security]
|
||||
version: "1.0"
|
||||
author: mahipal
|
||||
license: MIT
|
||||
---
|
||||
# Performing Kubernetes Penetration Testing
|
||||
|
||||
## Overview
|
||||
|
||||
Kubernetes penetration testing systematically evaluates cluster security by simulating attacker techniques against the API server, kubelet, etcd, pods, RBAC, network policies, and secrets. Using tools like kube-hunter, Kubescape, peirates, and manual kubectl exploitation, testers identify misconfigurations that could lead to cluster compromise.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Authorized penetration testing engagement
|
||||
- Kubernetes cluster access (various levels for different test scenarios)
|
||||
- kube-hunter, kubescape, kube-bench installed
|
||||
- kubectl configured
|
||||
- Network access to cluster components
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### Kubernetes Attack Surface
|
||||
|
||||
| Component | Port | Attack Vectors |
|
||||
|-----------|------|---------------|
|
||||
| API Server | 6443 | Auth bypass, RBAC abuse, anonymous access |
|
||||
| Kubelet | 10250/10255 | Unauthenticated access, command execution |
|
||||
| etcd | 2379/2380 | Unauthenticated read, secret extraction |
|
||||
| Dashboard | 8443 | Default credentials, token theft |
|
||||
| NodePort Services | 30000-32767 | Service exposure, application exploits |
|
||||
| CoreDNS | 53 | DNS spoofing, zone transfer |
|
||||
|
||||
### MITRE ATT&CK for Kubernetes
|
||||
|
||||
| Phase | Techniques |
|
||||
|-------|-----------|
|
||||
| Initial Access | Exposed Dashboard, Kubeconfig theft, Application exploit |
|
||||
| Execution | exec into container, CronJob, deploy privileged pod |
|
||||
| Persistence | Backdoor container, mutating webhook, static pod |
|
||||
| Privilege Escalation | Privileged container, node access, RBAC abuse |
|
||||
| Defense Evasion | Pod name mimicry, namespace hiding, log deletion |
|
||||
| Credential Access | Secret extraction, service account token theft |
|
||||
| Lateral Movement | Container escape, cluster internal services |
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
### Step 1: External Reconnaissance
|
||||
|
||||
```bash
|
||||
# Discover Kubernetes services
|
||||
nmap -sV -p 443,6443,8443,2379,10250,10255,30000-32767 target-cluster.com
|
||||
|
||||
# Check for exposed API server
|
||||
curl -k https://target-cluster.com:6443/api
|
||||
curl -k https://target-cluster.com:6443/version
|
||||
|
||||
# Check anonymous authentication
|
||||
curl -k https://target-cluster.com:6443/api/v1/namespaces
|
||||
|
||||
# Check for exposed kubelet
|
||||
curl -k https://node-ip:10250/pods
|
||||
curl http://node-ip:10255/pods # Read-only kubelet
|
||||
```
|
||||
|
||||
### Step 2: Automated Scanning with kube-hunter
|
||||
|
||||
```bash
|
||||
# Install kube-hunter
|
||||
pip install kube-hunter
|
||||
|
||||
# Remote scan
|
||||
kube-hunter --remote target-cluster.com
|
||||
|
||||
# Internal network scan (from within cluster)
|
||||
kube-hunter --internal
|
||||
|
||||
# Pod scan (from within a pod)
|
||||
kube-hunter --pod
|
||||
|
||||
# Generate report
|
||||
kube-hunter --remote target-cluster.com --report json --log output.json
|
||||
```
|
||||
|
||||
### Step 3: CIS Benchmark Assessment with kube-bench
|
||||
|
||||
```bash
|
||||
# Run kube-bench on master node
|
||||
kube-bench run --targets master
|
||||
|
||||
# Run on worker node
|
||||
kube-bench run --targets node
|
||||
|
||||
# Check specific sections
|
||||
kube-bench run --targets master --check 1.2.1,1.2.2,1.2.3
|
||||
|
||||
# JSON output
|
||||
kube-bench run --json > kube-bench-results.json
|
||||
|
||||
# Run as Kubernetes job
|
||||
kubectl apply -f https://raw.githubusercontent.com/aquasecurity/kube-bench/main/job.yaml
|
||||
kubectl logs -l app=kube-bench
|
||||
```
|
||||
|
||||
### Step 4: Framework Compliance with Kubescape
|
||||
|
||||
```bash
|
||||
# Install kubescape
|
||||
curl -s https://raw.githubusercontent.com/kubescape/kubescape/master/install.sh | /bin/bash
|
||||
|
||||
# Scan against NSA/CISA hardening guide
|
||||
kubescape scan framework nsa
|
||||
|
||||
# Scan against MITRE ATT&CK
|
||||
kubescape scan framework mitre
|
||||
|
||||
# Scan against CIS Kubernetes Benchmark
|
||||
kubescape scan framework cis-v1.23-t1.0.1
|
||||
|
||||
# Scan specific namespace
|
||||
kubescape scan framework nsa --namespace production
|
||||
|
||||
# JSON output
|
||||
kubescape scan framework nsa --format json --output kubescape-report.json
|
||||
```
|
||||
|
||||
### Step 5: RBAC Exploitation Testing
|
||||
|
||||
```bash
|
||||
# Check current permissions
|
||||
kubectl auth can-i --list
|
||||
|
||||
# Check specific high-value permissions
|
||||
kubectl auth can-i create pods
|
||||
kubectl auth can-i create pods --subresource=exec
|
||||
kubectl auth can-i get secrets
|
||||
kubectl auth can-i create clusterrolebindings
|
||||
kubectl auth can-i '*' '*' # cluster-admin check
|
||||
|
||||
# Enumerate service account tokens
|
||||
kubectl get serviceaccounts -A
|
||||
kubectl get secrets -A -o json | jq '.items[] | select(.type=="kubernetes.io/service-account-token") | {name: .metadata.name, namespace: .metadata.namespace}'
|
||||
|
||||
# Check for overly permissive roles
|
||||
kubectl get clusterrolebindings -o json | jq '.items[] | select(.subjects[]?.name=="system:anonymous" or .subjects[]?.name=="system:unauthenticated")'
|
||||
|
||||
# Test service account impersonation
|
||||
kubectl --as=system:serviceaccount:default:default get pods
|
||||
```
|
||||
|
||||
### Step 6: Secret Extraction Testing
|
||||
|
||||
```bash
|
||||
# List all secrets
|
||||
kubectl get secrets -A
|
||||
|
||||
# Extract specific secret
|
||||
kubectl get secret db-credentials -o jsonpath='{.data.password}' | base64 -d
|
||||
|
||||
# Check for secrets in environment variables
|
||||
kubectl get pods -A -o json | jq '.items[].spec.containers[].env[]? | select(.valueFrom.secretKeyRef)'
|
||||
|
||||
# Check for secrets in mounted volumes
|
||||
kubectl get pods -A -o json | jq '.items[].spec.volumes[]? | select(.secret)'
|
||||
|
||||
# Search etcd directly (if accessible)
|
||||
ETCDCTL_API=3 etcdctl --endpoints=https://etcd-ip:2379 \
|
||||
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
|
||||
--cert=/etc/kubernetes/pki/etcd/server.crt \
|
||||
--key=/etc/kubernetes/pki/etcd/server.key \
|
||||
get /registry/secrets --prefix --keys-only
|
||||
```
|
||||
|
||||
### Step 7: Pod Exploitation
|
||||
|
||||
```bash
|
||||
# Deploy test pod with elevated privileges
|
||||
cat <<EOF | kubectl apply -f -
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: pentest-pod
|
||||
namespace: default
|
||||
spec:
|
||||
hostNetwork: true
|
||||
hostPID: true
|
||||
containers:
|
||||
- name: pentest
|
||||
image: ubuntu:22.04
|
||||
command: ["sleep", "infinity"]
|
||||
securityContext:
|
||||
privileged: true
|
||||
volumeMounts:
|
||||
- name: host-root
|
||||
mountPath: /host
|
||||
volumes:
|
||||
- name: host-root
|
||||
hostPath:
|
||||
path: /
|
||||
EOF
|
||||
|
||||
# Exec into pod
|
||||
kubectl exec -it pentest-pod -- bash
|
||||
|
||||
# From inside privileged pod - access host filesystem
|
||||
chroot /host
|
||||
|
||||
# From inside any pod - check internal services
|
||||
curl -k https://kubernetes.default.svc/api/v1/namespaces
|
||||
cat /var/run/secrets/kubernetes.io/serviceaccount/token
|
||||
```
|
||||
|
||||
### Step 8: Network Policy Testing
|
||||
|
||||
```bash
|
||||
# Check for network policies
|
||||
kubectl get networkpolicies -A
|
||||
|
||||
# Test pod-to-pod communication (should be blocked by policies)
|
||||
kubectl run test-netpol --image=busybox --restart=Never -- wget -qO- --timeout=2 http://target-service.namespace.svc
|
||||
|
||||
# Test egress to external services
|
||||
kubectl run test-egress --image=busybox --restart=Never -- wget -qO- --timeout=2 http://example.com
|
||||
|
||||
# Test access to metadata service (cloud environments)
|
||||
kubectl run test-metadata --image=busybox --restart=Never -- wget -qO- --timeout=2 http://169.254.169.254/latest/meta-data/
|
||||
```
|
||||
|
||||
## Validation Commands
|
||||
|
||||
```bash
|
||||
# Verify kube-hunter findings
|
||||
kube-hunter --remote $CLUSTER_IP --report json
|
||||
|
||||
# Cross-validate with Kubescape
|
||||
kubescape scan framework nsa --format json
|
||||
|
||||
# Check remediation effectiveness
|
||||
kube-bench run --targets master,node --json
|
||||
|
||||
# Clean up pentest resources
|
||||
kubectl delete pod pentest-pod
|
||||
kubectl delete pod test-netpol test-egress test-metadata
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- [kube-hunter - Kubernetes Penetration Testing](https://github.com/aquasecurity/kube-hunter)
|
||||
- [Kubescape - Kubernetes Security Platform](https://github.com/kubescape/kubescape)
|
||||
- [kube-bench - CIS Benchmark](https://github.com/aquasecurity/kube-bench)
|
||||
- [MITRE ATT&CK Containers Matrix](https://attack.mitre.org/matrices/enterprise/containers/)
|
||||
- [Kubernetes Threat Matrix - Microsoft](https://microsoft.github.io/Threat-Matrix-for-Kubernetes/)
|
||||
@@ -0,0 +1,58 @@
|
||||
# Kubernetes Penetration Test Report Template
|
||||
|
||||
## Engagement Details
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Client | |
|
||||
| Cluster | |
|
||||
| Test Type | External / Internal / Assumed-Breach |
|
||||
| Tester | |
|
||||
| Date Range | |
|
||||
| Scope | |
|
||||
|
||||
## Executive Summary
|
||||
|
||||
[Brief overview of findings and overall cluster security posture]
|
||||
|
||||
## Findings Summary
|
||||
|
||||
| Severity | Count |
|
||||
|----------|-------|
|
||||
| CRITICAL | |
|
||||
| HIGH | |
|
||||
| MEDIUM | |
|
||||
| LOW | |
|
||||
|
||||
## Detailed Findings
|
||||
|
||||
### Finding 1: [Title]
|
||||
- **Severity**: CRITICAL / HIGH / MEDIUM / LOW
|
||||
- **Category**: Authentication / RBAC / Secrets / Network / Pod Security
|
||||
- **MITRE ATT&CK**: T1xxx
|
||||
- **Description**:
|
||||
- **Evidence**:
|
||||
- **Impact**:
|
||||
- **Remediation**:
|
||||
- **References**:
|
||||
|
||||
## Attack Paths Identified
|
||||
|
||||
### Path 1: [Description]
|
||||
```
|
||||
[Initial Access] --> [Step 2] --> [Step 3] --> [Impact]
|
||||
```
|
||||
|
||||
## Recommendations (Priority Order)
|
||||
|
||||
| Priority | Recommendation | Effort | Impact |
|
||||
|----------|---------------|--------|--------|
|
||||
| 1 | | Low/Med/High | |
|
||||
| 2 | | | |
|
||||
|
||||
## Cleanup Confirmation
|
||||
|
||||
- [ ] All test pods removed
|
||||
- [ ] All test RBAC resources removed
|
||||
- [ ] All test namespaces cleaned up
|
||||
- [ ] No persistent backdoors remain
|
||||
@@ -0,0 +1,54 @@
|
||||
# Standards Reference - Kubernetes Penetration Testing
|
||||
|
||||
## MITRE ATT&CK for Containers
|
||||
|
||||
### Relevant Techniques
|
||||
| ID | Technique | Phase |
|
||||
|----|-----------|-------|
|
||||
| T1609 | Container Administration Command | Execution |
|
||||
| T1610 | Deploy Container | Execution |
|
||||
| T1611 | Escape to Host | Privilege Escalation |
|
||||
| T1613 | Container and Resource Discovery | Discovery |
|
||||
| T1612 | Build Image on Host | Defense Evasion |
|
||||
| T1552.007 | Container API | Credential Access |
|
||||
|
||||
## CIS Kubernetes Benchmark v1.8
|
||||
|
||||
### Master Node Checks
|
||||
- 1.1: Control Plane Configuration Files
|
||||
- 1.2: API Server (anonymous auth, RBAC, audit logging)
|
||||
- 1.3: Controller Manager
|
||||
- 1.4: Scheduler
|
||||
|
||||
### Worker Node Checks
|
||||
- 4.1: Worker Node Configuration Files
|
||||
- 4.2: Kubelet (anonymous auth, authorization mode)
|
||||
|
||||
### Policies
|
||||
- 5.1: RBAC and Service Accounts
|
||||
- 5.2: Pod Security Standards
|
||||
- 5.3: Network Policies
|
||||
- 5.4: Secrets Management
|
||||
|
||||
## NSA/CISA Kubernetes Hardening Guide
|
||||
|
||||
### Key Areas
|
||||
- Scan containers and pods for vulnerabilities
|
||||
- Run containers as non-root users
|
||||
- Use network policies to restrict traffic
|
||||
- Encrypt secrets at rest
|
||||
- Audit logging for all API calls
|
||||
- Scan for misconfigurations regularly
|
||||
|
||||
## OWASP Kubernetes Top 10
|
||||
|
||||
1. K01: Insecure Workload Configurations
|
||||
2. K02: Supply Chain Vulnerabilities
|
||||
3. K03: Overly Permissive RBAC
|
||||
4. K04: Lack of Centralized Policy Enforcement
|
||||
5. K05: Inadequate Logging and Monitoring
|
||||
6. K06: Broken Authentication
|
||||
7. K07: Missing Network Segmentation
|
||||
8. K08: Secrets Management Failures
|
||||
9. K09: Misconfigured Cluster Components
|
||||
10. K10: Outdated and Vulnerable Kubernetes Components
|
||||
@@ -0,0 +1,92 @@
|
||||
# Workflows - Kubernetes Penetration Testing
|
||||
|
||||
## Workflow 1: External Kubernetes Pentest
|
||||
|
||||
```
|
||||
[Scope Definition] --> [Reconnaissance] --> [Service Discovery]
|
||||
| | |
|
||||
v v v
|
||||
Define targets DNS, OSINT, nmap 6443,8443
|
||||
Rules of engagement cloud metadata 10250,2379,30000+
|
||||
| | |
|
||||
+---------------------+--------------------+
|
||||
|
|
||||
v
|
||||
[Automated Scanning]
|
||||
kube-hunter --remote
|
||||
kubescape scan
|
||||
kube-bench (if access)
|
||||
|
|
||||
+---------+---------+
|
||||
| |
|
||||
v v
|
||||
[API Server Tests] [Kubelet Tests]
|
||||
Anonymous auth Unauthenticated access
|
||||
RBAC enumeration Command execution
|
||||
Token theft Pod listing
|
||||
| |
|
||||
+-------------------+
|
||||
|
|
||||
v
|
||||
[Exploitation]
|
||||
Deploy privileged pod
|
||||
Extract secrets
|
||||
Pivot to other namespaces
|
||||
|
|
||||
v
|
||||
[Report and Remediate]
|
||||
```
|
||||
|
||||
## Workflow 2: Internal/Assumed-Breach Testing
|
||||
|
||||
```
|
||||
Step 1: Initial Pod Access
|
||||
- Deploy test pod in target namespace
|
||||
- Collect service account token
|
||||
- Enumerate permissions: kubectl auth can-i --list
|
||||
|
||||
Step 2: Internal Reconnaissance
|
||||
- List namespaces, pods, services
|
||||
- Discover internal services via DNS
|
||||
- Check metadata endpoints (cloud IMDS)
|
||||
- Identify NetworkPolicy gaps
|
||||
|
||||
Step 3: Privilege Escalation
|
||||
- Check for wildcard RBAC roles
|
||||
- Test service account token from other pods
|
||||
- Attempt to create privileged pods
|
||||
- Check for vulnerable admission controllers
|
||||
|
||||
Step 4: Lateral Movement
|
||||
- Access services in other namespaces
|
||||
- Extract secrets and configmaps
|
||||
- Attempt container escape
|
||||
- Access cloud provider metadata
|
||||
|
||||
Step 5: Impact Assessment
|
||||
- Demonstrate data access (secrets, PVCs)
|
||||
- Show cluster-wide compromise path
|
||||
- Document attack chain
|
||||
```
|
||||
|
||||
## Workflow 3: Pentest Cleanup
|
||||
|
||||
```
|
||||
[Testing Complete]
|
||||
|
|
||||
v
|
||||
[Remove all pentest pods]
|
||||
kubectl delete pods -l purpose=pentest -A
|
||||
|
|
||||
v
|
||||
[Remove test RBAC resources]
|
||||
kubectl delete rolebinding pentest-rb
|
||||
kubectl delete serviceaccount pentest-sa
|
||||
|
|
||||
v
|
||||
[Verify cleanup]
|
||||
kubectl get all -l purpose=pentest -A
|
||||
|
|
||||
v
|
||||
[Document findings and hand off report]
|
||||
```
|
||||
@@ -0,0 +1,414 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Kubernetes Penetration Testing Automation Tool
|
||||
|
||||
Performs automated security checks against Kubernetes clusters
|
||||
including RBAC enumeration, secret exposure, network policy gaps,
|
||||
and misconfiguration detection.
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import json
|
||||
import sys
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Optional
|
||||
|
||||
|
||||
@dataclass
|
||||
class PentestFinding:
|
||||
category: str
|
||||
title: str
|
||||
severity: str
|
||||
details: str
|
||||
impact: str
|
||||
remediation: str
|
||||
mitre_id: str = ""
|
||||
|
||||
|
||||
@dataclass
|
||||
class PentestReport:
|
||||
findings: list = field(default_factory=list)
|
||||
cluster_info: dict = field(default_factory=dict)
|
||||
|
||||
|
||||
def run_kubectl(args: list, timeout: int = 30) -> tuple:
|
||||
cmd = ["kubectl"] + args
|
||||
try:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
|
||||
return result.returncode, result.stdout.strip(), result.stderr.strip()
|
||||
except (subprocess.TimeoutExpired, FileNotFoundError) as e:
|
||||
return -1, "", str(e)
|
||||
|
||||
|
||||
def run_kubectl_json(args: list) -> Optional[dict]:
|
||||
rc, out, _ = run_kubectl(args + ["-o", "json"])
|
||||
if rc != 0 or not out:
|
||||
return None
|
||||
try:
|
||||
return json.loads(out)
|
||||
except json.JSONDecodeError:
|
||||
return None
|
||||
|
||||
|
||||
def get_cluster_info(report: PentestReport):
|
||||
"""Gather basic cluster information."""
|
||||
rc, version_out, _ = run_kubectl(["version", "--short"])
|
||||
if rc == 0:
|
||||
report.cluster_info["version"] = version_out
|
||||
|
||||
rc, nodes_out, _ = run_kubectl(["get", "nodes", "-o", "wide", "--no-headers"])
|
||||
if rc == 0:
|
||||
report.cluster_info["nodes"] = len(nodes_out.split("\n"))
|
||||
|
||||
rc, ns_out, _ = run_kubectl(["get", "namespaces", "--no-headers"])
|
||||
if rc == 0:
|
||||
report.cluster_info["namespaces"] = len(ns_out.split("\n"))
|
||||
|
||||
|
||||
def test_anonymous_access(report: PentestReport):
|
||||
"""Test for anonymous API server access."""
|
||||
print("[*] Testing anonymous API access...")
|
||||
|
||||
test_commands = [
|
||||
(["get", "namespaces"], "List namespaces"),
|
||||
(["get", "pods", "-A"], "List all pods"),
|
||||
(["get", "secrets", "-A"], "List all secrets"),
|
||||
(["get", "nodes"], "List nodes"),
|
||||
]
|
||||
|
||||
for cmd, description in test_commands:
|
||||
rc, out, err = run_kubectl(["--as=system:anonymous"] + cmd)
|
||||
if rc == 0 and "Forbidden" not in err:
|
||||
report.findings.append(PentestFinding(
|
||||
category="Authentication",
|
||||
title=f"Anonymous access allowed: {description}",
|
||||
severity="CRITICAL",
|
||||
details=f"Anonymous user can: {description}",
|
||||
impact="Unauthenticated users can access cluster resources",
|
||||
remediation="Disable anonymous authentication: --anonymous-auth=false",
|
||||
mitre_id="T1078"
|
||||
))
|
||||
|
||||
|
||||
def test_rbac_misconfigurations(report: PentestReport):
|
||||
"""Check for overly permissive RBAC configurations."""
|
||||
print("[*] Testing RBAC configurations...")
|
||||
|
||||
# Check cluster role bindings for dangerous subjects
|
||||
crbs = run_kubectl_json(["get", "clusterrolebindings"])
|
||||
if crbs:
|
||||
for crb in crbs.get("items", []):
|
||||
name = crb["metadata"]["name"]
|
||||
role_ref = crb.get("roleRef", {}).get("name", "")
|
||||
subjects = crb.get("subjects", [])
|
||||
|
||||
for subject in subjects:
|
||||
subject_name = subject.get("name", "")
|
||||
subject_kind = subject.get("kind", "")
|
||||
|
||||
# Check for dangerous bindings
|
||||
dangerous_subjects = [
|
||||
"system:anonymous",
|
||||
"system:unauthenticated",
|
||||
"system:authenticated",
|
||||
]
|
||||
|
||||
if subject_name in dangerous_subjects and role_ref in ("cluster-admin", "admin", "edit"):
|
||||
report.findings.append(PentestFinding(
|
||||
category="RBAC",
|
||||
title=f"Dangerous ClusterRoleBinding: {name}",
|
||||
severity="CRITICAL",
|
||||
details=f"Subject '{subject_name}' bound to role '{role_ref}'",
|
||||
impact="Broad access granted to anonymous or all authenticated users",
|
||||
remediation=f"Remove or restrict ClusterRoleBinding '{name}'",
|
||||
mitre_id="T1078.004"
|
||||
))
|
||||
|
||||
# Check for wildcard permissions in ClusterRoles
|
||||
cluster_roles = run_kubectl_json(["get", "clusterroles"])
|
||||
if cluster_roles:
|
||||
for cr in cluster_roles.get("items", []):
|
||||
name = cr["metadata"]["name"]
|
||||
if name.startswith("system:"):
|
||||
continue
|
||||
|
||||
for rule in cr.get("rules", []):
|
||||
verbs = rule.get("verbs", [])
|
||||
resources = rule.get("resources", [])
|
||||
api_groups = rule.get("apiGroups", [])
|
||||
|
||||
if "*" in verbs and "*" in resources:
|
||||
report.findings.append(PentestFinding(
|
||||
category="RBAC",
|
||||
title=f"Wildcard ClusterRole: {name}",
|
||||
severity="HIGH",
|
||||
details=f"Role grants all verbs on all resources (apiGroups: {api_groups})",
|
||||
impact="Effectively cluster-admin level access",
|
||||
remediation="Apply least privilege - specify exact verbs and resources",
|
||||
mitre_id="T1078.004"
|
||||
))
|
||||
|
||||
|
||||
def test_secret_exposure(report: PentestReport):
|
||||
"""Check for exposed or poorly protected secrets."""
|
||||
print("[*] Testing secret exposure...")
|
||||
|
||||
secrets = run_kubectl_json(["get", "secrets", "-A"])
|
||||
if not secrets:
|
||||
return
|
||||
|
||||
sa_token_count = 0
|
||||
opaque_count = 0
|
||||
|
||||
for secret in secrets.get("items", []):
|
||||
secret_type = secret.get("type", "")
|
||||
name = secret["metadata"]["name"]
|
||||
namespace = secret["metadata"]["namespace"]
|
||||
|
||||
if secret_type == "kubernetes.io/service-account-token":
|
||||
sa_token_count += 1
|
||||
|
||||
# Check for secrets in environment variables
|
||||
pods = run_kubectl_json(["get", "pods", "-A"])
|
||||
if pods:
|
||||
for pod in pods.get("items", []):
|
||||
pod_name = pod["metadata"]["name"]
|
||||
pod_ns = pod["metadata"]["namespace"]
|
||||
|
||||
for container in pod.get("spec", {}).get("containers", []):
|
||||
for env in container.get("env", []):
|
||||
value = env.get("value", "")
|
||||
name_env = env.get("name", "").upper()
|
||||
|
||||
# Check for hardcoded sensitive values
|
||||
sensitive_names = ["PASSWORD", "SECRET", "API_KEY", "TOKEN", "PRIVATE_KEY"]
|
||||
if any(s in name_env for s in sensitive_names) and value and not env.get("valueFrom"):
|
||||
report.findings.append(PentestFinding(
|
||||
category="Secrets",
|
||||
title=f"Hardcoded secret in pod env: {pod_ns}/{pod_name}",
|
||||
severity="HIGH",
|
||||
details=f"Container '{container.get('name')}' has hardcoded '{name_env}'",
|
||||
impact="Secrets visible in pod spec, accessible via API",
|
||||
remediation="Use Kubernetes Secrets or external secret store",
|
||||
mitre_id="T1552.007"
|
||||
))
|
||||
|
||||
|
||||
def test_network_policies(report: PentestReport):
|
||||
"""Check for missing or insufficient network policies."""
|
||||
print("[*] Testing network policies...")
|
||||
|
||||
namespaces = run_kubectl_json(["get", "namespaces"])
|
||||
if not namespaces:
|
||||
return
|
||||
|
||||
for ns in namespaces.get("items", []):
|
||||
ns_name = ns["metadata"]["name"]
|
||||
if ns_name in ("kube-system", "kube-public", "kube-node-lease"):
|
||||
continue
|
||||
|
||||
netpols = run_kubectl_json(["get", "networkpolicies", "-n", ns_name])
|
||||
if not netpols or not netpols.get("items"):
|
||||
report.findings.append(PentestFinding(
|
||||
category="Network",
|
||||
title=f"No NetworkPolicies in namespace: {ns_name}",
|
||||
severity="MEDIUM",
|
||||
details=f"Namespace '{ns_name}' has no network policies",
|
||||
impact="All pod-to-pod traffic is allowed (flat network)",
|
||||
remediation=f"Create default-deny NetworkPolicy in namespace '{ns_name}'",
|
||||
mitre_id="T1046"
|
||||
))
|
||||
|
||||
|
||||
def test_pod_security(report: PentestReport):
|
||||
"""Check for insecure pod configurations."""
|
||||
print("[*] Testing pod security configurations...")
|
||||
|
||||
pods = run_kubectl_json(["get", "pods", "-A"])
|
||||
if not pods:
|
||||
return
|
||||
|
||||
for pod in pods.get("items", []):
|
||||
pod_name = pod["metadata"]["name"]
|
||||
pod_ns = pod["metadata"]["namespace"]
|
||||
spec = pod.get("spec", {})
|
||||
|
||||
# Skip system namespaces
|
||||
if pod_ns in ("kube-system", "kube-public", "falco-system"):
|
||||
continue
|
||||
|
||||
# Check hostPID, hostNetwork, hostIPC
|
||||
if spec.get("hostPID"):
|
||||
report.findings.append(PentestFinding(
|
||||
category="Pod Security",
|
||||
title=f"hostPID enabled: {pod_ns}/{pod_name}",
|
||||
severity="CRITICAL",
|
||||
details="Pod shares host PID namespace",
|
||||
impact="Can see and potentially interact with host processes",
|
||||
remediation="Set hostPID: false",
|
||||
mitre_id="T1611"
|
||||
))
|
||||
|
||||
if spec.get("hostNetwork"):
|
||||
report.findings.append(PentestFinding(
|
||||
category="Pod Security",
|
||||
title=f"hostNetwork enabled: {pod_ns}/{pod_name}",
|
||||
severity="HIGH",
|
||||
details="Pod shares host network namespace",
|
||||
impact="Can access host network interfaces and services",
|
||||
remediation="Set hostNetwork: false",
|
||||
mitre_id="T1611"
|
||||
))
|
||||
|
||||
for container in spec.get("containers", []):
|
||||
sc = container.get("securityContext", {})
|
||||
c_name = container.get("name", "")
|
||||
|
||||
if sc.get("privileged"):
|
||||
report.findings.append(PentestFinding(
|
||||
category="Pod Security",
|
||||
title=f"Privileged container: {pod_ns}/{pod_name}/{c_name}",
|
||||
severity="CRITICAL",
|
||||
details="Container runs with full host privileges",
|
||||
impact="Trivial container escape to host",
|
||||
remediation="Set privileged: false, use specific capabilities",
|
||||
mitre_id="T1611"
|
||||
))
|
||||
|
||||
# Check automountServiceAccountToken
|
||||
if spec.get("automountServiceAccountToken", True):
|
||||
sa = spec.get("serviceAccountName", "default")
|
||||
if sa == "default":
|
||||
report.findings.append(PentestFinding(
|
||||
category="Pod Security",
|
||||
title=f"Default SA token mounted: {pod_ns}/{pod_name}",
|
||||
severity="LOW",
|
||||
details="Default service account token auto-mounted",
|
||||
impact="Token accessible at /var/run/secrets/kubernetes.io/serviceaccount/token",
|
||||
remediation="Set automountServiceAccountToken: false",
|
||||
mitre_id="T1552.007"
|
||||
))
|
||||
|
||||
|
||||
def test_pss_enforcement(report: PentestReport):
|
||||
"""Check Pod Security Standards enforcement on namespaces."""
|
||||
print("[*] Testing PSS enforcement...")
|
||||
|
||||
namespaces = run_kubectl_json(["get", "namespaces"])
|
||||
if not namespaces:
|
||||
return
|
||||
|
||||
for ns in namespaces.get("items", []):
|
||||
ns_name = ns["metadata"]["name"]
|
||||
labels = ns["metadata"].get("labels", {})
|
||||
|
||||
if ns_name in ("kube-system", "kube-public", "kube-node-lease"):
|
||||
continue
|
||||
|
||||
enforce = labels.get("pod-security.kubernetes.io/enforce")
|
||||
if not enforce:
|
||||
report.findings.append(PentestFinding(
|
||||
category="PSS",
|
||||
title=f"No PSS enforcement on namespace: {ns_name}",
|
||||
severity="MEDIUM",
|
||||
details=f"Namespace '{ns_name}' lacks PSA enforce label",
|
||||
impact="No built-in restrictions on pod security contexts",
|
||||
remediation=f"Label namespace with pod-security.kubernetes.io/enforce=baseline or restricted"
|
||||
))
|
||||
elif enforce == "privileged":
|
||||
report.findings.append(PentestFinding(
|
||||
category="PSS",
|
||||
title=f"Privileged PSS on non-system namespace: {ns_name}",
|
||||
severity="HIGH",
|
||||
details=f"Namespace '{ns_name}' allows privileged pods",
|
||||
impact="No restrictions on pod configurations",
|
||||
remediation="Change PSS enforce level to baseline or restricted"
|
||||
))
|
||||
|
||||
|
||||
def print_report(report: PentestReport):
|
||||
"""Print pentest results."""
|
||||
print("\n" + "=" * 70)
|
||||
print("KUBERNETES PENETRATION TEST REPORT")
|
||||
print("=" * 70)
|
||||
|
||||
if report.cluster_info:
|
||||
print(f"\nCluster Info:")
|
||||
for k, v in report.cluster_info.items():
|
||||
print(f" {k}: {v}")
|
||||
|
||||
print(f"\nTotal Findings: {len(report.findings)}")
|
||||
severity_counts = {}
|
||||
for f in report.findings:
|
||||
severity_counts[f.severity] = severity_counts.get(f.severity, 0) + 1
|
||||
for sev in ["CRITICAL", "HIGH", "MEDIUM", "LOW"]:
|
||||
print(f" {sev}: {severity_counts.get(sev, 0)}")
|
||||
|
||||
print("=" * 70)
|
||||
|
||||
for severity in ["CRITICAL", "HIGH", "MEDIUM", "LOW"]:
|
||||
findings = [f for f in report.findings if f.severity == severity]
|
||||
if findings:
|
||||
print(f"\n{severity} FINDINGS:")
|
||||
print("-" * 70)
|
||||
for f in findings:
|
||||
print(f" [{f.category}] {f.title}")
|
||||
print(f" Details: {f.details}")
|
||||
print(f" Impact: {f.impact}")
|
||||
print(f" Fix: {f.remediation}")
|
||||
if f.mitre_id:
|
||||
print(f" MITRE: {f.mitre_id}")
|
||||
print()
|
||||
|
||||
|
||||
def main():
|
||||
print("[*] Kubernetes Penetration Testing Tool")
|
||||
print("[*] Authorized testing only\n")
|
||||
|
||||
report = PentestReport()
|
||||
|
||||
get_cluster_info(report)
|
||||
test_anonymous_access(report)
|
||||
test_rbac_misconfigurations(report)
|
||||
test_secret_exposure(report)
|
||||
test_network_policies(report)
|
||||
test_pod_security(report)
|
||||
test_pss_enforcement(report)
|
||||
|
||||
print_report(report)
|
||||
|
||||
output = {
|
||||
"cluster_info": report.cluster_info,
|
||||
"summary": {
|
||||
"total_findings": len(report.findings),
|
||||
"critical": sum(1 for f in report.findings if f.severity == "CRITICAL"),
|
||||
"high": sum(1 for f in report.findings if f.severity == "HIGH"),
|
||||
"medium": sum(1 for f in report.findings if f.severity == "MEDIUM"),
|
||||
"low": sum(1 for f in report.findings if f.severity == "LOW"),
|
||||
},
|
||||
"findings": [
|
||||
{
|
||||
"category": f.category,
|
||||
"title": f.title,
|
||||
"severity": f.severity,
|
||||
"details": f.details,
|
||||
"impact": f.impact,
|
||||
"remediation": f.remediation,
|
||||
"mitre_id": f.mitre_id,
|
||||
}
|
||||
for f in report.findings
|
||||
],
|
||||
}
|
||||
|
||||
with open("k8s_pentest_report.json", "w") as f:
|
||||
json.dump(output, f, indent=2)
|
||||
print("[*] Report saved to k8s_pentest_report.json")
|
||||
|
||||
critical_count = output["summary"]["critical"]
|
||||
if critical_count > 0:
|
||||
print(f"\n[!] {critical_count} CRITICAL findings require immediate attention")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user