Skip to main content
CleanStart

Knowledge Hub

Container Forensics and Evidence Preservation Guide

document version: 1.0 audience: Security Engineers, Forensic Analysts, Incident Response Teams reading time: 18 minutes updated: 2026-03-22.

Overview

When a security incident occurs, the ability to rapidly collect, preserve, and analyze evidence determines speed of containment and quality of root cause analysis. This guide provides procedures for container forensics with CleanStart images, including:

Live collection: Capture evidence while container is running Post-mortem analysis: Examine stopped container for signs of compromise Chain of custody: Document evidence handling to meet legal/compliance standards Artifact correlation: Link runtime evidence to CleanStart attestations and SBOMs.

Section 1: Pre-Incident Preparation

1.1 Evidence Infrastructure Setup

Before an incident occurs, establish dedicated forensics infrastructure:

storage.

# Create immutable forensics bucketgsutil mb gs://forensics-evidence-prod # Enable object immutability (cannot delete for 1 year)gsutil retention set 365d gs://forensics-evidence-prod # Enable audit logging (who accessed forensics data)gcloud logging sinks create forensics-audit \  gs://forensics-evidence-prod \  --log-filter='resource.type="gcs_bucket" AND bucket_name="forensics-evidence-prod"'

forensics container image.

# Build forensics container with analysis toolscat > Dockerfile.forensics <<EOFFROM registry.cleanstart.com/cleanstart/ubuntu:22.04RUN apt-get update && apt-get install -y \    strace \    ltrace \    strings \    objdump \    hexdump \    tcpdump \    wireshark-commonEOF docker build -f Dockerfile.forensics -t registry.company.com/forensics-toolkit:latest .docker push registry.company.com/forensics-toolkit:latest

Forensics Pod Manifest (for rapid deployment):

apiVersion: v1kind: Podmetadata:  name: forensics-toolkit  namespace: securityspec:  containers:  - name: toolkit    image: registry.company.com/forensics-toolkit:latest    volumeMounts:    - name: suspect-container-mnt      mountPath: /mnt/suspect  # Will mount suspect container's filesystem    - name: evidence-storage      mountPath: /evidence  # For captured evidence    securityContext:      privileged: true  # Required for deep forensics  serviceAccountName: forensics  volumes:  - name: evidence-storage    gcePersistentDisk:      pdName: forensics-evidence-disk      readOnly: false

Section 2: Live Evidence Collection

2.1 Immediate Actions (First 5 Minutes)

on-call engineer actions.

#!/bin/bashset -e # 1. Alert incident response teamecho "INCIDENT DECLARED: Suspected container compromise"# (Send to PagerDuty, Slack #security, etc.) # 2. Identify suspect containerSUSPECT_POD="auth-service-7f4d5c6"SUSPECT_NS="production"SUSPECT_CONTAINER="auth-app" # 3. Document pod details (before killing)kubectl get pod $SUSPECT_POD -n $SUSPECT_NS -o yaml > pod-metadata.yaml # 4. Capture running processeskubectl exec $SUSPECT_POD -n $SUSPECT_NS -c $SUSPECT_CONTAINER \  -- ps aux > evidence-001-processes.txt # 5. Capture open fileskubectl exec $SUSPECT_POD -n $SUSPECT_NS -c $SUSPECT_CONTAINER \  -- lsof > evidence-002-open-files.txt # 6. Capture network connectionskubectl exec $SUSPECT_POD -n $SUSPECT_NS -c $SUSPECT_CONTAINER \  -- netstat -tupan > evidence-003-network-connections.txt # 7. Capture environment variableskubectl exec $SUSPECT_POD -n $SUSPECT_NS -c $SUSPECT_CONTAINER \  -- env > evidence-004-environment.txt # 8. Capture system logs from containerkubectl logs $SUSPECT_POD -n $SUSPECT_NS -c $SUSPECT_CONTAINER \  --timestamps=true > evidence-005-container-logs.txt # 9. Timestamp all evidencefor file in evidence-*.txt; do  echo "Captured at: $(date -u +%Y-%m-%dT%H:%M:%SZ)" >> $filedone # 10. Calculate checksums (for chain of custody)sha256sum evidence-*.txt > MANIFEST.sha256 # 11. Archive evidencetar czf suspect-pod-evidence-$(date +%s).tar.gz evidence-*.txt MANIFEST.sha256 # 12. Upload to immutable forensics storagegsutil cp suspect-pod-evidence-*.tar.gz gs://forensics-evidence-prod/$(date +%Y/%m/%d)/

output.

evidence-001-processes.txt: All processes in container (may show injected malware)evidence-002-open-files.txt: Files/sockets open (may show C2 connections)evidence-003-network-connections.txt: Network state (which hosts was container talking to)evidence-004-environment.txt: Environment variables (may contain secrets, command injection)evidence-005-container-logs.txt: Container stdout/stderr logs (what did app log?)

2.2 Container Filesystem Snapshot

before killing the container, capture entire filesystem.

#!/bin/bash SUSPECT_POD="auth-service-7f4d5c6"SUSPECT_NS="production"SUSPECT_CONTAINER="auth-app" # Option 1: Copy filesystem from running container# (requires container to be interactive)kubectl debug $SUSPECT_POD -n $SUSPECT_NS -c $SUSPECT_CONTAINER \  --image=registry.cleanstart.com/cleanstart/ubuntu:22.04 \  --target=$SUSPECT_CONTAINER \  -- tar czf /tmp/container-fs-dump.tar.gz / # This spawns ephemeral debugging pod with access to suspect container's namespace# Then tars entire filesystem # Option 2: Docker-level snapshot (if on single node)CONTAINER_ID=$(kubectl describe pod $SUSPECT_POD -n $SUSPECT_NS | grep "Container ID" | head -1 | cut -d'/' -f3) docker export $CONTAINER_ID | gzip > container-filesystem-$(date +%s).tar.gz # Option 3: Overlay filesystem diff (only changed files)# (for running container with overlay2 storage driver)CONTAINER_ID=$(docker inspect $SUSPECT_POD | jq -r '.[0].Id')docker diff $CONTAINER_ID > filesystem-changes.txt

what the filesystem snapshot reveals New files created at runtime (shellcode, malware) Modified configuration files (backdoors) Downloaded tools (curl, wget, nc) Shell history (commands attacker ran).

2.3 Memory Dump and Analysis

capture container memory for malware analysis.

#!/bin/bash # Get container PID on hostCONTAINER_ID=$(kubectl get pod auth-service-7f4d5c6 -n production -o jsonpath='{.status.containerStatuses[0].containerID}' | cut -d'/' -f3) # Find main process PIDPID=$(docker inspect $CONTAINER_ID | jq -r '.[0].State.Pid') # Dump memory (requires root)gcore -o memory-dump $PID # This creates memory-dump.[PID] file# Analyze with:# - volatility: detect injected code, rootkits# - radare2: analyze binary code# - strings: search for suspicious strings

Section 3: Post-Mortem Analysis (Container Stopped)

3.1 Container Layer Analysis

analyze cleanstart image layers and what changed.

#!/bin/bash # 1. List image layersIMAGE="registry.cleanstart.com/cleanstart/python:3.11"docker image inspect $IMAGE --format='{{json .RootFS.Layers}}' | jq # 2. Extract each layermkdir -p layer-analysis docker save $IMAGE | tar -xf - -O | while read line; do  LAYER=$(echo $line | grep -o '"sha256:[^"]*"' | head -1 | tr -d '"')   # Extract layer contents  docker run --rm \    -v /var/lib/docker/overlay2:/overlay2:ro \    alpine:latest \    tar -xzf /overlay2/$LAYER/diffdone # 3. Diff layers against baselinediff -r layer-analysis/python-3.11-base layer-analysis/python-3.11-deployed

3.2 Container Image Attestation Verification

verify image hasn't been tampered with.

#!/bin/bash SUSPECT_IMAGE="registry.cleanstart.com/cleanstart/python:3.11" # 1. Verify image signaturecosign verify --key https://keys.cleanstart.com/prod.pub $SUSPECT_IMAGE # Output should show: Signature valid# If fails: Image was modified after build (forensic evidence of tampering) # 2. Download SBOMcosign download sbom $SUSPECT_IMAGE > evidence-sbom.json # 3. Download provenancecosign download attestation $SUSPECT_IMAGE > evidence-provenance.json # 4. Verify provenance signaturecosign verify-blob \  --key https://keys.cleanstart.com/prod.pub \  --signature evidence-provenance.json.sig \  evidence-provenance.json # If all verifications pass: Image came from CleanStart build system# If any fail: Supply chain compromise or post-build tampering

3.3 Extract Evidence from Container Layers

cleanstart containers have sbom embedded; use it for forensics.

#!/bin/bash # 1. Download embedded SBOM from imagecosign download sbom registry.cleanstart.com/cleanstart/python:3.11 | jq > image-sbom.json # 2. For each package in SBOM, check if it matches deployed statejq '.components[] | {name, version}' image-sbom.json > expected-packages.txt # 3. Extract actual packages from container filesystemdocker run --rm registry.cleanstart.com/cleanstart/python:3.11 \  sh -c 'pip freeze' > actual-packages.txt # 4. Diffdiff expected-packages.txt actual-packages.txt # If differences found:# - New packages: Container runtime installed additional software (compromise indicator)# - Missing packages: Image layer was modified (tampering)

Section 4: Evidence Correlation with Attestations

4.1 Timeline Reconstruction

build comprehensive incident timeline.

#!/bin/bash # Extract timestamps from multiple sources echo "=== TIMELINE RECONSTRUCTION ===" > incident-timeline.txt # 1. Image build time (from SBOM)echo "Image Build:" >> incident-timeline.txtcosign download sbom registry.cleanstart.com/cleanstart/python:3.11 | \  jq '.metadata.component.buildTime' >> incident-timeline.txt # 2. Pod creation timeecho "Pod Creation:" >> incident-timeline.txtkubectl get pod auth-service-7f4d5c6 -n production -o jsonpath='{.metadata.creationTimestamp}' >> incident-timeline.txt # 3. Image pull timeecho "Image Pull:" >> incident-timeline.txtkubectl get event -n production --field-selector involvedObject.name=auth-service-7f4d5c6 | grep "Image" >> incident-timeline.txt # 4. Alert timeecho "Falco Alert:" >> incident-timeline.txtgrep "Shell in Distroless" /var/log/falco/alerts.log | head -1 >> incident-timeline.txt # 5. Container stderr/stdoutecho "Container Logs:" >> incident-timeline.txtkubectl logs auth-service-7f4d5c6 -n production --timestamps=true >> incident-timeline.txt

example output.

=== TIMELINE RECONSTRUCTION ===Image Build: 2026-03-20T08:30:00Z (2 days ago)Pod Creation: 2026-03-22T10:00:00Z (4 hours ago)Image Pull: 2026-03-22T10:00:15ZFalco Alert: 2026-03-22T14:32:45Z (CRITICAL: Shell in distroless container)Container Logs: [app startup] -> [API calls] -> [unexpected network activity at 14:32:20]

analysis Compromise happened ~4 hours after pod creation Image was clean (built 2 days ago with SLSA attestation) Likely: Runtime exploitation (not supply chain).

4.2 Triage: Supply Chain vs. Runtime Compromise

decision tree.

1. Is image signature valid?   Yes -> Likely runtime compromise (go to 4.2a)   No  -> SUPPLY CHAIN COMPROMISE (go to 4.2b) 4.2a: Runtime Compromise  - Check Falco logs: Is there evidence of exploitation?  - Check network connections: Unexpected outbound?  - Check filesystem modifications: Malware written to disk?  - Containment: Kill container, patch vulnerability in app 4.2b: Supply Chain Compromise  - Image tampered with after build  - OR CleanStart signing key was compromised  - Containment: Assume all deployments compromised  - Immediate: Revoke image, rebuild, audit all other images  - Escalate: CISO, Chief Legal Officer

Section 5: Chain of Custody

5.1 Evidence Handling Protocol

All evidence must maintain strict chain of custody for legal admissibility:

evidence log template.

Evidence ID: EV-2026-0322-001Description: Memory dump from auth-service podCollected By: john.smith@company.com (IR Team Lead)Collection Time: 2026-03-22T14:35:00ZStorage Location: gs://forensics-evidence-prod/2026/03/22/memory-dump.binStorage Hash (SHA256): abc123def456...Accessed By: [log all access]Final Status: [sealed for legal proceedings / destroyed after 90 days]

access log.

# Enable immutable logging of all evidence accessgcloud logging create-sink forensics-access-log \  logging.googleapis.com/log-sink \  --log-filter='resource.name=~"gs://forensics-evidence-prod/.*"' # Every read/write is logged with actor, time, and intent

5.2 Evidence Preservation

make evidence immutable immediately after collection.

#!/bin/bash # Set immutability on evidence files (GCS)gsutil retention set 3650d gs://forensics-evidence-prod/ # Or use WORM (Write-Once-Read-Many) at container levelgsutil set-object-hold gs://forensics-evidence-prod/evidence-001.tar.gz # Verify immutabilitygsutil object-hold get gs://forensics-evidence-prod/evidence-001.tar.gz# Output: Object Hold: ENABLED (cannot be deleted or overwritten)

Section 6: Integration with SIEM

6.1 Sending Evidence to Central Logging

ingest forensics data into security information and event management (siem).

#!/bin/bash # Parse evidence and send to Splunk/DataDog/ELK # 1. Convert forensics evidence to structured logcat evidence-001-processes.txt | while read line; do  jq -n \    --arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \    --arg source "container-forensics" \    --arg process_line "$line" \    '{timestamp: $timestamp, source: $source, process: $process_line}'done > forensics-structured.jsonl # 2. Send to SIEMsplunk_hec_token="12345-67890-ABCDEF"splunk_endpoint="https://splunk.company.com:8088/services/collector" curl -k $splunk_endpoint \  -H "Authorization: Splunk $splunk_hec_token" \  -d @forensics-structured.jsonl

Section 7: Post-Incident: Lessons Learned

7.1 Root Cause Analysis Report

template for incident rca.

# Incident Root Cause Analysis: Container Compromise ## Summary- **Incident ID**: INC-2026-0322-001- **Severity**: P1 (Critical)- **Duration**: 4 hours 32 minutes (detected to containment)- **Root Cause**: [CVE-2026-XXXXX in application dependency]- **Impact**: [1 container compromised; no data exfiltration detected] ## Timeline- **2026-03-22 14:00**: Pod created from CleanStart/python:3.11- **2026-03-22 14:32**: Shell spawned in container (exploitation)- **2026-03-22 14:33**: Falco alert fired- **2026-03-22 14:35**: Container killed- **2026-03-22 14:45**: Forensics completed; image verified clean- **2026-03-22 15:30**: Application patch deployed ## Forensic Findings- **Image Signature**: VALID (image came from CleanStart)- **SBOM Verification**: PASSED (no unexpected packages)- **Filesystem**: Modified to include [malware name]- **Compromise Vector**: Exploited [CVE] in application code ## Evidence Preserved- Container memory dump: 2.3 GB (gs://forensics-evidence-prod/.../memory.bin)- Filesystem snapshot: 500 MB (gs://forensics-evidence-prod/.../fs.tar.gz)- Network capture: 150 MB (gs://forensics-evidence-prod/.../network.pcap) ## Remediation1. [Patch application dependency to X.Y.Z]2. [Rebuild image with fixed dependency]3. [Redeploy to all environments]4. [Rotate secrets exposed during incident]5. [Enable Falco detection for similar exploits] ## Prevention- Implement static SBOM scanning in CI/CD (catch vulnerable dependencies)- Enable Falco runtime detection on all production pods- Increase frequency of image rebuilds (weekly -> daily)- Add integration test for known CVE-2026-XXXXX

Section 8: Quick Reference Checklist

use this during incident.

[ ] 0-5 min: Collect live evidence (ps, netstat, env, logs) [ ] 5-15 min: Snapshot filesystem before killing container [ ] 15-30 min: Verify image signature and SBOM [ ] 30-60 min: Analyze evidence for compromise indicators [ ] 1-2 hrs: Timeline reconstruction and triage [ ] 2-24 hrs: Detailed forensics analysis [ ] 24-48 hrs: Root cause analysis report [ ] Follow-up: Prevention measures and detection improvements.

resources NIST Computer Forensics Guide SANS Incident Handler Handbook Kubernetes Forensics.

maintained by: Security Engineering, Incident Response Team last updated: 2026-03-22.