In this section

LX1.8 Container and Kubernetes Evidence Collection

3-4 hours · Module 1 · Free

Container Evidence Collection: Racing the Restart Clock

Operational Objective
The Ephemeral Evidence Problem: A compromised Kubernetes pod running your web application will be restarted by the liveness probe in approximately 90 seconds. When it restarts, the container's writable filesystem layer — containing the attacker's web shell, modified configurations, staged tools, and bash history — is erased completely. The new pod starts from the pristine base image with zero trace of the compromise. You need to capture the container filesystem, logs, metadata, and process state before the orchestrator destroys the evidence. Every second spent deciding what to collect is a second of evidence you lose.
Deliverable: The complete Docker and Kubernetes evidence collection workflow — 8-step container capture, dead container recovery procedures, Kubernetes pod and cluster-level evidence collection, and container escape detection. Understanding the three-layer evidence model: container writable layer, orchestrator metadata, and host-level evidence.
⏱ Estimated completion: 40 minutes

Why Container Collection Is Different

Container evidence collection violates the fundamental assumption of forensics: that evidence persists until collected. On a bare-metal server, evidence exists on a physical disk that survives reboots, power cycles, and — if you do nothing — indefinitely. On a container, the writable filesystem layer exists only while the container is running. When a Kubernetes pod is terminated and rescheduled, the new pod starts from the pristine base image with no trace of what happened in the previous instance. The attacker's web shell, the modified configuration files, the bash history, the downloaded tools — all erased by a container restart.

This creates an urgency that does not exist in other investigation environments. On a bare-metal server, you can wait hours before beginning collection — the evidence is stable. On a container, you may have minutes before a liveness probe failure, an autoscaler decision, or a manual restart destroys the evidence.

Docker Container Collection — Complete Workflow

# Step 1: Confirm the container is still running
docker ps --filter name=compromised-app
# If the container is not running, skip to "Dead Container Recovery"

# Step 2: Capture container metadata (configuration, env vars, mounts)
docker inspect compromised-app > evidence/docker_inspect.json

# Step 3: Capture container logs (stdout/stderr — application output)
docker logs --timestamps compromised-app > evidence/docker_logs.txt 2>&1

# Step 4: Identify what the attacker changed (diff from base image)
docker diff compromised-app > evidence/docker_diff.txt
# Output: A = added, C = changed, D = deleted
# Every entry is a file the attacker created or modified

# Step 5: Export the complete container filesystem
docker export compromised-app > evidence/container_filesystem.tar
# This captures the merged view — base image + writable layer

# Step 6: Copy specific directories for targeted analysis
docker cp compromised-app:/var/log/ evidence/container_var_log/
docker cp compromised-app:/tmp/ evidence/container_tmp/
docker cp compromised-app:/etc/crontab evidence/container_crontab 2>/dev/null
docker cp compromised-app:/root/.bash_history evidence/container_bash_history 2>/dev/null

# Step 7: Capture the running process state inside the container
docker exec compromised-app ps auxf > evidence/container_processes.txt 2>/dev/null
docker exec compromised-app ss -tlnp > evidence/container_network.txt 2>/dev/null
docker exec compromised-app cat /etc/resolv.conf > evidence/container_dns.txt 2>/dev/null

# Step 8: Check for container escape indicators
docker exec compromised-app ls -la /var/run/docker.sock 2>/dev/null && \
  echo "WARNING: Docker socket mounted inside container" > evidence/escape_indicator.txt
docker exec compromised-app cat /proc/1/cgroup > evidence/container_cgroup.txt 2>/dev/null
# Pod-level evidence
kubectl describe pod suspicious-pod -n production > evidence/pod_describe.txt
kubectl logs suspicious-pod -n production --all-containers > evidence/pod_logs.txt 2>&1
kubectl logs suspicious-pod -n production --previous > evidence/pod_logs_previous.txt 2>/dev/null
kubectl cp production/suspicious-pod:/var/log/ evidence/pod_var_log/
kubectl cp production/suspicious-pod:/tmp/ evidence/pod_tmp/

# Exec into the pod for live volatile collection
kubectl exec -it suspicious-pod -n production -- ps auxf > evidence/pod_processes.txt 2>/dev/null
kubectl exec -it suspicious-pod -n production -- ss -tlnp > evidence/pod_network.txt 2>/dev/null
kubectl exec -it suspicious-pod -n production -- cat /proc/1/cgroup > evidence/pod_cgroup.txt 2>/dev/null

# Cluster-level evidence
kubectl get events -n production --field-selector involvedObject.name=suspicious-pod --sort-by='.lastTimestamp' > evidence/k8s_events.txt
kubectl get pod suspicious-pod -n production -o yaml > evidence/pod_yaml.txt

# Service account token (was it stolen for RBAC abuse?)
kubectl exec suspicious-pod -n production -- cat /var/run/secrets/kubernetes.io/serviceaccount/token > evidence/sa_token.txt 2>/dev/null

# Kubernetes audit log (if enabled — cluster admin must provide access)
# Location varies: /var/log/kubernetes/audit.log, or cloud provider's log service
Expand for Deeper Context

The Docker evidence collection workflow captures three categories of evidence: the container filesystem (what the attacker modified), the container logs (what the application recorded), and the container metadata (how the container was configured).

The docker diff output is uniquely valuable for container forensics. Because the base image is known and immutable (it is the image you deployed), every file that appears as Added or Changed in the diff is either legitimate application activity (log files, cache files, session data) or attacker activity. Separate the two by comparing against expected application behavior. A PHP web shell appearing as an Added file in the web root is clearly attacker activity. A modified /etc/passwd appearing as a Changed file indicates the attacker created a new user account.

Dead Container Recovery

If the container has already been terminated, your evidence options narrow significantly but do not disappear entirely.

Container runtime logs: Docker stores container logs even after termination (until the container is removed with docker rm). If the container was terminated but not removed: docker logs > evidence/dead_container_logs.txt.

Docker data directory: The container's writable layer persists on the host filesystem in /var/lib/docker/ until the container is removed. The location depends on the storage driver: /var/lib/docker/overlay2//diff/ for overlay2 (the default on modern Docker installations). The layer ID is in the container's inspect output. If the container was terminated but not removed, you can browse the writable layer directly on the host.

Persistent volumes: Any data stored on Docker volumes (docker volume ls) or bind mounts survives container termination. Check the container's inspect output for Mounts — any volume or bind mount paths still contain their data on the host filesystem.

Container image: Even if the container is gone, the image it was running from is still on the host (unless explicitly removed). docker images lists available images. docker history shows the image build layers. Compare the image against the known-good image from your container registry — if the image was tampered with (the attacker modified the image and redeployed), the layer digests will differ.

Kubernetes Pod Collection

Kubernetes adds an orchestration layer above Docker/containerd. The collection workflow must capture evidence from both the pod (container-level) and the cluster (orchestrator-level).

The kubectl logs --previous flag retrieves logs from the previous container instance in the same pod — if the container crashed and restarted, the previous instance's logs may contain the evidence of the attacker's activity that triggered the crash.

The service account token collection (/var/run/secrets/kubernetes.io/serviceaccount/token) is critical for detecting RBAC abuse. If the attacker stole the pod's service account token, they can make API calls to the Kubernetes control plane — creating pods, reading secrets, modifying deployments. The audit log records these API calls, but you need the token to correlate them with the compromised pod.

Container Escape Detection

Container escape — where the attacker breaks out of the container to the host — fundamentally changes the investigation scope. What was a container-level incident becomes a host-level incident, potentially affecting every container on the node.

Evidence of container escape:

Docker socket mount: ls -la /var/run/docker.sock inside the container. If the Docker socket is mounted into the container, the attacker can issue Docker commands against the host's Docker daemon — creating new privileged containers, accessing other containers' filesystems, and executing commands on the host. This is the most common container escape vector.

Privileged container: check docker inspect output for "Privileged": true. A privileged container has nearly unrestricted access to the host kernel — the attacker can load kernel modules, access host devices, and modify host filesystems.

Excessive capabilities: check for CAP_SYS_ADMIN, CAP_SYS_PTRACE, CAP_NET_ADMIN in the container's capability set. These capabilities enable escape paths.

Host process visibility: from inside the container, cat /proc/1/cgroup — if the output shows the host's cgroup hierarchy rather than the container's, the process may have escaped the PID namespace.

CONTAINER EVIDENCE — THREE-LAYER MODEL Layer 1: Container MOST VOLATILE — collect first docker diff (what changed) docker export (full filesystem) docker logs (stdout/stderr) docker exec ps/ss (processes) Destroyed on container restart Layer 2: Orchestrator MODERATE — survives restart K8s events (pod lifecycle) K8s audit log (API calls) Pod YAML (config, SA token) kubectl logs --previous Retention varies (1h-90d) Layer 3: Host / Node PERSISTENT — survives restart Persistent volumes (PVs) Container runtime logs /var/lib/docker/overlay2/ Node host if escape occurred Survives container termination COLLECTION PRIORITY: Layer 1 first (seconds matter) → Layer 2 → Layer 3 If container is already terminated: Layer 1 is gone. Check Layer 3 for residual evidence, Layer 2 for orchestrator records, and persistent volumes for application data.
Figure LX1.8 — The three-layer container evidence model. Layer 1 (container writable layer) is the most volatile and must be collected first. Layer 2 (orchestrator metadata) survives restarts but has variable retention. Layer 3 (host/node) persists until explicitly deleted.

Worked artifact — Container evidence collection checklist:

Use this checklist for every container investigation. Check each item as completed.

Case: INC-2026-XXXX Container/Pod: [name] Image: [image:tag]

Layer 1 — Container (collect immediately): - ☐ Container still running confirmed (docker ps / kubectl get pods) - ☐ Metadata captured (docker inspect / kubectl describe pod) - ☐ Logs captured (docker logs / kubectl logs --all-containers) - ☐ Filesystem diff captured (docker diff) - ☐ Full filesystem exported (docker export / kubectl cp) - ☐ Process state captured (docker exec ps auxf) - ☐ Network state captured (docker exec ss -tnp) - ☐ Escape indicators checked (docker.sock mount, privileged flag, capabilities)

Layer 2 — Orchestrator: - ☐ Previous pod logs captured (kubectl logs --previous) - ☐ Kubernetes events collected (kubectl get events) - ☐ Pod YAML exported (kubectl get pod -o yaml) - ☐ Service account token captured (if RBAC abuse suspected) - ☐ Kubernetes audit log requested from cluster admin

Layer 3 — Host/Node: - ☐ Persistent volumes identified and preserved - ☐ Container writable layer on host checked (/var/lib/docker/overlay2/) - ☐ Node-level investigation initiated (if escape detected)

Escape assessment: ☐ No escape indicators ☐ Escape suspected → escalate to host investigation

Decision points: container collection priorities

Container is running and attacker is active inside it: Run the 8-step Docker collection immediately. Do not wait for approval. Do not coordinate with the application team first. Collect, then communicate. The evidence disappears the moment the container restarts.

Container has already been terminated and replaced: Layer 1 evidence is gone. Check Layer 3: persistent volumes, the overlay2 directory on the host (if container was not docker rm'd), and runtime logs. Check Layer 2: kubectl logs --previous, Kubernetes events, audit log. Document what is unavailable.

Multiple compromised pods in the same namespace: Script the collection: for pod in pod1 pod2 pod3; do kubectl exec $pod -- tar czf - /var/log /tmp | tar xzf - -C evidence/$pod/ & done; wait. Parallel collection from all pods simultaneously. Then return for the full 8-step workflow on each.

Container escape suspected: The investigation scope expands from container to host. Collect Layer 1 evidence from the container first, then pivot to the host node for a full Linux investigation (the entire LX1 collection sequence applies to the node).

Troubleshooting: common container collection issues

docker exec fails with "container not running." The container was terminated between your docker ps check and the exec command. The writable layer evidence is gone unless the container was not removed. Check docker ps -a for stopped containers — you can still run docker logs and docker diff on stopped (but not removed) containers.

kubectl cp fails with "tar not found in container." Minimal container images (Alpine, distroless, scratch) may not include tar, which kubectl cp requires. Alternative: kubectl exec pod -- cat /var/log/syslog > evidence/syslog to copy individual files. For full filesystem capture, exec into the node and access the container's overlay filesystem directly.

Container logs are empty or truncated. The container may be using a logging driver that sends logs to an external system (Fluentd, CloudWatch, Stackdriver) rather than storing them locally. Check docker inspect for the LogConfig section. Collect logs from the external logging system instead.

docker diff shows thousands of changes. The application writes heavily to its writable layer (cache files, session data, temp files). The attacker's modifications are buried in legitimate application changes. Filter the diff output: focus on executable files (grep -E '\.(sh|py|pl|php|elf)$'), files in unusual locations (/tmp, /dev/shm, web roots), and configuration file changes (/etc/).

Beyond this investigation: LX9 (Container Compromise) is dedicated entirely to container and Kubernetes forensics, from pod-level evidence collection to cluster-wide scope assessment across multiple container runtimes.

Myth: "Container forensics is impossible because containers are ephemeral — there is nothing to investigate."

Reality: Container ephemerality makes forensics urgent, not impossible. A running container contains the complete filesystem, process state, network connections, and logs — the same evidence as a bare-metal server. The constraint is time, not capability. Additionally, the docker diff command provides evidence that is actually cleaner than bare-metal forensics: because the base image is known-good, every change in the diff is a deviation that must be explained. Containers also produce orchestrator-level evidence (Kubernetes events, audit logs, service account activity) that has no equivalent in bare-metal environments.

Try it yourself

Practice the complete Docker collection workflow.

Practice the complete Docker collection workflow. Run a test container: docker run -d --name forensic-test nginx:latest. Make a change inside it: docker exec forensic-test bash -c "echo 'test-evidence' > /tmp/attacker-file.txt". Now run the full collection sequence from Step 1–8 above. Examine the docker diff output — you should see /tmp/attacker-file.txt as an Added file. Export the filesystem and verify you can find the file in the tar archive: tar tf evidence/container_filesystem.tar | grep attacker. Clean up: docker rm -f forensic-test.

Beyond This Investigation

Container evidence collection is the foundation for LX9 (Container Compromise), which investigates a complete container breach scenario including escape to the host and lateral movement through the Kubernetes API. The collection techniques in this subsection capture the evidence that LX9's analysis examines in depth.

Check your understanding:

1. A compromised container was restarted by Kubernetes 5 minutes ago. What evidence from the previous container instance is still accessible? 2. What does the output of docker diff represent, and why is it more useful for container forensics than a full filesystem listing? 3. You find /var/run/docker.sock mounted inside a compromised container. What can the attacker do with this, and what evidence should you look for on the host? 4. The Kubernetes audit log shows create pod and get secrets API calls using the compromised pod's service account token. What does this indicate about the scope of the compromise?

Decision point

You arrive at a compromised Linux server. The server is still running and the attacker may still be active. Do you collect volatile evidence first or image the disk?

Volatile evidence first — always. Memory contents, running processes (ps auxf), network connections (ss -tlnp), logged-in users (who), and kernel modules (lsmod) exist only while the system is running. Disk evidence survives a reboot; volatile evidence does not. The collection sequence: LiME memory dump → process state → network state → user sessions → then proceed to disk evidence. If the attacker is active, the volatile evidence captures their current operations — processes, connections, and tools that disappear the moment the system is powered off or the attacker disconnects.

Unlock the Full Course See Full Course Agenda